Three AWS Credential Files in One Container: Telling Them Apart
The challenge
Finance traced unexpected S3 writes to the production AWS account 402198773541, and the trail points at the checkout container. You have a shell inside that pod. The container can reach three separate AWS credential files, dropped there by three different mechanisms: a mounted Secret, a ConfigMap, and the app user's own home directory. Two of them are harmless, one belongs to a sandbox account and one to a CI account that was switched off in April. Each file records the account id on the line above the key. Read all three, match the account id against the production one, and submit the access key id that belongs to it.
What you'll learn
- Enumerate the credential material reachable from inside a container
- Tell a mounted Secret, a ConfigMap and a baked-in file apart by where they live
- Use the AWS account id to attribute a key to an environment
- Recognise that a ConfigMap is not a secret store
- Spot a credential that ships in the image rather than being mounted
Skills tested
Prerequisites
- Basic shell navigation
- Knowing roughly what a Kubernetes pod mounts
How it works
A container almost never has exactly one credential. It accumulates them: something mounted by the chart, something baked into the image by a Dockerfile step, something a developer copied in to get an integration working on a Friday. From inside the pod they all look equally plausible, and the path tells you nothing about which account a key opens.
That is why the account id matters. An AWS access key id is an opaque string, but the account it belongs to is the thing that decides whether a finding is an incident or a shrug. Here the audit note names the production account, 402198773541, and each credentials file records its own account on the line above the key. The work is matching, not decoding.
The mounted Secret at /var/run/secrets/aws/credentials is the decoy precisely because it is the most legitimate-looking artifact in the container. It is sandbox. The ConfigMap at /etc/config/app.properties carries a key too, which is its own finding since a ConfigMap is stored unencrypted and is readable by anyone with list access on the namespace, but that account was decommissioned in April. The match is /home/appuser/.aws/credentials.
The second finding is the one people miss. Check /app/deploy/values.yaml and the home directory is not in the volume list, so that file was never mounted. It travels inside the image, which means every replica carries it, it survives a secret rotation, and anyone who can pull the image gets it without touching the cluster at all.
Common mistakes
- Submitting the first key you find. The mounted Secret is the most official-looking file and the wrong answer. Read all three before deciding.
- Assuming the Secret mount is the production one. Where a credential is mounted says nothing about which account it opens.
- Ignoring the ConfigMap because it is not called a secret. That is exactly why a key sitting in one is worth reporting.
- Submitting the account id instead of the key id. The account id identifies the environment; the answer is the
AKIA...string. - Hunting for the secret access key. It is redacted, and you do not need it. The key id is enough to attribute activity.
How to defend against it
Nothing here needed an exploit, which is the point: the container was handed three sets of credentials and only one of them was even supposed to be there.
- Stop shipping static keys to workloads. Use IAM Roles for Service Accounts so the pod gets short-lived credentials tied to its identity and there is no file to leak.
- Never put credential material in a ConfigMap. It is unencrypted at rest and readable by anyone with list access on the namespace.
- Fail the build on credentials baked into an image. A key in the image layer outlives every secret rotation and leaks with the image.
- Tag keys with their environment and alert on production keys used from unexpected workloads, which is what caught this one.
- Delete decommissioned credentials rather than leaving them in place. The CI account was switched off in April and its key was still sitting in the config.