Description
AWS credentials embedded in EC2 user_data can be exposed through source repositories, Terraform state, initialization logs or instance metadata. An access key ID alone cannot authorize AWS API calls, but disclosure of its corresponding secret access key can allow misuse of the credentials’ permissions.
Attach an IAM role through an instance profile and configure applications to use temporary credentials instead of static keys.
Potential impact
- Valid credentials may be reused from another environment to perform permitted AWS operations.
- Secrets copied into scripts and deployment materials are difficult to recover and replace.
- Replacing credentials can interrupt initialization tasks or applications that depend on them.
Remediation
- Remove access key IDs and secret access keys from user data and any scripts it invokes.
- Give the EC2 role only the required actions and resources, and let the AWS SDK use its temporary credentials. Retrieve external-service secrets from a managed service such as Secrets Manager with restricted access.
- If real credentials were exposed, identify their consumers, replace and revoke them, and investigate related API activity. Restrict access to source, state files and initialization logs.
Examples
These are partial examples. Supply environment-appropriate ami_id and subnet_id values and configure the IAM instance profile separately.
Before
module "ec2_instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "~> 3.0"
name = "single-instance"
ami = var.ami_id
subnet_id = var.subnet_id
user_data = "1234567890123456789012345678901234567890$"
}
The string is placed directly in user_data. This illustrative value does not establish that an AWS credential has been issued; an actual secret access key should not be stored here.
After
module "ec2_instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "~> 3.0"
name = "single-instance"
ami = var.ami_id
subnet_id = var.subnet_id
user_data = file("scripts/first-boot-http.sh")
}
file() reads the script and supplies its contents as user data. Moving a secret into a file does not protect it, so keep credentials out of the script and configure role-based access.