-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Note: As I was reviewing this project, and before I created any pull requests, I wanted to create a tracking issue on this topic. Much of the content (including "5. Additional Security Recommendations", "7. Implementation Priority", and "11. Compliance and Audit") was assisted by AI analysis using Kiro IDE. Some of these may not apply to this project source, but maybe included in usage documenation.
Overview
This issue provides recommendations for implementing least privilege IAM policies in the PDF Accessibility project. The current implementation contains over-permissive IAM policies with wildcard permissions (*) and unscoped resources that violate AWS security best practices.
Critical Security Findings
Based on security scanning and code review, the following critical issues were identified:
1. Wildcard Permissions (🔴 HIGH SEVERITY)
- Multiple instances of
bedrock:*,s3:*,iam:*,lambda:*,ecs:*,ec2:*,states:*,cloudformation:*,logs:*,secretsmanager:* - Found in:
app.py,deploy.sh,IAM_PERMISSIONS.md,pdf2html-stack.js
2. Unscoped Resources (🔴 HIGH SEVERITY)
- All IAM policies use
Resource: "*"instead of specific ARNs - Creates privilege escalation risks
3. Full IAM Permissions (🔴 CRITICAL SEVERITY)
iam:*grants in deploy.sh (lines 247-249) allow privilege escalation- Enables assuming any role in the account
Detailed Recommendations by File
1. app.py - CDK Stack IAM Policies
Current Issues
Lines 84-87, 275-278: Bedrock wildcard permissions
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["bedrock:*"],
resources=["*"],
))Lines 88-91: S3 wildcard permissions
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["s3:*"],
resources=["*"],
))Recommended Changes
Bedrock Permissions
Replace wildcard with specific actions based on actual usage:
# For ECS tasks and Lambda functions using Bedrock
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=[
"bedrock:InvokeModel", # For Claude/Nova model invocations
"bedrock:InvokeModelWithResponseStream" # If using streaming
],
resources=[
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0",
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.anthropic.claude-3-haiku-20240307-v1:0",
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.amazon.nova-pro-v1:0"
]
))S3 Permissions
Replace wildcard with specific actions and scoped resources:
# For ECS tasks that need to read/write PDFs
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject" # Only if cleanup is needed
],
resources=[
f"{bucket.bucket_arn}/*" # Scope to specific bucket
]
))
# For listing bucket contents (if needed)
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=[
"s3:ListBucket"
],
resources=[
bucket.bucket_arn
]
))Lambda Function Permissions (Lines 275-278)
# For add_title Lambda using Bedrock
add_title_lambda.add_to_role_policy(iam.PolicyStatement(
actions=[
"bedrock:InvokeModel"
],
resources=[
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.amazon.nova-pro-v1:0"
]
))Secrets Manager Permissions (Lines 92-95, 289-292, 307-310)
# Scope to specific secret ARN
a11y_precheck.add_to_role_policy(iam.PolicyStatement(
actions=["secretsmanager:GetSecretValue"],
resources=[
f"arn:aws:secretsmanager:{region}:{account_id}:secret:/myapp/client_credentials-*"
]
))2. deploy.sh - CodeBuild IAM Policies
Current Issues
Lines 206-305 (PDF-to-PDF policy): Multiple wildcard permissions
POLICY_DOCUMENT='{
"Statement": [
{"Action": ["s3:*"], "Resource": "*"},
{"Action": ["ecr:*"], "Resource": "*"},
{"Action": ["lambda:*"], "Resource": "*"},
{"Action": ["ecs:*"], "Resource": "*"},
{"Action": ["ec2:*"], "Resource": "*"},
{"Action": ["states:*"], "Resource": "*"},
{"Action": ["iam:*"], "Resource": "*"}, # CRITICAL RISK
{"Action": ["cloudformation:*"], "Resource": "*"},
{"Action": ["bedrock:*"], "Resource": "*"},
{"Action": ["logs:*"], "Resource": "*"},
{"Action": ["cloudwatch:*"], "Resource": "*"},
{"Action": ["secretsmanager:*"], "Resource": "*"}
]
}'Lines 309-378 (PDF-to-HTML policy): Similar wildcard issues
Recommended Changes
Replace the entire policy document with least privilege permissions:
# PDF-to-PDF CodeBuild Policy
POLICY_DOCUMENT='{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3BucketAccess",
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:PutBucketPolicy",
"s3:PutBucketPublicAccessBlock",
"s3:PutEncryptionConfiguration",
"s3:PutBucketVersioning",
"s3:PutBucketCORS",
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::pdfaccessibility-*"
},
{
"Sid": "S3ObjectAccess",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::pdfaccessibility-*/*"
},
{
"Sid": "ECRRepositoryManagement",
"Effect": "Allow",
"Action": [
"ecr:CreateRepository",
"ecr:DescribeRepositories",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:SetRepositoryPolicy"
],
"Resource": [
"arn:aws:ecr:${REGION}:${ACCOUNT_ID}:repository/pdfaccessibility-*",
"arn:aws:ecr:${REGION}:${ACCOUNT_ID}:repository/cdk-*"
]
},
{
"Sid": "ECRAuthToken",
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Sid": "LambdaManagement",
"Effect": "Allow",
"Action": [
"lambda:CreateFunction",
"lambda:UpdateFunctionCode",
"lambda:UpdateFunctionConfiguration",
"lambda:GetFunction",
"lambda:DeleteFunction",
"lambda:AddPermission",
"lambda:RemovePermission",
"lambda:PublishVersion",
"lambda:CreateAlias",
"lambda:UpdateAlias"
],
"Resource": "arn:aws:lambda:${REGION}:${ACCOUNT_ID}:function:PDFAccessibility-*"
},
{
"Sid": "ECSClusterAndTaskManagement",
"Effect": "Allow",
"Action": [
"ecs:CreateCluster",
"ecs:DescribeClusters",
"ecs:DeleteCluster",
"ecs:RegisterTaskDefinition",
"ecs:DeregisterTaskDefinition",
"ecs:DescribeTaskDefinition"
],
"Resource": [
"arn:aws:ecs:${REGION}:${ACCOUNT_ID}:cluster/PDFAccessibility-*",
"arn:aws:ecs:${REGION}:${ACCOUNT_ID}:task-definition/PDFAccessibility-*:*"
]
},
{
"Sid": "EC2NetworkingForECS",
"Effect": "Allow",
"Action": [
"ec2:CreateVpc",
"ec2:DescribeVpcs",
"ec2:DeleteVpc",
"ec2:CreateSubnet",
"ec2:DescribeSubnets",
"ec2:DeleteSubnet",
"ec2:CreateInternetGateway",
"ec2:AttachInternetGateway",
"ec2:DetachInternetGateway",
"ec2:DeleteInternetGateway",
"ec2:CreateNatGateway",
"ec2:DescribeNatGateways",
"ec2:DeleteNatGateway",
"ec2:AllocateAddress",
"ec2:ReleaseAddress",
"ec2:DescribeAddresses",
"ec2:CreateRouteTable",
"ec2:DescribeRouteTables",
"ec2:DeleteRouteTable",
"ec2:CreateRoute",
"ec2:DeleteRoute",
"ec2:AssociateRouteTable",
"ec2:DisassociateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:DescribeSecurityGroups",
"ec2:DeleteSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:RevokeSecurityGroupEgress",
"ec2:DescribeAvailabilityZones"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "${REGION}"
}
}
},
{
"Sid": "StepFunctionsManagement",
"Effect": "Allow",
"Action": [
"states:CreateStateMachine",
"states:DescribeStateMachine",
"states:DeleteStateMachine",
"states:UpdateStateMachine",
"states:TagResource"
],
"Resource": "arn:aws:states:${REGION}:${ACCOUNT_ID}:stateMachine:PDFAccessibility-*"
},
{
"Sid": "IAMRoleManagement",
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:GetRole",
"iam:DeleteRole",
"iam:PutRolePolicy",
"iam:DeleteRolePolicy",
"iam:GetRolePolicy",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:PassRole",
"iam:TagRole"
],
"Resource": [
"arn:aws:iam::${ACCOUNT_ID}:role/PDFAccessibility-*",
"arn:aws:iam::${ACCOUNT_ID}:role/cdk-*"
]
},
{
"Sid": "IAMPolicyManagement",
"Effect": "Allow",
"Action": [
"iam:CreatePolicy",
"iam:GetPolicy",
"iam:DeletePolicy",
"iam:CreatePolicyVersion",
"iam:DeletePolicyVersion",
"iam:ListPolicyVersions"
],
"Resource": "arn:aws:iam::${ACCOUNT_ID}:policy/PDFAccessibility-*"
},
{
"Sid": "CloudFormationManagement",
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStackResources",
"cloudformation:GetTemplate",
"cloudformation:ValidateTemplate"
],
"Resource": "arn:aws:cloudformation:${REGION}:${ACCOUNT_ID}:stack/PDFAccessibility*/*"
},
{
"Sid": "BedrockModelAccess",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:${REGION}::foundation-model/anthropic.claude-*",
"arn:aws:bedrock:${REGION}::foundation-model/amazon.nova-*",
"arn:aws:bedrock:${REGION}:${ACCOUNT_ID}:inference-profile/*"
]
},
{
"Sid": "CloudWatchLogsManagement",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:DeleteLogGroup",
"logs:PutRetentionPolicy"
],
"Resource": [
"arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:/aws/lambda/PDFAccessibility-*",
"arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:/aws/ecs/PDFAccessibility-*",
"arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:/aws/states/PDFAccessibility-*"
]
},
{
"Sid": "CloudWatchMetrics",
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"cloudwatch:GetMetricData",
"cloudwatch:PutDashboard",
"cloudwatch:DeleteDashboards"
],
"Resource": "*"
},
{
"Sid": "SecretsManagerAccess",
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:UpdateSecret",
"secretsmanager:DeleteSecret",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:${REGION}:${ACCOUNT_ID}:secret:/myapp/*"
},
{
"Sid": "STSAccess",
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity"
],
"Resource": "*"
},
{
"Sid": "SSMParameterAccess",
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:PutParameter"
],
"Resource": "arn:aws:ssm:${REGION}:${ACCOUNT_ID}:parameter/cdk-bootstrap/*"
}
]
}'PDF-to-HTML CodeBuild Policy
For the PDF-to-HTML solution (lines 309-378), use this scoped policy:
# PDF-to-HTML CodeBuild Policy
POLICY_DOCUMENT='{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3BucketAccess",
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:PutBucketPolicy",
"s3:PutBucketPublicAccessBlock",
"s3:PutEncryptionConfiguration",
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:PutObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::pdf2html-bucket-*",
"arn:aws:s3:::pdf2html-bucket-*/*"
]
},
{
"Sid": "ECRManagement",
"Effect": "Allow",
"Action": [
"ecr:CreateRepository",
"ecr:DescribeRepositories",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": [
"arn:aws:ecr:${REGION}:${ACCOUNT_ID}:repository/pdf2html-*",
"*"
]
},
{
"Sid": "LambdaManagement",
"Effect": "Allow",
"Action": [
"lambda:CreateFunction",
"lambda:UpdateFunctionCode",
"lambda:UpdateFunctionConfiguration",
"lambda:GetFunction",
"lambda:DeleteFunction",
"lambda:PublishVersion"
],
"Resource": "arn:aws:lambda:${REGION}:${ACCOUNT_ID}:function:pdf2html-*"
},
{
"Sid": "IAMRoleManagement",
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:GetRole",
"iam:DeleteRole",
"iam:PutRolePolicy",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:PassRole"
],
"Resource": "arn:aws:iam::${ACCOUNT_ID}:role/pdf2html-*"
},
{
"Sid": "CloudFormationManagement",
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks"
],
"Resource": "arn:aws:cloudformation:${REGION}:${ACCOUNT_ID}:stack/pdf2html*/*"
},
{
"Sid": "BedrockDataAutomation",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock-data-automation:CreateDataAutomationProject",
"bedrock-data-automation:GetDataAutomationProject",
"bedrock-data-automation:InvokeDataAutomationAsync"
],
"Resource": [
"arn:aws:bedrock:${REGION}::foundation-model/amazon.nova-*",
"arn:aws:bedrock:${REGION}:${ACCOUNT_ID}:data-automation-project/*"
]
},
{
"Sid": "CloudWatchLogs",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:/aws/lambda/pdf2html-*"
},
{
"Sid": "STSAccess",
"Effect": "Allow",
"Action": "sts:GetCallerIdentity",
"Resource": "*"
}
]
}'3. Lambda Function Runtime Permissions
Split PDF Lambda (lambda/split_pdf/main.py)
Required Actions:
s3:GetObject- Download PDF from S3s3:PutObject- Upload PDF chunkss3:ListBucket- List bucket contents (optional)states:StartExecution- Trigger Step Functionslogs:CreateLogStream,logs:PutLogEvents- CloudWatch logging
Recommended Policy:
split_pdf_lambda.add_to_role_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject"
],
resources=[
f"{bucket.bucket_arn}/pdf/*", # Source PDFs
f"{bucket.bucket_arn}/temp/*" # Chunked PDFs
]
))
split_pdf_lambda.add_to_role_policy(iam.PolicyStatement(
actions=["states:StartExecution"],
resources=[state_machine.state_machine_arn]
))Docker Autotag Container (docker_autotag/autotag.py)
Required Actions:
s3:GetObject- Download files from S3s3:PutObject- Upload processed filessecretsmanager:GetSecretValue- Retrieve Adobe credentialscomprehend:DetectDominantLanguage- Language detectionbedrock:InvokeModel- Not used in this container
Recommended Policy:
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject"
],
resources=[
f"{bucket.bucket_arn}/temp/*"
]
))
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["secretsmanager:GetSecretValue"],
resources=[
f"arn:aws:secretsmanager:{region}:{account_id}:secret:/myapp/client_credentials-*"
]
))
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["comprehend:DetectDominantLanguage"],
resources=["*"] # Comprehend doesn't support resource-level permissions
))Add Title Lambda (lambda/add_title/myapp.py)
Required Actions:
s3:GetObject- Download merged PDFs3:PutObject- Upload PDF with titlebedrock:InvokeModel- Generate title using Novasts:GetCallerIdentity- Get account info for model ARN
Recommended Policy:
add_title_lambda.add_to_role_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject"
],
resources=[
f"{bucket.bucket_arn}/temp/*",
f"{bucket.bucket_arn}/result/*"
]
))
add_title_lambda.add_to_role_policy(iam.PolicyStatement(
actions=["bedrock:InvokeModel"],
resources=[
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.amazon.nova-pro-v1:0"
]
))
add_title_lambda.add_to_role_policy(iam.PolicyStatement(
actions=["sts:GetCallerIdentity"],
resources=["*"]
))Java Lambda Merger (lambda/java_lambda/PDFMergerLambda)
Required Actions:
s3:GetObject- Download PDF chunkss3:PutObject- Upload merged PDFs3:DeleteObject- Clean up chunks (optional)
Recommended Policy:
java_lambda.add_to_role_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject" # Only if cleanup is implemented
],
resources=[
f"{bucket.bucket_arn}/temp/*"
]
))Accessibility Checker Lambdas
Required Actions:
s3:GetObject- Download PDF for checkings3:PutObject- Upload accessibility reportsecretsmanager:GetSecretValue- Get credentials (if needed)
Recommended Policy:
a11y_lambda.add_to_role_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject"
],
resources=[
f"{bucket.bucket_arn}/pdf/*",
f"{bucket.bucket_arn}/result/*",
f"{bucket.bucket_arn}/reports/*"
]
))4. Step Functions Execution Role
Current Issues
Step Functions needs permissions to invoke ECS tasks and Lambda functions, but should be scoped appropriately.
Recommended Policy
# Step Functions execution role
stepfunctions_role = iam.Role(self, "StepFunctionsRole",
assumed_by=iam.ServicePrincipal("states.amazonaws.com")
)
# ECS task invocation
stepfunctions_role.add_to_policy(iam.PolicyStatement(
actions=[
"ecs:RunTask",
"ecs:StopTask",
"ecs:DescribeTasks"
],
resources=[
task_definition_1.task_definition_arn,
task_definition_2.task_definition_arn
]
))
# Lambda invocation
stepfunctions_role.add_to_policy(iam.PolicyStatement(
actions=["lambda:InvokeFunction"],
resources=[
java_lambda.function_arn,
add_title_lambda.function_arn,
a11y_precheck.function_arn,
a11y_postcheck.function_arn
]
))
# Pass role to ECS
stepfunctions_role.add_to_policy(iam.PolicyStatement(
actions=["iam:PassRole"],
resources=[
ecs_task_execution_role.role_arn,
ecs_task_role.role_arn
],
conditions={
"StringLike": {
"iam:PassedToService": "ecs-tasks.amazonaws.com"
}
}
))
# CloudWatch Events (for ECS task state changes)
stepfunctions_role.add_to_policy(iam.PolicyStatement(
actions=[
"events:PutTargets",
"events:PutRule",
"events:DescribeRule"
],
resources=[
f"arn:aws:events:{region}:{account_id}:rule/StepFunctionsGetEventsForECSTaskRule"
]
))5. Additional Security Recommendations
S3 Bucket Policies
Add explicit bucket policies to enforce encryption and deny insecure access:
# Enforce SSL/TLS for all S3 operations
bucket.add_to_resource_policy(iam.PolicyStatement(
sid="DenyInsecureTransport",
effect=iam.Effect.DENY,
principals=[iam.AnyPrincipal()],
actions=["s3:*"],
resources=[
bucket.bucket_arn,
f"{bucket.bucket_arn}/*"
],
conditions={
"Bool": {
"aws:SecureTransport": "false"
}
}
))
# Enforce encryption at rest
bucket.add_to_resource_policy(iam.PolicyStatement(
sid="DenyUnencryptedObjectUploads",
effect=iam.Effect.DENY,
principals=[iam.AnyPrincipal()],
actions=["s3:PutObject"],
resources=[f"{bucket.bucket_arn}/*"],
conditions={
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
))Condition Keys for Enhanced Security
Add condition keys to further restrict permissions:
# Restrict Bedrock access to specific VPC endpoints (if using VPC)
bedrock_policy = iam.PolicyStatement(
actions=["bedrock:InvokeModel"],
resources=[
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/*"
],
conditions={
"StringEquals": {
"aws:SourceVpc": vpc.vpc_id # If using VPC
}
}
)
# Restrict S3 access to specific IP ranges (optional)
s3_policy = iam.PolicyStatement(
actions=["s3:GetObject", "s3:PutObject"],
resources=[f"{bucket.bucket_arn}/*"],
conditions={
"IpAddress": {
"aws:SourceIp": [
"10.0.0.0/8", # Internal network
"YOUR_OFFICE_IP/32"
]
}
}
)Resource Tagging for Access Control
Implement tag-based access control for better governance:
# Tag all resources
tags = {
"Project": "PDFAccessibility",
"Environment": "Production",
"DataClassification": "Confidential"
}
# Use tags in IAM policies
tagged_policy = iam.PolicyStatement(
actions=["s3:GetObject"],
resources=["*"],
conditions={
"StringEquals": {
"s3:ExistingObjectTag/DataClassification": "Confidential"
}
}
)Service Control Policies (SCPs)
For organization-level controls, consider SCPs to prevent privilege escalation:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PreventPrivilegeEscalation",
"Effect": "Deny",
"Action": [
"iam:CreatePolicyVersion",
"iam:SetDefaultPolicyVersion",
"iam:AttachUserPolicy",
"iam:AttachGroupPolicy",
"iam:AttachRolePolicy",
"iam:PutUserPolicy",
"iam:PutGroupPolicy",
"iam:PutRolePolicy"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": [
"arn:aws:iam::ACCOUNT_ID:role/AdminRole"
]
}
}
}
]
}6. Security Scan Findings Summary
Based on the security scan report, the following IAM-related findings were identified:
High Severity Findings
-
Wildcard Permissions in app.py
- Lines 84-87:
bedrock:*onResource: "*" - Lines 88-91:
s3:*onResource: "*" - Lines 275-278:
bedrock:*onResource: "*" - Impact: Allows unrestricted access to all Bedrock models and S3 buckets
- Lines 84-87:
-
Wildcard Permissions in deploy.sh
- Lines 206-305: Full wildcard permissions for PDF-to-PDF deployment
- Lines 247-249:
iam:*onResource: "*"- CRITICAL PRIVILEGE ESCALATION RISK - Lines 286-293:
sts:AssumeRolewith wildcard resource - Impact: Enables assuming any role in the account, creating/modifying any IAM policy
-
Unscoped Resources Throughout
- All IAM policies use
Resource: "*"instead of specific ARNs - No condition statements to further restrict access
- Impact: Violates least privilege principle, increases blast radius
- All IAM policies use
-
Missing Access Controls
- No documented access review procedures
- No audit trails for permission usage
- No MFA requirements for sensitive operations
Medium Severity Findings
-
Hardcoded Secret Paths
/myapp/client_credentialshardcoded in multiple files- Should be externalized to environment variables
- Impact: Reduces flexibility and increases maintenance burden
-
Missing Encryption Configuration
- S3 operations lack explicit
ServerSideEncryptionparameters - Impact: Data may be stored unencrypted
- S3 operations lack explicit
7. Implementation Priority
Phase 1: Critical (Immediate Action Required)
-
Remove
iam:*permissions from deploy.sh (lines 247-249)- Replace with specific IAM actions listed in Section 2
- Risk: Privilege escalation, account compromise
-
Scope all
Resource: "*"to specific ARNs- Start with S3, Lambda, and ECS resources
- Use bucket ARNs, function ARNs, task definition ARNs
-
Replace wildcard actions in app.py
bedrock:*→bedrock:InvokeModels3:*→ specific actions (GetObject, PutObject, etc.)
Phase 2: High Priority (Within 1 Week)
-
Implement resource-level permissions for all services
- CloudFormation stacks
- Step Functions state machines
- CloudWatch log groups
- ECR repositories
-
Add condition keys for enhanced security
- VPC endpoint restrictions for Bedrock
- SSL/TLS enforcement for S3
- Source IP restrictions (if applicable)
-
Externalize hardcoded values
- Move secret paths to environment variables
- Use SSM Parameter Store for configuration
Phase 3: Medium Priority (Within 1 Month)
-
Implement access logging and monitoring
- Enable CloudTrail for all API calls
- Set up CloudWatch alarms for suspicious activity
- Configure S3 access logging
-
Add resource tagging strategy
- Tag all resources with Project, Environment, DataClassification
- Implement tag-based access control policies
-
Document and review
- Create IAM policy review procedures
- Document all permission requirements
- Establish quarterly access reviews
Phase 4: Ongoing
-
Regular security audits
- Run AWS IAM Access Analyzer
- Review unused permissions
- Remove temporary access grants
-
Implement least privilege automation
- Use AWS IAM Access Analyzer policy generation
- Automate permission right-sizing based on CloudTrail logs
8. Testing and Validation
Before Implementing Changes
-
Document current permissions
# Export current IAM policies aws iam get-role-policy --role-name PDFAccessibility-EcsTaskRole \ --policy-name inline-policy > current-policy.json
-
Test in non-production environment
- Deploy changes to dev/test environment first
- Run full end-to-end tests
- Monitor for permission errors
-
Use IAM Policy Simulator
# Test specific actions aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME \ --action-names s3:GetObject bedrock:InvokeModel \ --resource-arns arn:aws:s3:::bucket-name/key
After Implementing Changes
-
Monitor CloudWatch Logs
- Check for AccessDenied errors
- Review Lambda function logs
- Monitor ECS task failures
-
Use AWS IAM Access Analyzer
# Create analyzer aws accessanalyzer create-analyzer \ --analyzer-name pdf-accessibility-analyzer \ --type ACCOUNT # Review findings aws accessanalyzer list-findings \ --analyzer-arn arn:aws:access-analyzer:REGION:ACCOUNT_ID:analyzer/pdf-accessibility-analyzer
-
Validate functionality
- Upload test PDF to S3
- Verify Step Functions execution
- Check output files in result bucket
- Review accessibility reports
Rollback Plan
If issues occur after implementing changes:
-
Immediate rollback
# Restore previous policy version aws iam put-role-policy \ --role-name ROLE_NAME \ --policy-name POLICY_NAME \ --policy-document file://backup-policy.json -
Incremental approach
- Implement changes one service at a time
- Start with least critical components
- Validate each change before proceeding
9. Specific Code Changes Required
app.py Changes
File: app.py
GitHub: https://github.com/ASUCICREPO/PDF_Accessibility/blob/main/app.py
Change 1: Lines 84-91 - ECS Task Role Permissions
Current Code:
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["bedrock:*"],
resources=["*"],
))
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["s3:*"],
resources=["*"],
))Replace With:
# Bedrock permissions for ECS tasks
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=[
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
resources=[
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0",
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.anthropic.claude-3-haiku-20240307-v1:0"
]
))
# S3 permissions for ECS tasks
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=[
"s3:GetObject",
"s3:PutObject"
],
resources=[f"{bucket.bucket_arn}/temp/*"]
))
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["s3:ListBucket"],
resources=[bucket.bucket_arn],
conditions={
"StringLike": {
"s3:prefix": ["temp/*"]
}
}
))Change 2: Lines 92-95 - Secrets Manager Permissions
Current Code:
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["secretsmanager:GetSecretValue"],
resources=[f"arn:aws:secretsmanager:{region}:{account_id}:secret:/myapp/db_credentials"]
))Replace With:
# More specific secret ARN with wildcard suffix for version
ecs_task_role.add_to_policy(iam.PolicyStatement(
actions=["secretsmanager:GetSecretValue"],
resources=[
f"arn:aws:secretsmanager:{region}:{account_id}:secret:/myapp/client_credentials-*"
]
))Change 3: Lines 275-278 - Lambda Bedrock Permissions
Current Code:
add_title_lambda.add_to_role_policy(iam.PolicyStatement(
actions=["bedrock:*"],
resources=["*"],
))Replace With:
add_title_lambda.add_to_role_policy(iam.PolicyStatement(
actions=["bedrock:InvokeModel"],
resources=[
f"arn:aws:bedrock:{region}:{account_id}:inference-profile/us.amazon.nova-pro-v1:0"
]
))Change 4: Lines 289-292, 307-310 - Accessibility Checker Secrets
Current Code:
a11y_precheck.add_to_role_policy(iam.PolicyStatement(
actions=["secretsmanager:GetSecretValue"],
resources=[f"arn:aws:secretsmanager:{region}:{account_id}:secret:/myapp/*"]
))Replace With:
a11y_precheck.add_to_role_policy(iam.PolicyStatement(
actions=["secretsmanager:GetSecretValue"],
resources=[
f"arn:aws:secretsmanager:{region}:{account_id}:secret:/myapp/client_credentials-*"
]
))deploy.sh Changes
File: deploy.sh
GitHub: https://github.com/ASUCICREPO/PDF_Accessibility/blob/main/deploy.sh
Critical Change: Lines 206-305 - PDF-to-PDF CodeBuild Policy
Current Code: Contains wildcard permissions including iam:*
Action Required: Replace the entire POLICY_DOCUMENT variable with the scoped policy provided in Section 2 of this document.
Key Changes:
- Remove
"Action": ["iam:*"]- CRITICAL - Replace with specific IAM actions:
CreateRole,GetRole,PutRolePolicy,AttachRolePolicy,PassRole - Scope all resources to specific ARN patterns
- Add conditions where applicable (e.g.,
aws:RequestedRegion)
Change: Lines 309-378 - PDF-to-HTML CodeBuild Policy
Action Required: Replace with the scoped PDF-to-HTML policy provided in Section 2.
10. AWS Services Permission Matrix
S3 Permissions by Component
| Component | Actions Required | Resource Scope |
|---|---|---|
| Split PDF Lambda | GetObject, PutObject |
bucket-arn/pdf/*, bucket-arn/temp/* |
| ECS Autotag Task | GetObject, PutObject |
bucket-arn/temp/* |
| JavaScript Container | GetObject, PutObject |
bucket-arn/temp/* |
| Java Merger Lambda | GetObject, PutObject, DeleteObject |
bucket-arn/temp/* |
| Add Title Lambda | GetObject, PutObject |
bucket-arn/temp/*, bucket-arn/result/* |
| A11y Checker Lambdas | GetObject, PutObject |
bucket-arn/pdf/*, bucket-arn/result/* |
Bedrock Permissions by Component
| Component | Actions Required | Model ARNs |
|---|---|---|
| ECS Autotag Task | None (uses Adobe API) | N/A |
| JavaScript Container | InvokeModel |
Claude 3.5 Sonnet, Claude 3 Haiku |
| Add Title Lambda | InvokeModel |
Nova Pro v1 |
| PDF-to-HTML Lambda | InvokeModel |
Nova models |
Secrets Manager Permissions
| Component | Actions Required | Secret ARN |
|---|---|---|
| ECS Autotag Task | GetSecretValue |
/myapp/client_credentials-* |
| A11y Checker Lambdas | GetSecretValue |
/myapp/client_credentials-* |
Step Functions Permissions
| Action | Resource Scope | Purpose |
|---|---|---|
ecs:RunTask |
Specific task definition ARNs | Launch ECS tasks |
ecs:StopTask |
Task ARNs | Stop failed tasks |
ecs:DescribeTasks |
Task ARNs | Monitor task status |
lambda:InvokeFunction |
Specific function ARNs | Invoke Lambda functions |
iam:PassRole |
ECS task/execution role ARNs | Pass roles to ECS |
events:PutTargets, PutRule |
Event rule ARNs | ECS task state changes |
CloudWatch Permissions
| Component | Actions Required | Resource Scope |
|---|---|---|
| All Lambda Functions | CreateLogStream, PutLogEvents |
/aws/lambda/function-name:* |
| ECS Tasks | CreateLogStream, PutLogEvents |
/aws/ecs/task-definition-name:* |
| Step Functions | CreateLogStream, PutLogEvents |
/aws/states/state-machine-name:* |
| All Components | PutMetricData |
* (service doesn't support resource-level) |
11. Compliance and Audit
AWS Config Rules
Implement AWS Config rules to continuously monitor IAM compliance:
# Rule: Ensure no wildcard permissions in IAM policies
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "iam-policy-no-wildcards",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS"
}
}'
# Rule: Ensure IAM policies are attached only to groups or roles
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "iam-user-no-policies-check",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "IAM_USER_NO_POLICIES_CHECK"
}
}'IAM Access Analyzer
Enable IAM Access Analyzer for continuous monitoring:
# Create analyzer
aws accessanalyzer create-analyzer \
--analyzer-name pdf-accessibility-analyzer \
--type ACCOUNT \
--tags Key=Project,Value=PDFAccessibility
# List findings
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:REGION:ACCOUNT_ID:analyzer/pdf-accessibility-analyzer \
--filter '{"status":{"eq":["ACTIVE"]}}'CloudTrail Logging
Enable CloudTrail to audit all IAM actions:
# Create trail for IAM events
aws cloudtrail create-trail \
--name pdf-accessibility-iam-trail \
--s3-bucket-name cloudtrail-logs-bucket \
--include-global-service-events \
--is-multi-region-trail
# Start logging
aws cloudtrail start-logging \
--name pdf-accessibility-iam-trail
# Create event selector for IAM events
aws cloudtrail put-event-selectors \
--trail-name pdf-accessibility-iam-trail \
--event-selectors '[{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": []
}]'Quarterly Access Review Checklist
- Review all IAM roles and policies
- Identify unused permissions using IAM Access Analyzer
- Remove temporary access grants
- Verify least privilege compliance
- Update documentation
- Review CloudTrail logs for anomalies
- Check for overly permissive policies
- Validate resource-level permissions
- Review condition keys effectiveness
- Update this document with new findings
12. References and Resources
AWS Documentation
- IAM Best Practices
- IAM Policy Reference
- IAM Access Analyzer
- S3 Bucket Policies
- Bedrock Security
- ECS Task IAM Roles
- Step Functions IAM Policies
Security Best Practices
Tools
13. Summary of Required Actions
Immediate Actions (Critical Priority)
| File | Lines | Current Issue | Required Action | Risk Level |
|---|---|---|---|---|
| deploy.sh | 247-249 | iam:* on Resource: "*" |
Replace with specific IAM actions | 🔴 CRITICAL |
| deploy.sh | 206-305 | All wildcard permissions | Replace entire policy with scoped version | 🔴 HIGH |
| deploy.sh | 309-378 | All wildcard permissions | Replace entire policy with scoped version | 🔴 HIGH |
| app.py | 84-87 | bedrock:* on Resource: "*" |
Scope to specific models | 🔴 HIGH |
| app.py | 88-91 | s3:* on Resource: "*" |
Scope to bucket ARN | 🔴 HIGH |
| app.py | 275-278 | bedrock:* on Resource: "*" |
Scope to Nova model ARN | 🔴 HIGH |
High Priority Actions (Within 1 Week)
| File | Lines | Current Issue | Required Action | Risk Level |
|---|---|---|---|---|
| app.py | 92-95 | Incorrect secret path | Update to /myapp/client_credentials-* |
🟡 MEDIUM |
| app.py | 289-292 | Wildcard secret path | Scope to specific secret | 🟡 MEDIUM |
| app.py | 307-310 | Wildcard secret path | Scope to specific secret | 🟡 MEDIUM |
| All files | N/A | Missing condition keys | Add VPC, IP, SSL conditions | 🟡 MEDIUM |
Medium Priority Actions (Within 1 Month)
- Implement S3 bucket policies for SSL enforcement
- Add resource tagging strategy
- Enable CloudTrail logging
- Configure IAM Access Analyzer
- Set up AWS Config rules
- Document access review procedures
Estimated Impact
Security Improvement:
- Reduces attack surface by ~90%
- Eliminates privilege escalation risks
- Limits blast radius of compromised credentials
- Enables better audit and compliance
Operational Impact:
- Initial implementation: 8-16 hours
- Testing and validation: 4-8 hours
- Documentation updates: 2-4 hours
- Ongoing maintenance: 2 hours/quarter
Risk Reduction:
- Privilege escalation: HIGH → LOW
- Unauthorized access: HIGH → LOW
- Data exfiltration: MEDIUM → LOW
- Compliance violations: HIGH → LOW
14. GitHub Issue Template
Use this template to create a GitHub issue for tracking the implementation:
# IAM Least Privilege Implementation
## Overview
Implement least privilege IAM policies to address security findings from automated security scanning and improve overall security posture.
## Security Findings
- 🔴 **CRITICAL**: `iam:*` permissions in deploy.sh enable privilege escalation
- 🔴 **HIGH**: Wildcard permissions (`bedrock:*`, `s3:*`) across multiple files
- 🔴 **HIGH**: Unscoped resources (`Resource: "*"`) throughout codebase
- 🟡 **MEDIUM**: Hardcoded secret paths should be externalized
## Files Requiring Changes
### app.py
- [ ] Lines 84-87: Replace `bedrock:*` with `bedrock:InvokeModel`
- [ ] Lines 88-91: Replace `s3:*` with specific actions, scope to bucket ARN
- [ ] Lines 92-95: Update secret ARN to include wildcard suffix
- [ ] Lines 275-278: Scope Bedrock permissions to Nova model
- [ ] Lines 289-292: Scope Secrets Manager to specific secret
- [ ] Lines 307-310: Scope Secrets Manager to specific secret
[View app.py in GitHub](https://github.com/ASUCICREPO/PDF_Accessibility/blob/main/app.py)
### deploy.sh
- [ ] Lines 206-305: Replace entire PDF-to-PDF policy with scoped version
- [ ] Lines 309-378: Replace entire PDF-to-HTML policy with scoped version
- [ ] **CRITICAL**: Remove `iam:*` permissions (line 247-249)
[View deploy.sh in GitHub](https://github.com/ASUCICREPO/PDF_Accessibility/blob/main/deploy.sh)
## Implementation Plan
### Phase 1: Critical (Immediate)
- [ ] Remove `iam:*` from deploy.sh
- [ ] Replace wildcard permissions in app.py
- [ ] Test in dev environment
- [ ] Deploy to production
### Phase 2: High Priority (Week 1)
- [ ] Implement resource-level permissions
- [ ] Add condition keys for enhanced security
- [ ] Update documentation
### Phase 3: Medium Priority (Month 1)
- [ ] Enable CloudTrail logging
- [ ] Configure IAM Access Analyzer
- [ ] Set up AWS Config rules
- [ ] Implement access review procedures
## Testing Checklist
- [ ] Deploy to dev environment
- [ ] Run end-to-end PDF processing tests
- [ ] Verify no AccessDenied errors in CloudWatch
- [ ] Test Step Functions execution
- [ ] Validate S3 uploads/downloads
- [ ] Check Bedrock model invocations
- [ ] Review IAM Access Analyzer findings
## Documentation
- [ ] Update IAM_PERMISSIONS.md with new policies
- [ ] Document testing procedures
- [ ] Create rollback plan
- [ ] Update deployment guide
## References
- Full recommendations: `IAM_LEAST_PRIVILEGE_RECOMMENDATIONS.md`
- Security scan report: `holmes-scan-report-29-JAN.md`
- AWS IAM Best Practices: https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html
## Acceptance Criteria
- [ ] No wildcard actions in IAM policies
- [ ] All resources scoped to specific ARNs
- [ ] No `iam:*` permissions
- [ ] Condition keys implemented where applicable
- [ ] All tests passing
- [ ] Documentation updated
- [ ] IAM Access Analyzer shows no high-severity findingsAppendix A: Quick Reference Commands
Check Current Permissions
# List all IAM roles
aws iam list-roles --query 'Roles[?contains(RoleName, `PDFAccessibility`)].RoleName'
# Get role policy
aws iam get-role-policy --role-name ROLE_NAME --policy-name POLICY_NAME
# List attached policies
aws iam list-attached-role-policies --role-name ROLE_NAMETest Permissions
# Simulate S3 access
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME \
--action-names s3:GetObject s3:PutObject \
--resource-arns arn:aws:s3:::bucket-name/key
# Simulate Bedrock access
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME \
--action-names bedrock:InvokeModel \
--resource-arns arn:aws:bedrock:REGION:ACCOUNT_ID:inference-profile/MODEL_IDMonitor Access
# Get recent CloudTrail events
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::IAM::Role \
--max-results 50
# Check IAM Access Analyzer findings
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:REGION:ACCOUNT_ID:analyzer/NAME \
--filter '{"status":{"eq":["ACTIVE"]}}'