Open leozhad opened 5 years ago
any news on this?
The private resource type Community::S3::PublicAccessBlock
can be used in the meantime.
Installation instructions:
aws cloudformation register-type \
--region us-east-1 \
--type-name "Community::S3::PublicAccessBlock" \
--schema-handler-package "s3://community-resource-provider-catalog/community-s3-publicaccessblock-0.1.0.zip" \
--type RESOURCE \
--execution-role-arn <ROLE_ARN_WITH_ENOUGH_PRIVILEGE>
Usage example:
AWSTemplateFormatVersion: 2010-09-09
Resources:
S3AccountPublicAccessBlock:
Type: 'Community::S3::PublicAccessBlock'
Properties:
BlockPublicAcls: true
BlockPublicPolicy: false
IgnorePublicAcls: true
RestrictPublicBuckets: true
This is a frustrating gap in CloudFormation.
As AWS's infrastructure-as-code tool, CloudFormation should be able to build all desired AWS infrastructure in a brand new AWS account using code, right?
But the inability to disable this account-wide feature via officially-supported CFN resource types means that CFN cannot do that when those resources include S3 buckets with public access. :stuck_out_tongue_closed_eyes:
The error message associated with violating this account-wide public access block is also quite unhelpful:
"MyBucket": {
"Type": "AWS::S3::Bucket"
}
"BucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {"Ref": "MyBucket"},
"PolicyDocument": {
"Statement":[
{
"Action":["s3:GetObject"],
"Effect":"Allow",
"Resource": { "Fn::Sub" : "${MyBucket.Arn}/*" },
"Principal": "*"
}
]
}
}
}
Attempting to create this stack results in the error API: s3:PutBucketPolicy Access Denied
. Because of this error, I spent a bunch of time trying to figure out if there was some race condition in the order of creation of the bucket and the policy, or some scenario in which the owner of an S3 bucket can't put a policy to the bucket they just created… when in fact neither of those had anything to do with the problem.
Just to add, this would be a great addition to use in StackSets applied to an Organization. All new and existing accounts could get BlockPublicAccess as a security baseline.
any updates on this? ive found some workarounds but wondering if theres something in the works to make this a lot easier
This can be deployed as a stack set:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Security: S3 Public Access Block Configuration"
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Lambda Function Configuration"
Parameters:
- LoggingLevel
- Label:
default: "S3 Public Access Block Configuration"
Parameters:
- BlockPublicAcls
- BlockPublicPolicy
- IgnorePublicAcls
- RestrictPublicBuckets
ParameterLabels:
BlockPublicAcls:
default: "Block Public ACLs"
BlockPublicPolicy:
default: "Block Public Policy"
IgnorePublicAcls:
default: "Ignore Public ACLs"
LoggingLevel:
default: "Logging Level"
RestrictPublicBuckets:
default: "Restrict Public Buckets"
Parameters:
BlockPublicAcls:
AllowedValues: ["True", "False"]
Default: "True"
Description: "S3 will block public access permissions applied to newly added buckets or objects, and prevent the creation of new public access ACLs for existing buckets and objects. This setting doesn't change any existing permissions that allow public access to S3 resources using ACLs."
Type: String
BlockPublicPolicy:
AllowedValues: ["True", "False"]
Default: "True"
Description: "S3 will block new bucket and access point policies that grant public access to buckets and objects. This setting doesn't change any existing policies that allow public access to S3 resources."
Type: String
IgnorePublicAcls:
AllowedValues: ["True", "False"]
Default: "True"
Description: "S3 will ignore all ACLs that grant public access to buckets and objects."
Type: String
LoggingLevel:
AllowedValues: ["CRITICAL", "DEBUG", "ERROR", "INFO", "WARNING"]
Default: "INFO"
Description: "The logging level for the Lambda function."
Type: String
RestrictPublicBuckets:
AllowedValues: ["True", "False"]
Default: "True"
Description: "S3 will ignore public and cross-account access for buckets or access points with policies that grant public access to buckets and objects."
Type: String
Resources:
LambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: "sts:AssumeRole"
Description: "Used by the S3 Public Access Block Lambda to update the account-level S3 public access block configuration."
Policies:
- PolicyName: LambdaExecutionPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "arn:aws:logs:*:*:*"
- Effect: Allow
Action:
- "s3:DeletePublicAccessBlock"
- "s3:PutAccountPublicAccessBlock"
- "sts:GetCallerIdentity"
Resource: "*"
RoleName: "s3-public-access-block-update"
S3PublicAccessBlockLambda:
Type: "AWS::Lambda::Function"
Properties:
Architectures:
- x86_64
Code:
ZipFile: |
import os
import logging
import boto3
from botocore.exceptions import ClientError
import cfnresponse
logger = logging.getLogger()
logger.setLevel(os.environ['LOGGING_LEVEL'])
s3control_client = boto3.client("s3control")
sts_client = boto3.client('sts')
account_id = sts_client.get_caller_identity()["Account"]
def lambda_handler(event, context):
logger.info(f"Received event: {event}")
physical_id = event['LogicalResourceId']
try:
if event['RequestType'] in ['Create', 'Update']:
set_public_access_block(event['ResourceProperties'])
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, physical_id)
elif event['RequestType'] == 'Delete':
delete_public_access_block()
cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, physical_id)
else:
logger.error(f"Invalid request type: {event['RequestType']}")
cfnresponse.send(event, context, cfnresponse.FAILED, {}, physical_id)
except Exception as e:
logger.error(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {}, physical_id)
def set_public_access_block(properties):
try:
s3control_client.put_public_access_block(
PublicAccessBlockConfiguration={
'BlockPublicAcls': properties['BlockPublicAcls'].lower() == 'true',
'BlockPublicPolicy': properties['BlockPublicPolicy'].lower() == 'true',
'IgnorePublicAcls': properties['IgnorePublicAcls'].lower() == 'true',
'RestrictPublicBuckets': properties['RestrictPublicBuckets'].lower() == 'true'
},
AccountId=account_id
)
logger.info("S3 Public Access Block configuration updated successfully")
except ClientError as e:
logger.error(f"Error updating S3 Public Access Block configuration: {str(e)}")
raise
def delete_public_access_block():
try:
s3control_client.delete_public_access_block(AccountId=account_id)
logger.info("S3 Public Access Block configuration deleted successfully")
except ClientError as e:
if e.response['Error']['Code'] == 'NoSuchPublicAccessBlockConfiguration':
logger.info("S3 Public Access Block configuration was already deleted or did not exist")
else:
logger.error(f"Error deleting S3 Public Access Block configuration: {str(e)}")
raise
Description: "Updates and deletes the account-level S3 Public Access Block configuration."
Environment:
Variables:
LOGGING_LEVEL: !Ref LoggingLevel
FunctionName: "s3-public-access-block-update"
Handler: index.lambda_handler
LoggingConfig:
LogGroup: !Ref S3PublicAccessBlockLambdaLogGroup
MemorySize: 128
PackageType: Zip
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: python3.12
Timeout: 10
S3PublicAccessBlockLambdaLogGroup:
Type: "AWS::Logs::LogGroup"
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
RetentionInDays: 7
S3PublicAccessBlock:
Type: "Custom::S3PublicAccessBlock"
Properties:
BlockPublicAcls: !Ref BlockPublicAcls
BlockPublicPolicy: !Ref BlockPublicPolicy
IgnorePublicAcls: !Ref IgnorePublicAcls
RestrictPublicBuckets: !Ref RestrictPublicBuckets
ServiceToken: !GetAtt S3PublicAccessBlockLambda.Arn
Instructions for CloudFormation Coverage New Issues Template
Quick Summary: