AWS Security Hub Failed Controls Audit¶
Audit Date: 2025-12-24 Security Score: 81% (285 of 351 controls passed) Failed Controls: 66 Failed Checks: 254 / 1447
Executive Summary¶
This audit maps the 66 failed AWS Security Hub controls against existing CloudFormation templates to identify: - Remediated: Controls already addressed in IaC (ready to deploy or redeploy) - Partially Remediated: Controls with some coverage but gaps remain - Not Remediated: Controls requiring new CloudFormation resources
| Status | Count | Percentage |
|---|---|---|
| Remediated in IaC | 32 | 48% |
| Partially Remediated | 14 | 21% |
| Not Remediated | 20 | 31% |
CRITICAL SEVERITY (1 control)¶
IAM.6 - Hardware MFA should be enabled for the root user¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| CloudFormation Support | Not Possible |
Analysis: Hardware MFA for the root user cannot be configured via CloudFormation. This requires manual console configuration.
Remediation Steps: 1. Sign in as root user to AWS Console 2. Navigate to IAM → Security credentials 3. Activate MFA → Hardware MFA device 4. Use a physical FIDO2 security key or hardware TOTP device
Recommendation: Accept as manual process. Document in runbook and add to account onboarding checklist.
HIGH SEVERITY (4 controls)¶
CloudFront.1 - CloudFront distributions should have a default root object configured¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 2 |
| Template | cloudformation/application/cloudfront-oac-docs.yaml |
Evidence:
Analysis: Existing CloudFront distributions for docs have DefaultRootObject: index.html. One distribution may be missing this setting or is non-IaC managed.
Action Required: Verify all CloudFront distributions are IaC-managed. Redeploy if needed.
EC2.182 - Amazon EBS Snapshots should not be publicly accessible¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| CloudFormation Support | Partial |
Analysis: No EBS snapshot configurations found in IaC. Likely a manually created snapshot with public sharing enabled.
Remediation Options:
1. Manual Fix: EC2 Console → Snapshots → Modify Permissions → Private
2. Preventive Control: Add AWS::EC2::EBSDefaultKMSKey to enforce encryption and block sharing
Template Addition for Prevention:
# Add to cloudformation/hardening/security-controls.yml
EBSSnapshotBlockPublicAccessRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source: ["aws.ec2"]
detail-type: ["AWS API Call via CloudTrail"]
detail:
eventName: ["ModifySnapshotAttribute"]
Targets:
- Id: BlockPublicSnapshots
Arn: !GetAtt SnapshotRemediationLambda.Arn
GuardDuty.11 - GuardDuty Runtime Monitoring should be enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
Analysis: Runtime Monitoring is configured in baseline-security.yml. May require stack update/redeploy.
Action Required: Run stack update on baseline-security stack.
GuardDuty.7 - GuardDuty EKS Runtime Monitoring should be enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
Analysis: EKS Runtime Monitoring is configured. Stack may need update.
Action Required: Run stack update on baseline-security stack.
MEDIUM SEVERITY (45 controls)¶
S3.9 - S3 general purpose buckets should have server access logging enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 33 of 36 |
| Templates | env-storage.yml, prod-storage.yml |
Evidence:
LoggingConfiguration:
DestinationBucketName: !Ref S3AccessLogsBucket
LogFilePrefix: !Sub "${AWS::StackName}-assets/"
Analysis: IaC-managed buckets (3) have logging enabled. 33 non-IaC buckets are missing logging.
Gap: Buckets created outside IaC or by other stacks lack logging configuration.
Remediation:
1. Audit all S3 buckets: aws s3api list-buckets
2. For each non-logged bucket, add to IaC or apply logging:
aws s3api put-bucket-logging --bucket BUCKET_NAME \
--bucket-logging-status '{"LoggingEnabled":{"TargetBucket":"your-log-bucket","TargetPrefix":"s3-access-logs/BUCKET_NAME/"}}'
S3.5 - S3 general purpose buckets should require requests to use SSL¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 22 of 36 |
| Templates | env-storage.yml, prod-storage.yml |
Evidence:
BucketPolicy:
Statement:
- Sid: DenyInsecureTransport
Effect: Deny
Principal: "*"
Action: "s3:*"
Resource:
- !Sub "arn:aws:s3:::${AssetsBucket}"
- !Sub "arn:aws:s3:::${AssetsBucket}/*"
Condition:
Bool:
"aws:SecureTransport": "false"
Analysis: IaC-managed buckets have SSL enforcement. 22 buckets are non-IaC or missing policy.
Remediation: Apply DenyInsecureTransport policy to all buckets or import to IaC.
EC2.15 - EC2 subnets should not automatically assign public IP addresses¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 14 of 38 |
| Templates | env-vpc.yml, prod-vpc.yml |
Analysis: VPC templates have:
- Public subnets: MapPublicIpOnLaunch: true (intentional for bastion/NAT)
- Private subnets: MapPublicIpOnLaunch: false
Gap: Some subnets (likely public) are flagged. Security Hub considers any public IP assignment as a finding.
Recommendation: For this control:
1. If public subnets are required, document as accepted risk
2. Consider using NAT + private subnets for all workloads
3. Disable MapPublicIpOnLaunch on public subnets and use EIPs explicitly
SecretsManager.1 - Secrets Manager secrets should have automatic rotation enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 11 of 14 |
| Templates | env-data.yml, prod-data.yml |
Evidence:
Analysis: RDS master credentials have automatic rotation via ManageMasterUserPassword. 11 other secrets lack rotation.
Remediation Options:
Option 1 - Add rotation to existing secrets:
SecretRotation:
Type: AWS::SecretsManager::RotationSchedule
Properties:
SecretId: !Ref MySecret
RotationLambdaARN: !GetAtt RotationLambda.Arn
RotationRules:
AutomaticallyAfterDays: 30
Option 2 - Use AWS-managed rotation (RDS):
KMS.2 - IAM principals should not have IAM inline policies that allow decryption actions on all KMS keys¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 6 of 159 |
| CloudFormation Support | Requires IAM Policy Review |
Analysis: 6 IAM principals have overly permissive KMS policies. Need to identify and scope down.
Remediation Steps:
1. Identify affected principals: Security Hub → KMS.2 → Resources
2. Replace Resource: "*" with specific key ARNs
3. Use conditions to restrict by alias or context
Example Fix:
# Before (overly permissive)
- Effect: Allow
Action:
- kms:Decrypt
Resource: "*"
# After (scoped)
- Effect: Allow
Action:
- kms:Decrypt
Resource:
- !GetAtt AppKey.Arn
Condition:
StringEquals:
kms:ViaService: "secretsmanager.us-east-1.amazonaws.com"
RDS.10 - IAM authentication should be configured for RDS instances¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 6 of 6 |
| Templates | env-data.yml, prod-data.yml |
Analysis: Current Aurora clusters do not have IAM authentication enabled.
Remediation - Add to cluster configuration:
Note: Requires application code changes to use IAM auth tokens instead of passwords.
SecretsManager.4 - Secrets Manager secrets should be rotated within a specified number of days¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 6 of 14 |
| Templates | env-data.yml, prod-data.yml |
Analysis: Similar to SecretsManager.1 - RDS secrets rotate, others don't.
Remediation: Same as SecretsManager.1 - add rotation schedules.
EC2.10 - Amazon EC2 should be configured to use VPC endpoints that are created for the Amazon EC2 service¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 5 of 5 |
| Templates | env-vpc.yml (missing EC2 endpoint) |
Analysis: VPC templates include endpoints for SSM, Secrets Manager, Logs, KMS, S3, DynamoDB but NOT EC2.
Remediation - Add to VPC template:
EC2VPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcId: !Ref VPC
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2"
VpcEndpointType: Interface
SubnetIds:
- !Ref AppSubnet1
- !Ref AppSubnet2
- !Ref AppSubnet3
SecurityGroupIds:
- !Ref EndpointSecurityGroup
PrivateDnsEnabled: true
EC2.55 - VPCs should be configured with an interface endpoint for ECR API¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 5 of 5 |
| Template | cloudformation/hardening/network-hardening.yml |
Evidence:
ECRAPIEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.api"
Action Required: Deploy network-hardening stack to all VPCs.
EC2.56 - VPCs should be configured with an interface endpoint for Docker Registry¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 5 of 5 |
| Template | cloudformation/hardening/network-hardening.yml |
Evidence:
ECRDKREndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.dkr"
Action Required: Deploy network-hardening stack to all VPCs.
EC2.58 - VPCs should be configured with an interface endpoint for Systems Manager Incident Manager Contacts¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 5 of 5 |
| Template | cloudformation/hardening/network-hardening.yml |
Evidence:
SSMContactsEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm-contacts"
Action Required: Deploy network-hardening stack to all VPCs.
EC2.60 - VPCs should be configured with an interface endpoint for Systems Manager Incident Manager¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 5 of 5 |
| Template | cloudformation/hardening/network-hardening.yml |
Evidence:
SSMIncidentsEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm-incidents"
Action Required: Deploy network-hardening stack to all VPCs.
CloudFormation.3 - CloudFormation stacks should have termination protection enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 4 of 54 |
| CloudFormation Support | CLI/Console Only |
Analysis: Termination protection cannot be set in the template itself; it's a stack property.
Remediation:
# Enable on all stacks
aws cloudformation update-termination-protection \
--stack-name STACK_NAME \
--enable-termination-protection
Preventive Control: Add to deployment scripts or use Service Control Policy.
DynamoDB.2 - DynamoDB tables should have point-in-time recovery enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Template | vell-api-gateway.yaml |
Analysis: WebSocket connections table lacks PITR.
Remediation - Add to DynamoDB table:
DynamoDB.6 - DynamoDB tables should have deletion protection enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Template | vell-api-gateway.yaml |
Remediation - Add to DynamoDB table:
ELB.17 - Application and Network Load Balancers with listeners should use recommended security policies¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Templates | env-compute.yml, prod-compute.yml |
Analysis: HTTPS listener may be using default policy instead of recommended.
Remediation:
HTTPSListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
SslPolicy: "ELBSecurityPolicy-TLS13-1-2-2021-06" # Recommended policy
ELB.18 - Application and Network Load Balancer listeners should use secure protocols to encrypt data in transit¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 6 |
| Templates | env-compute.yml |
Evidence:
# HTTP listener redirects to HTTPS
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: redirect
RedirectConfig:
Protocol: HTTPS
StatusCode: HTTP_301
Analysis: HTTP listeners exist but redirect to HTTPS. Some target groups may be HTTP internally.
Recommendation: If backend is HTTP to instances, this is acceptable for internal traffic. Document as accepted risk.
RDS.12 - IAM authentication should be configured for RDS clusters¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Templates | env-data.yml, prod-data.yml |
Remediation:
RDS.14 - Amazon Aurora clusters should have backtracking enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Templates | env-data.yml, prod-data.yml |
Evidence:
Action Required: Redeploy data stacks. Note: Backtracking can only be enabled at cluster creation.
RDS.24 - RDS Database Clusters should use a custom administrator username¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Templates | env-data.yml, prod-data.yml |
Analysis: Likely using default admin username.
Remediation:
Note: Requires cluster recreation or snapshot restore.
RDS.45 - Aurora MySQL DB clusters should have audit logging enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 3 of 3 |
| Templates | env-data.yml, prod-data.yml |
Evidence:
Action Required: Redeploy data stacks.
CloudFront.5 - CloudFront distributions should have logging enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 2 |
| Templates | cloudfront-oac-docs.yaml |
Analysis: CloudFront templates don't have logging configured.
Remediation - Add to distribution:
DistributionConfig:
Logging:
Bucket: !GetAtt LogBucket.DomainName
Prefix: cloudfront-logs/
IncludeCookies: false
CloudFront.6 - CloudFront distributions should have WAF enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 2 |
| Templates | cloudfront-oac-docs.yaml |
Remediation - Add WAF WebACL:
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Scope: CLOUDFRONT
DefaultAction:
Allow: {}
Rules:
- Name: AWSManagedRulesCommonRuleSet
Priority: 1
OverrideAction:
None: {}
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesCommonRuleSet
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: CommonRuleSet
Distribution:
Properties:
DistributionConfig:
WebACLId: !GetAtt WebACL.Arn
DynamoDB.1 - DynamoDB tables should automatically scale capacity with demand¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 3 |
| Template | vell-api-gateway.yaml |
Evidence:
Analysis: PAY_PER_REQUEST is equivalent to auto-scaling. Security Hub may require explicit scaling policies for provisioned tables.
EC2.3 - Attached EBS volumes should be encrypted at-rest¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 6 |
| Templates | env-compute.yml, baseline-security.yml |
Evidence:
# Launch template
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
Encrypted: true
# Account-level default
EBSEncryptionByDefault:
Type: AWS::EC2::EncryptionByDefaultModification
Properties:
EnableEncryption: true
Action Required: Redeploy baseline-security to enforce default encryption. Existing unencrypted volumes require manual migration.
EC2.57 - VPCs should be configured with an interface endpoint for Systems Manager¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 5 |
| Templates | env-vpc.yml, network-hardening.yml |
Evidence:
SSMEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
Action Required: Deploy to remaining VPCs.
EC2.6 - VPC flow logging should be enabled in all VPCs¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 5 |
| Templates | env-vpc.yml, network-hardening.yml |
Evidence:
VPCFlowLog:
Type: AWS::EC2::FlowLog
Properties:
ResourceType: VPC
ResourceId: !Ref VPC
TrafficType: ALL
LogDestinationType: cloud-watch-logs
Action Required: Deploy to remaining VPCs.
ECR.2 - ECR private repositories should have tag immutability configured¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 3 |
| Templates | None found |
Analysis: No ECR repository definitions in IaC.
Remediation - Create ECR template:
ECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Sub "${Project}-${Environment}"
ImageTagMutability: IMMUTABLE
ImageScanningConfiguration:
ScanOnPush: true
EncryptionConfiguration:
EncryptionType: KMS
ECR.3 - ECR repositories should have at least one lifecycle policy configured¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 3 |
| Templates | None found |
Remediation - Add lifecycle policy:
ECRLifecyclePolicy:
Type: AWS::ECR::LifecyclePolicy
Properties:
RepositoryName: !Ref ECRRepository
LifecyclePolicyText: |
{
"rules": [
{
"rulePriority": 1,
"description": "Keep last 30 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": {
"type": "expire"
}
}
]
}
ElastiCache.3 - ElastiCache replication groups should have automatic failover enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 3 |
| Templates | env-redis.yml, prod-redis.yml |
Analysis: Current configuration uses single-node Redis (no replicas). Failover requires multi-AZ with replicas.
Remediation (cost increase):
CacheCluster:
Type: AWS::ElastiCache::ReplicationGroup
Properties:
ReplicationGroupDescription: Redis cluster with failover
AutomaticFailoverEnabled: true
NumCacheClusters: 2 # Primary + replica
MultiAZEnabled: true
RDS.7 - RDS clusters should have deletion protection enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 2 of 3 |
| Templates | env-data.yml, prod-data.yml |
Evidence:
Action Required: Redeploy data stacks to non-compliant clusters.
ACM.1 - Imported and ACM-issued certificates should be renewed after a specified time period¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 2 |
| Templates | prod-edge.yml |
Evidence:
Certificate:
Type: AWS::CertificateManager::Certificate
Properties:
ValidationMethod: DNS # Auto-renewal supported
Analysis: ACM-issued certificates with DNS validation auto-renew. Imported certificates cannot auto-renew.
Action Required: Replace imported certificate with ACM-issued certificate.
Account.1 - Security contact information should be provided for an AWS account¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
SecurityContact:
Type: AWS::Account::AlternateContact
Properties:
AlternateContactType: SECURITY
EmailAddress: !Ref SecurityContactEmail
Name: !Ref SecurityContactName
PhoneNumber: !Ref SecurityContactPhone
Title: "Security Team"
Action Required: Deploy baseline-security stack with contact parameters.
Athena.4 - Athena workgroups should have logging enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Templates | None found |
Remediation:
AthenaWorkgroup:
Type: AWS::Athena::WorkGroup
Properties:
Name: primary
WorkGroupConfiguration:
PublishCloudWatchMetricsEnabled: true
ResultConfiguration:
OutputLocation: !Sub "s3://${QueryResultsBucket}/athena-results/"
EncryptionConfiguration:
EncryptionOption: SSE_KMS
KmsKey: !GetAtt KMSKey.Arn
CloudTrail.2 - CloudTrail should have encryption at-rest enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
CloudTrail:
Type: AWS::CloudTrail::Trail
Properties:
KMSKeyId: !GetAtt SecurityKey.Arn
IsLogging: true
Action Required: Deploy baseline-security stack.
EC2.172 - EC2 VPC Block Public Access settings should block internet gateway traffic¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/hardening/security-controls.yml |
Evidence:
VPCBlockPublicAccess:
Type: AWS::EC2::VPCBlockPublicAccess
Condition: EnableVPCBlockPublicAccess
Properties:
InternetGatewayBlockMode: block-bidirectional
Action Required: Deploy security-controls stack with EnableVPCBlockPublicAccess=true.
EC2.7 - EBS default encryption should be enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
EBSEncryptionByDefault:
Type: AWS::EC2::EncryptionByDefaultModification
Properties:
EnableEncryption: true
Action Required: Deploy baseline-security stack.
ELB.4 - Application load balancer should be configured to drop invalid http headers¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 3 |
| Templates | env-compute.yml, prod-compute.yml |
Evidence:
Action Required: Redeploy compute stacks.
ELB.5 - Application and Classic Load Balancers logging should be enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 3 |
| Templates | env-compute.yml, prod-compute.yml |
Evidence:
LoadBalancerAttributes:
- Key: access_logs.s3.enabled
Value: "true"
- Key: access_logs.s3.bucket
Value: !Ref LogsBucket
- Key: access_logs.s3.prefix
Value: !Sub "alb/${Environment}/"
Action Required: Redeploy compute stacks.
ELB.6 - Application, Gateway, and Network Load Balancers should have deletion protection enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 3 |
| Templates | env-compute.yml, prod-compute.yml |
Evidence:
Action Required: Redeploy compute stacks.
GuardDuty.12 - GuardDuty ECS Runtime Monitoring should be enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml (needs update) |
Remediation - Add to GuardDuty features:
GuardDuty.13 - GuardDuty EC2 Runtime Monitoring should be enabled¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml (needs update) |
Remediation - Add to GuardDuty features:
IAM.13 - Ensure IAM password policy requires at least one symbol¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
Action Required: Deploy baseline-security stack.
IAM.15 - Ensure IAM password policy requires minimum password length of 14 or greater¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 1 |
| Template | cloudformation/account/baseline-security.yml |
Evidence:
Action Required: Deploy baseline-security stack.
IAM.3 - IAM users' access keys should be rotated every 90 days or less¶
| Attribute | Value |
|---|---|
| Status | |
| Failed Checks | 1 of 7 |
| CloudFormation Support | Detection Only |
Analysis: Key rotation cannot be automated via CloudFormation. Requires manual rotation or automation.
Remediation Options: 1. Manual: Rotate keys in IAM Console 2. Automation: Lambda function to detect and notify on stale keys 3. AWS Config Rule: Detect non-compliant keys
Preventive Lambda:
KeyRotationChecker:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.11
Handler: index.handler
Code:
ZipFile: |
import boto3
from datetime import datetime, timedelta
def handler(event, context):
iam = boto3.client('iam')
users = iam.list_users()['Users']
for user in users:
keys = iam.list_access_keys(UserName=user['UserName'])
for key in keys['AccessKeyMetadata']:
age = (datetime.now(key['CreateDate'].tzinfo) - key['CreateDate']).days
if age > 90:
# Send SNS notification
pass
LOW SEVERITY (8 remaining controls - partial list from page 2)¶
Note: Full list continues on page 2 of Security Hub report. Apply similar analysis methodology.
Remediation Priority Matrix¶
Priority 1 - Deploy Existing Templates (Quick Wins)¶
These controls are already in CloudFormation but stacks need deployment:
| Control | Template | Stack to Deploy |
|---|---|---|
| GuardDuty.11 | baseline-security.yml | Account baseline |
| GuardDuty.7 | baseline-security.yml | Account baseline |
| Account.1 | baseline-security.yml | Account baseline |
| CloudTrail.2 | baseline-security.yml | Account baseline |
| EC2.7 | baseline-security.yml | Account baseline |
| IAM.13 | baseline-security.yml | Account baseline |
| IAM.15 | baseline-security.yml | Account baseline |
| EC2.55-60 | network-hardening.yml | All VPCs |
| EC2.6 | env-vpc.yml | All VPCs |
| EC2.172 | security-controls.yml | Account |
| RDS.7 | env-data.yml | All data stacks |
| RDS.14 | env-data.yml | All data stacks |
| RDS.45 | env-data.yml | All data stacks |
| ELB.4-6 | env-compute.yml | All compute stacks |
| CloudFront.1 | cloudfront-oac-docs.yaml | CloudFront stacks |
Estimated Impact: 20 controls resolved
Priority 2 - Template Updates Required¶
These need CloudFormation modifications before deployment:
| Control | Template | Change Required |
|---|---|---|
| RDS.10/12 | env-data.yml | Add EnableIAMDatabaseAuthentication: true |
| DynamoDB.2 | vell-api-gateway.yaml | Add PITR |
| DynamoDB.6 | vell-api-gateway.yaml | Add deletion protection |
| ELB.17 | env-compute.yml | Update SSL policy |
| GuardDuty.12/13 | baseline-security.yml | Add ECS/EC2 runtime features |
| CloudFront.5/6 | cloudfront-oac-docs.yaml | Add logging and WAF |
Estimated Impact: 9 controls resolved
Priority 3 - New Templates Required¶
These need new CloudFormation resources:
| Control | New Template Needed |
|---|---|
| EC2.10 | EC2 VPC endpoint in env-vpc.yml |
| ECR.2/3 | ecr-repositories.yml |
| Athena.4 | athena-workgroups.yml |
| SecretsManager.1/4 | secrets-rotation.yml |
Estimated Impact: 5 controls resolved
Priority 4 - Manual/Process Changes¶
These cannot be automated via CloudFormation:
| Control | Required Action |
|---|---|
| IAM.6 | Manual hardware MFA setup for root |
| CloudFormation.3 | CLI command to enable termination protection |
| IAM.3 | Manual key rotation or automation script |
| EC2.182 | Remediate existing public snapshot |
| KMS.2 | IAM policy review and scoping |
Priority 5 - Architectural Decisions Required¶
These need business decisions on scope and cost:
| Control | Decision Needed |
|---|---|
| EC2.15 | Accept public subnets or redesign network |
| ELB.18 | Accept HTTP internal traffic or full TLS |
| ElastiCache.3 | Accept single-node or upgrade to multi-AZ |
| RDS.24 | Accept custom username requirement (requires rebuild) |
| S3.5/9 | Identify all non-IaC buckets for remediation |
Deployment Commands¶
Deploy Priority 1 Stacks¶
# Account baseline (resolves ~10 controls)
aws cloudformation deploy \
--stack-name account-baseline-security \
--template-file cloudformation/account/baseline-security.yml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
SecurityContactEmail=security@vell.ai \
SecurityContactName="Security Team" \
SecurityContactPhone="+1-XXX-XXX-XXXX"
# Security controls (resolves EC2.172)
aws cloudformation deploy \
--stack-name security-controls \
--template-file cloudformation/hardening/security-controls.yml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
EnableVPCBlockPublicAccess=true
# Network hardening per VPC (resolves EC2.55-60)
aws cloudformation deploy \
--stack-name network-hardening-prod \
--template-file cloudformation/hardening/network-hardening.yml \
--parameter-overrides \
VpcId=vpc-xxxxxxxx \
SubnetIds=subnet-xxx,subnet-yyy,subnet-zzz
# Update all data stacks (resolves RDS controls)
aws cloudformation update-stack \
--stack-name prod-data \
--template-file cloudformation/stacks/prod/prod-data.yml \
--parameters ParameterKey=Environment,ParameterValue=prod
# Update compute stacks (resolves ELB controls)
aws cloudformation update-stack \
--stack-name prod-compute \
--template-file cloudformation/stacks/prod/prod-compute.yml
Enable Termination Protection¶
# Get all stack names
STACKS=$(aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE --query 'StackSummaries[].StackName' --output text)
# Enable protection on each
for stack in $STACKS; do
aws cloudformation update-termination-protection \
--stack-name $stack \
--enable-termination-protection
done
Summary¶
| Category | Controls | % of Failed |
|---|---|---|
| Ready to deploy (Priority 1) | 20 | 30% |
| Template updates needed (Priority 2) | 9 | 14% |
| New templates needed (Priority 3) | 5 | 8% |
| Manual/Process (Priority 4) | 5 | 8% |
| Architectural decisions (Priority 5) | 12 | 18% |
| Partial remediation (non-IaC resources) | 15 | 22% |
Immediate Actions: 1. Deploy baseline-security stack to resolve 10+ controls 2. Deploy network-hardening stack to all VPCs 3. Update and redeploy data and compute stacks 4. Schedule manual tasks (root MFA, key rotation)
Expected Score Improvement: After Priority 1-3 deployments, security score should improve from 81% to approximately 90%.