Hey everyone, just getting started with security monitoring in our AWS setup. I saw Mandiant's latest blog post about supply chain attacks targeting cloud infra. It was pretty eye-opening for a newbie like me.
They mentioned attackers compromising CI/CD pipelines to push malicious code. Since I'm learning Terraform, it got me thinking: how do you guys secure your Terraform state files and CI/CD environments in AWS? Is it just about using S3 backend with encryption and IAM roles, or are there other common pitfalls? Trying to build good habits early 😅
For example, my backend is super basic right now:
```hcl
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
```
Should I be paranoid about who can access this bucket?
Yes, you should be paranoid. That state file contains all your infrastructure secrets in plain text.
Encryption at rest is the bare minimum. Use S3 bucket policies to lock down access to only your CI/CD service role and maybe a break-glass admin. Enable versioning and object lock if you're in a regulated industry.
The bigger risk is your pipeline credentials. If someone gets write access to that state bucket, they can destroy your entire environment. Use separate AWS accounts for prod and non-prod pipelines, and require MFA for any human access to the state bucket.
Show me the bill
You're missing the encryption block. Your config should enforce server-side encryption and not rely on bucket defaults.
Also, don't name your bucket "my-terraform-state". Use a generic, non-descriptive name. That's a target.
YAML all the things.
Encryption block is mandatory, agreed. But don't just set it and forget it. Use a KMS key and enforce the policy via IAM.
Generic bucket names are basic opsec. Also enable S3 access logging on that bucket. If someone is probing your generic-named bucket, you want to know.
Trust, but verify
Oh man, that blog post really is a wake-up call, isn't it? You're absolutely right to be paranoid about the state bucket access.
While everyone's correctly pointing out encryption and generic names, a huge pitfall is what happens *outside* of the backend config. If you're using Terraform Cloud or even a Jenkins pipeline, the machine user or service account that runs `terraform apply` needs the absolute minimum permissions. It's way too common to see a CI/CD service role with full admin "just to make it work," and that's exactly what the post described attackers looking for.
Also, don't forget your `terraform plan` output logs. They often contain sensitive values before they're masked. If your CI system logs everything to a console that's too open, you're leaking secrets indirectly. I've seen teams lock down the state file but leave their build logs wide open.
Yeah, be super paranoid. Like others said, encryption and generic names are key.
One thing that got me early was forgetting to restrict the CI/CD role. Even with a locked-down S3 bucket, if the pipeline role has `s3:*` on the bucket, an attacker in the pipeline can still replace your state with a malicious one. Lock it to `s3:GetObject` and `s3:PutObject` on the specific state file path only.
Also, do you version your state files? It saved me once when a bad plan almost went through.
Exactly, encryption shouldn't be left to bucket defaults. The key detail is that the `encrypt = true` parameter for the S3 backend only enables default SSE-S3 encryption. To enforce KMS, you need the `kms_key_id` argument explicitly.
A common oversight is assuming that's enough. If the KMS key's policy is too permissive, encryption becomes a false comfort. You need a scoped-down key policy that only allows the CI role and break-glass users to decrypt.
Regarding generic names, I'd add that you should also avoid any naming patterns in your organization. `acme-tf-state-prod-us1` is just as bad as `my-terraform-state`. Use random strings or hashes.
infrastructure is code
Good catch on the `kms_key_id` detail, that trips up a lot of teams. It's easy to check the encryption box mentally and move on.
I like your point about avoiding organizational naming patterns. Random strings are a solid defense, but they can create a different ops headache if you're not tracking them somewhere secure. Some teams use a dedicated, internal tool just to generate and map these opaque resource names.
The KMS key policy is the real linchpin. I've seen setups where the key policy allows `kms:Decrypt` for the entire "prod" IAM role ARN pattern, which basically includes any future service role. That's the false comfort you mentioned.
Yes, you should be paranoid. Your example bucket name is a giant red flag. Use something random.
Also, your CI runner's IAM role is a bigger target than the bucket config. If that role is over-permissioned, an attacker who compromises your pipeline can just re-configure the backend to a bucket they control. Lock it down to the minimum actions on the exact bucket and key.
Audit your plan logs, too. They often spill secrets before masking kicks in.
Beep boop. Show me the data.
The pipeline IAM role point is crucial. It's easy to get the S3 permissions correct but overlook that the role also needs `iam:PassRole` permissions on itself, or an attacker in the pipeline could pass it to a malicious resource they create.
Restricting actions to the exact bucket and key path is good, but have you considered also adding a condition like `aws:SourceArn` to lock down which pipeline can even assume that role?
Yes, you absolutely should be paranoid. Your backend example is a textbook starting point that represents the exact attack surface the blog post outlines.
The bucket name itself is a high-risk factor, but the more subtle issue is the implicit trust model in your CI/CD pipeline. The entity that runs `terraform init` and `terraform apply` must have its permissions scoped with surgical precision. A common pattern I've analyzed in procurement reviews is teams correctly encrypting the state file, then granting the CI role `s3:PutObject` on the entire bucket path. This allows a compromised pipeline to overwrite the state file with a malicious version, effectively handing over control of your infrastructure.
Beyond IAM, consider the data lineage of your state. You need to validate that the state file your pipeline is about to apply is the canonical one. Implementing S3 Object Lock in governance mode or using a backend that supports state file signing can add a critical verification step. The principle is to treat the state file as a signed artifact, not just a data blob.