Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 # Get the latest from: https://github.com/pre-commit/pre-commit-hooks/releases
hooks:
- id: no-commit-to-branch
- id: check-yaml
args: [--unsafe]
- id: check-json
- id: mixed-line-ending
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-added-large-files
- id: pretty-format-json
args: ['--autofix']
- repo: https://github.com/aws-cloudformation/cfn-lint
rev: v1.44.0
hooks:
- id: cfn-lint
files: infra/cfn/.*\\.(json|yml|yaml)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.41.0
hooks:
- id: markdownlint
name: "Checking Markdown with markdownlint"
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
exclude: package.lock.json
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
150 changes: 150 additions & 0 deletions .secrets.baseline
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
"version": "1.5.0",
"plugins_used": [
{
"name": "ArtifactoryDetector"
},
{
"name": "AWSKeyDetector"
},
{
"name": "AzureStorageKeyDetector"
},
{
"name": "Base64HighEntropyString",
"limit": 4.5
},
{
"name": "BasicAuthDetector"
},
{
"name": "CloudantDetector"
},
{
"name": "DiscordBotTokenDetector"
},
{
"name": "GitHubTokenDetector"
},
{
"name": "GitLabTokenDetector"
},
{
"name": "HexHighEntropyString",
"limit": 3.0
},
{
"name": "IbmCloudIamDetector"
},
{
"name": "IbmCosHmacDetector"
},
{
"name": "IPPublicDetector"
},
{
"name": "JwtTokenDetector"
},
{
"name": "KeywordDetector",
"keyword_exclude": ""
},
{
"name": "MailchimpDetector"
},
{
"name": "NpmDetector"
},
{
"name": "OpenAIDetector"
},
{
"name": "PrivateKeyDetector"
},
{
"name": "PypiTokenDetector"
},
{
"name": "SendGridDetector"
},
{
"name": "SlackDetector"
},
{
"name": "SoftlayerDetector"
},
{
"name": "SquareOAuthDetector"
},
{
"name": "StripeDetector"
},
{
"name": "TelegramBotTokenDetector"
},
{
"name": "TwilioKeyDetector"
}
],
"filters_used": [
{
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
},
{
"path": "detect_secrets.filters.common.is_baseline_file",
"filename": ".secrets.baseline"
},
{
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
"min_level": 2
},
{
"path": "detect_secrets.filters.heuristic.is_indirect_reference"
},
{
"path": "detect_secrets.filters.heuristic.is_likely_id_string"
},
{
"path": "detect_secrets.filters.heuristic.is_lock_file"
},
{
"path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
},
{
"path": "detect_secrets.filters.heuristic.is_potential_uuid"
},
{
"path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
},
{
"path": "detect_secrets.filters.heuristic.is_sequential_string"
},
{
"path": "detect_secrets.filters.heuristic.is_swagger_file"
},
{
"path": "detect_secrets.filters.heuristic.is_templated_secret"
}
],
"results": {
"Makefile": [
{
"type": "Secret Keyword",
"filename": "Makefile",
"hashed_secret": "504a5c3a2ce5fa0acc5f8d9685dc45860c25a8ed",
"is_verified": false,
"line_number": 16,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "Makefile",
"hashed_secret": "99c849bb78a99cb7d6c13e159d4f052f933efa49",
"is_verified": false,
"line_number": 17,
"is_secret": false
}
]
},
"generated_at": "2026-02-22T16:05:34Z"
}
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.PHONY: install run


install:
brew install pre-commit gawk coreutils cfn-lint
brew install checkov semgrep markdownlint-cli shellcheck
pre-commit install

run:
pre-commit run -a

update:
pre-commit autoupdate

audit-secrets:
detect-secrets scan > .secrets.baseline
detect-secrets audit .secrets.baseline

default: run
Empty file added README.md
Empty file.
142 changes: 142 additions & 0 deletions infra/cfn/nas-backup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: nas-backup stack - Corrected for IAM runtime variables and validation.

Parameters:
environment:
Type: String
Default: prod
AllowedValues: [dev, stg, prod]
Description: Deployment environment (must be lowercase).

Resources:
# S3 Bucket
NASBackupBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Sub 'nas-backup-${environment}-${AWS::AccountId}'
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: CleanIncompleteMultipartUploads
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 1
Tags:
- Key: 'cloudspout:environment'
Value: !Ref environment
- Key: 'cloudspout:project'
Value: 'nas-backup'
- Key: 'cloudspout:repository'
Value: 'cloudspout/nas-backup'

# IAM User
NASBackupUser:
Type: 'AWS::IAM::User'
Properties:
UserName: !Sub 'nas-backup-${environment}'
Tags:
- Key: 'cloudspout:environment'
Value: !Ref environment
- Key: 'cloudspout:project'
Value: 'nas-backup'
- Key: 'cloudspout:repository'
Value: 'cloudspout/nas-backup'
- Key: 'cloudspout:home-ip'
Value: '0.0.0.0/0'
- Key: 'cloudspout:fallback-ip'
Value: '0.0.0.0/0'

# IAM Managed Policy - Using ${!} to escape IAM runtime variables
NASBackupManagedPolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: !Sub 'nas-backup-policy-${environment}'
Users: [!Ref NASBackupUser]
PolicyDocument:
Version: '2012-10-17'
Statement:
# BLOCK 1: Global Account Listing
- Effect: Allow
Action: ['s3:ListAllMyBuckets']
Resource: '*'
Condition:
IpAddress:
'aws:SourceIp':
- '${aws:PrincipalTag/cloudspout:home-ip}'
- Effect: Allow
Action: ['s3:ListAllMyBuckets']
Resource: '*'
Condition:
IpAddress:
'aws:SourceIp':
- '${aws:PrincipalTag/cloudspout:fallback-ip}'

# BLOCK 2: Home IP Access
- Effect: Allow
Action:
- 's3:ListBucket'
- 's3:GetBucketLocation'
- 's3:ListBucketMultipartUploads'
- 's3:PutObject'
- 's3:GetObject'
- 's3:DeleteObject'
- 's3:AbortMultipartUpload'
- 's3:ListMultipartUploadParts'
Resource:
- !GetAtt NASBackupBucket.Arn
- !Sub '${NASBackupBucket.Arn}/*'
Condition:
IpAddress:
'aws:SourceIp': '${aws:PrincipalTag/cloudspout:home-ip}'

# BLOCK 3: Fallback IP Access
- Effect: Allow
Action:
- 's3:ListBucket'
- 's3:GetBucketLocation'
- 's3:ListBucketMultipartUploads'
- 's3:PutObject'
- 's3:GetObject'
- 's3:DeleteObject'
- 's3:AbortMultipartUpload'
- 's3:ListMultipartUploadParts'
Resource:
- !GetAtt NASBackupBucket.Arn
- !Sub '${NASBackupBucket.Arn}/*'
Condition:
IpAddress:
'aws:SourceIp': '${aws:PrincipalTag/cloudspout:fallback-ip}'

NASBackupUserAccessKey:
Type: 'AWS::IAM::AccessKey'
Properties:
UserName: !Ref NASBackupUser

NASBackupSecret:
Type: 'AWS::SecretsManager::Secret'
Properties:
Name: !Sub 'nas-backup-credentials-${environment}'
SecretString: !Sub |
{
"AccessKeyId": "${NASBackupUserAccessKey}",
"SecretAccessKey": "${NASBackupUserAccessKey.SecretAccessKey}"
}
Tags:
- Key: 'cloudspout:environment'
Value: !Ref environment
- Key: 'cloudspout:project'
Value: 'nas-backup'

Outputs:
BucketName:
Value: !Ref NASBackupBucket
SecretARN:
Value: !Ref NASBackupSecret
Loading