AWS code-library medium security documentation change
Summary
Enhanced security hardening in CloudFormation example script: added input validation, secure logging, IMDSv2 enforcement, least-privilege IAM roles, secure file deletion, and automated cleanup.
Security assessment
Changes include concrete security improvements: 1) Input validation for IP/AWS region prevents injection/misconfiguration 2) 'chmod 600' on logs restricts access 3) 'shred' ensures secure file deletion 4) IMDSv2 enforcement (HttpTokens:required) mitigates SSRF risks 5) IAM roles implement least privilege 6) Automatic cleanup prevents resource leakage.
Diff
diff --git a/code-library/latest/ug/bash_2_cloudformation_code_examples.md b/code-library/latest/ug/bash_2_cloudformation_code_examples.md index 277d7a752..94554db6d 100644 --- a//code-library/latest/ug/bash_2_cloudformation_code_examples.md +++ b//code-library/latest/ug/bash_2_cloudformation_code_examples.md @@ -53 +53,3 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Set up logging + set -euo pipefail + + # Set up logging with secure permissions @@ -54,0 +57,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + touch "$LOG_FILE" + chmod 600 "$LOG_FILE" @@ -66,0 +71,17 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + # Function to validate IP address format + validate_ip() { + local ip=$1 + if ! [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + return 1 + fi + + local IFS=. + local -a octets=($ip) + for octet in "${octets[@]}"; do + if ((octet > 255)); then + return 1 + fi + done + return 0 + } + @@ -74 +95 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$STACK_NAME" ]; then + if [ -n "${STACK_NAME:-}" ]; then @@ -76 +97 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - aws cloudformation delete-stack --stack-name "$STACK_NAME" + aws cloudformation delete-stack --stack-name "$STACK_NAME" --region "${AWS_REGION:-us-east-1}" || true @@ -79 +100 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - aws cloudformation wait stack-delete-complete --stack-name "$STACK_NAME" + aws cloudformation wait stack-delete-complete --stack-name "$STACK_NAME" --region "${AWS_REGION:-us-east-1}" 2>/dev/null || true @@ -84 +105 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -f "$TEMPLATE_FILE" ]; then + if [ -f "${TEMPLATE_FILE:-}" ]; then @@ -86 +107 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - rm -f "$TEMPLATE_FILE" + shred -vfz -n 3 "$TEMPLATE_FILE" 2>/dev/null || rm -f "$TEMPLATE_FILE" @@ -99 +120 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if [ -n "$STACK_NAME" ]; then + if [ -n "${STACK_NAME:-}" ]; then @@ -104,4 +125 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - echo "Would you like to clean up these resources? (y/n): " - read -r CLEANUP_CHOICE - - if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then + echo "Cleaning up resources automatically..." @@ -109,3 +126,0 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - else - echo "Resources were not cleaned up. You may need to delete them manually." - fi @@ -117,0 +133,7 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + trap 'cleanup' EXIT + + # Validate AWS region + AWS_REGION="${AWS_REGION:-us-east-1}" + if ! [[ "$AWS_REGION" =~ ^[a-z]{2}-[a-z]+-[0-9]{1}$ ]]; then + handle_error "Invalid AWS_REGION format: $AWS_REGION" + fi @@ -119,3 +141,19 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Generate a unique stack name - STACK_NAME="MyTestStack" - TEMPLATE_FILE="webserver-template.yaml" + # Generate a unique stack name with timestamp + TIMESTAMP=$(date +%s) + STACK_NAME="MyTestStack-${TIMESTAMP}" + TEMPLATE_FILE="webserver-template-${TIMESTAMP}.yaml" + + # Verify AWS CLI is installed + if ! command -v aws &> /dev/null; then + handle_error "AWS CLI is not installed or not in PATH" + fi + + # Verify curl is installed + if ! command -v curl &> /dev/null; then + handle_error "curl is not installed or not in PATH" + fi + + # Verify AWS credentials are configured + if ! aws sts get-caller-identity --region "$AWS_REGION" &> /dev/null; then + handle_error "AWS credentials not configured or invalid" + fi @@ -128,0 +167,12 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + Metadata: + AWS::CloudFormation::Init: + config: + packages: + yum: + httpd: [] + services: + sysvinit: + httpd: + enabled: true + ensureRunning: true + @@ -145 +195 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - Description: Your IP address in CIDR format (e.g. 203.0.113.1/32). + Description: Your IP address in CIDR format (e.g 203.0.113.1/32). @@ -149 +198,0 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - Default: 0.0.0.0/0 @@ -153,0 +203,19 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + WebServerRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: ec2.amazonaws.com + Action: 'sts:AssumeRole' + ManagedPolicyArns: + - 'arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy' + + WebServerInstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Roles: + - !Ref WebServerRole + @@ -157 +225,12 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - GroupDescription: Allow HTTP access via my IP address + GroupDescription: Allow HTTP access via specified IP address + SecurityGroupEgress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + Description: Allow HTTP outbound + - IpProtocol: tcp + FromPort: 443 + ToPort: 443 + CidrIp: 0.0.0.0/0 + Description: Allow HTTPS outbound for package updates @@ -162,0 +242 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + Description: HTTP access from specified IP @@ -168,0 +249 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + IamInstanceProfile: !Ref WebServerInstanceProfile @@ -170,0 +252,5 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + Monitoring: true + MetadataOptions: + HttpEndpoint: enabled + HttpTokens: required + HttpPutResponseHopLimit: 1 @@ -172,0 +259,3 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + set -euo pipefail + exec > >(tee /var/log/user-data.log) + exec 2>&1 @@ -177,0 +267 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + chmod 644 /var/www/html/index.html @@ -185,0 +276,6 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + InstanceId: + Value: !Ref WebServer + Description: EC2 Instance ID + SecurityGroupId: + Value: !Ref WebServerSecurityGroup + Description: Security Group ID @@ -187,0 +284,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + chmod 600 "$TEMPLATE_FILE" + @@ -195,2 +293,3 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - VALIDATION_RESULT=$(aws cloudformation validate-template --template-body "file://$TEMPLATE_FILE" 2>&1) - if [ $? -ne 0 ]; then + if ! VALIDATION_RESULT=$(aws cloudformation validate-template \ + --template-body "file://$TEMPLATE_FILE" \ + --region "$AWS_REGION" 2>&1); then @@ -204 +303,12 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - MY_IP=$(curl -s https://checkip.amazonaws.com) + + MY_IP="" + for endpoint in "https://checkip.amazonaws.com" "https://api.ipify.org" "https://icanhazip.com"; do + if MY_IP=$(curl -s --max-time 5 "$endpoint" 2>/dev/null); then + MY_IP="${MY_IP//[[:space:]]/}" + if validate_ip "$MY_IP"; then + break + fi + MY_IP="" + fi + done + @@ -206 +316 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - handle_error "Failed to retrieve public IP address" + handle_error "Failed to retrieve public IP address from multiple sources" @@ -208,2 +318,3 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - MY_IP="${MY_IP}/32" - echo "Your public IP address: $MY_IP" + + MY_IP_CIDR="${MY_IP}/32" + echo "Your public IP address: $MY_IP_CIDR" @@ -215 +326 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - CREATE_RESULT=$(aws cloudformation create-stack \ + if ! CREATE_RESULT=$(aws cloudformation create-stack \ @@ -220,4 +331,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru