AWS code-library medium security documentation change
Summary
Enhanced security hardening in CloudFront setup script: added input validation, secure permissions, encryption, error handling, and automated cleanup
Security assessment
Changes include: 1) Input validation for AWS account ID, OAC ID, and distribution ID to prevent injection attacks; 2) Secure file permissions (chmod 600/700) for temporary files; 3) Bucket encryption enforcement (AES256); 4) Public access blocking; 5) Secure random ID generation; 6) Redirection of errors to stderr; 7) Removal of sensitive config files during cleanup. These mitigate risks of misconfiguration, data leakage, and unauthorized access.
Diff
diff --git a/code-library/latest/ug/bash_2_cloudfront_code_examples.md b/code-library/latest/ug/bash_2_cloudfront_code_examples.md index a7bae82d2..b53f110a6 100644 --- a//code-library/latest/ug/bash_2_cloudfront_code_examples.md +++ b//code-library/latest/ug/bash_2_cloudfront_code_examples.md @@ -54,0 +55,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + set -euo pipefail + @@ -63 +65 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - echo "ERROR: $1" + echo "ERROR: $1" >&2 @@ -65 +67 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$BUCKET_NAME" ]; then + if [ -n "${BUCKET_NAME:-}" ]; then @@ -68 +70 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$OAC_ID" ]; then + if [ -n "${OAC_ID:-}" ]; then @@ -71 +73 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$DISTRIBUTION_ID" ]; then + if [ -n "${DISTRIBUTION_ID:-}" ]; then @@ -84 +86 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$DISTRIBUTION_ID" ]; then + if [ -n "${DISTRIBUTION_ID:-}" ]; then @@ -87,3 +89,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Get the current configuration and ETag - ETAG=$(aws cloudfront get-distribution-config --id "$DISTRIBUTION_ID" --query 'ETag' --output text) - if [ $? -ne 0 ]; then + # Get the current configuration and ETag in one call + DIST_CONFIG=$(aws cloudfront get-distribution-config --id "$DISTRIBUTION_ID" 2>/dev/null) || { @@ -91,4 +92,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - else - # Create a modified configuration with Enabled=false - aws cloudfront get-distribution-config --id "$DISTRIBUTION_ID" | \ - jq '.DistributionConfig.Enabled = false' > temp_disabled_config.json + DIST_CONFIG="" + } @@ -96 +95,5 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Update the distribution to disable it + if [ -n "$DIST_CONFIG" ]; then + ETAG=$(echo "$DIST_CONFIG" | jq -r '.ETag') + + # Modify and update distribution in one pipeline + if echo "$DIST_CONFIG" | jq '.DistributionConfig.Enabled = false' | \ @@ -99,2 +102,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --distribution-config file://<(jq '.DistributionConfig' temp_disabled_config.json) \ - --if-match "$ETAG" + --distribution-config "$(cat)" \ + --if-match "$ETAG" 2>/dev/null; then @@ -102,3 +104,0 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ $? -ne 0 ]; then - echo "Failed to disable distribution. Continuing with cleanup..." - else @@ -106 +106,3 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - aws cloudfront wait distribution-deployed --id "$DISTRIBUTION_ID" + aws cloudfront wait distribution-deployed --id "$DISTRIBUTION_ID" 2>/dev/null || { + echo "Distribution deployment wait timed out. Proceeding with deletion..." + } @@ -108,4 +110,10 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Delete the distribution - ETAG=$(aws cloudfront get-distribution-config --id "$DISTRIBUTION_ID" --query 'ETag' --output text) - aws cloudfront delete-distribution --id "$DISTRIBUTION_ID" --if-match "$ETAG" - if [ $? -ne 0 ]; then + # Get fresh ETag for deletion + DIST_CONFIG=$(aws cloudfront get-distribution-config --id "$DISTRIBUTION_ID" 2>/dev/null) || { + echo "Failed to get updated config. Skipping distribution deletion..." + DIST_CONFIG="" + } + + if [ -n "$DIST_CONFIG" ]; then + ETAG=$(echo "$DIST_CONFIG" | jq -r '.ETag') + aws cloudfront delete-distribution --id "$DISTRIBUTION_ID" --if-match "$ETAG" 2>/dev/null && \ + echo "CloudFront distribution deleted." || \ @@ -113,2 +120,0 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - else - echo "CloudFront distribution deleted." @@ -115,0 +122,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + else + echo "Failed to disable distribution. Continuing with cleanup..." @@ -120 +128 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$OAC_ID" ]; then + if [ -n "${OAC_ID:-}" ]; then @@ -122,6 +130,9 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - OAC_ETAG=$(aws cloudfront get-origin-access-control --id "$OAC_ID" --query 'ETag' --output text 2>/dev/null) - if [ $? -ne 0 ]; then - echo "Failed to get Origin Access Control ETag. You may need to delete it manually." - else - aws cloudfront delete-origin-access-control --id "$OAC_ID" --if-match "$OAC_ETAG" - if [ $? -ne 0 ]; then + OAC_DATA=$(aws cloudfront get-origin-access-control --id "$OAC_ID" 2>/dev/null) || { + echo "Failed to get Origin Access Control. You may need to delete it manually." + OAC_DATA="" + } + + if [ -n "$OAC_DATA" ]; then + OAC_ETAG=$(echo "$OAC_DATA" | jq -r '.ETag') + aws cloudfront delete-origin-access-control --id "$OAC_ID" --if-match "$OAC_ETAG" 2>/dev/null && \ + echo "Origin Access Control deleted." || \ @@ -129,3 +139,0 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - else - echo "Origin Access Control deleted." - fi @@ -135 +143 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$BUCKET_NAME" ]; then + if [ -n "${BUCKET_NAME:-}" ] && [ "$BUCKET_IS_SHARED" != "true" ]; then @@ -137,2 +145 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - aws s3 rm "s3://$BUCKET_NAME" --recursive - if [ $? -ne 0 ]; then + aws s3 rm "s3://$BUCKET_NAME" --recursive 2>/dev/null || { @@ -140 +147 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - fi + } @@ -142,2 +149,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - aws s3 rb "s3://$BUCKET_NAME" - if [ $? -ne 0 ]; then + aws s3 rb "s3://$BUCKET_NAME" 2>/dev/null && \ + echo "S3 bucket deleted." || \ @@ -145,3 +151,0 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - else - echo "S3 bucket deleted." - fi @@ -151,2 +155,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - rm -f temp_disabled_config.json - rm -rf temp_content + rm -f temp_disabled_config.json distribution-config.json bucket-policy.json 2>/dev/null || true + rm -rf temp_content 2>/dev/null || true @@ -155 +159,25 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Generate a random identifier for the bucket name + # Trap errors and cleanup + trap 'handle_error "Script interrupted"' INT TERM + + # Validate AWS CLI is available + if ! command -v aws &> /dev/null; then + handle_error "AWS CLI is not installed or not in PATH" + fi + + # Validate jq is available + if ! command -v jq &> /dev/null; then + handle_error "jq is not installed or not in PATH" + fi + + # Validate AWS credentials are configured + if ! aws sts get-caller-identity &> /dev/null; then + handle_error "AWS credentials are not configured or invalid" + fi + + # Initialize variables + BUCKET_NAME="" + OAC_ID="" + DISTRIBUTION_ID="" + BUCKET_IS_SHARED=false + + # Generate a random identifier for the bucket name using secure random @@ -157 +185,5 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Check for shared prereq bucket + if [ -z "$RANDOM_ID" ]; then + handle_error "Failed to generate random identifier" + fi + + # Check for shared prereq bucket and get account ID in parallel calls @@ -159 +191,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' --output text 2>/dev/null) + --query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' --output text 2>/dev/null) || PREREQ_BUCKET="" + @@ -170 +203,12 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Create a temporary directory for content + # Get AWS account ID early + ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) + if [ $? -ne 0 ]; then + handle_error "Failed to get AWS account ID" + fi + + # Validate account ID format + if ! [[ "$ACCOUNT_ID" =~ ^[0-9]{12}$ ]]; then + handle_error "Invalid AWS account ID format: $ACCOUNT_ID" + fi + + # Create a temporary directory for content with restrictive permissions @@ -172,0 +217 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + chmod 700 "$TEMP_DIR" @@ -177 +222,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Step 1: Create an S3 bucket + # Step 1: Create an S3 bucket (only if not shared) + if [ "$BUCKET_IS_SHARED" != "true" ]; then @@ -179 +225 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - aws s3 mb "s3://$BUCKET_NAME" + aws s3 mb "s3://$BUCKET_NAME" --region us-east-1 @@ -183,0 +230,19 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + # Batch bucket configuration calls for efficiency + aws s3api put-bucket-versioning --bucket "$BUCKET_NAME" --versioning-configuration Status=Enabled & + aws s3api put-public-access-block \ + --bucket "$BUCKET_NAME" \ + --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" & + aws s3api put-bucket-encryption \ + --bucket "$BUCKET_NAME" \ + --server-side-encryption-configuration '{ + "Rules": [ + { + "ApplyServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + }