Instance profiles, service roles, execution roles
Lesson 2 of 6 in Securing Compute Workloads.
A workload needs an identity for the same reason a person does: so that its permissions can be scoped, logged, and revoked. On AWS that identity is always a role, never a key — and the exam tests whether you can name the right shape of role for the thing that is running.
Instance profile — the wrapper that attaches a role to an EC2 instance. The role is the identity; the instance profile is the container that makes it attachable. Code on the instance gets temporary credentials from the Instance Metadata Service and the SDKs find them automatically. This is also the single most common correction on this exam: an access key in ~/.aws/credentials on an instance becomes an instance profile.
Service role — a role an AWS service assumes to act on your behalf, with the service as the trust-policy principal: AWS Config recording your resources, CodeBuild running a build, Image Builder driving a build instance, Backup copying snapshots. A service-linked role is the special case the service creates and maintains itself, with a predefined policy you cannot casually widen.
Execution role — the identity assumed by code the platform runs for you: a Lambda function, an ECS task, a SageMaker job. For Lambda it is both the function's identity and how Lambda writes the function's logs to CloudWatch and reads from poll-based event sources.
And one distinction the exam reaches for often enough to deserve its own table: ECS has two roles per task, and they belong to different actors.
Interactive sorting exercise: Each workload needs an identity. Which shape of role authorizes it?
| Task role | Task execution role | |
|---|---|---|
Who assumes it | your application code, inside the container | the ECS agent or the Fargate infrastructure, around the container |
Typical permissions | the AWS APIs the application calls — S3 prefixes, a DynamoDB table, an SQS queue, a KMS key |
|
When it is used | for the lifetime of the running container | mostly before your code starts, and for log delivery while it runs |
Symptom when it is wrong | the application gets AccessDenied from an AWS API it needs | the task never starts: |
Shared or per-task | per task definition — least privilege lives here | often shared, because its permissions are plumbing rather than business access |
Now the delivery mechanism, because it is also an attack surface. On EC2, credentials for the instance profile's role are served by the Instance Metadata Service at the link-local address 169.254.169.254. In the original protocol, IMDSv1, a plain GET to that address returns the role's temporary credentials. Any code on the instance can read them — and so can anything that can persuade code on the instance to make an HTTP request on its behalf. That is server-side request forgery, and it is how several well-known breaches turned a web-application bug into stolen AWS credentials.
IMDSv2 closes it by making metadata access session-oriented: you first send a PUT to /latest/api/token carrying a TTL header, receive a session token, and then include that token as a header on every GET. Three properties do the work. A PUT is required, and most SSRF primitives can only issue GETs. A custom request header is required, which blocks naive open-proxy and redirect tricks. And the response carries a hop limit (default 1), so the token cannot be relayed off the instance by a container in bridge networking or a reverse proxy — set it to 2 if your containers legitimately need metadata.
Treat IMDSv2 as mandatory, and enforce it rather than requesting it: set HttpTokens: required in launch templates, apply ModifyInstanceMetadataOptions to instances already running, set the account-level IMDS defaults per Region so new instances require it even when a launch forgets, add an SCP denying ec2:RunInstances unless ec2:MetadataHttpTokens is required, and run the AWS Config rule that flags non-compliant instances. If a workload needs no role at all, go further and disable the metadata endpoint entirely.
The SSRF path to credentials — and where IMDSv2 ends it
- App fetches a URL the user supplies
An image-preview, webhook, PDF-render or URL-health feature — any place the server fetches a user-controlled address.
- Attacker points it at 169.254.169.254
The link-local metadata address is reachable from the instance itself, which is exactly what the attacker is borrowing.
- Does the instance still allow IMDSv1?
HttpTokens: optional means both protocols answer. HttpTokens: required means v1 requests are refused.
- GET returns the role’s temporary credentials
The SDK-shaped JSON includes AccessKeyId, SecretAccessKey and Token — everything needed to call AWS as the instance.
- Credentials used from outside the account
GuardDuty’s UnauthorizedAccess:EC2/InstanceCredentialExfiltration finding exists precisely for this moment — detection after the fact.
- IMDS demands a PUT for a session token
PUT /latest/api/token with X-aws-ec2-metadata-token-ttl-seconds. No token, no metadata.
- Can the SSRF primitive send a PUT with custom headers?
Usually not: the vulnerable feature issues a GET and controls the URL, not the method or the headers.
- 401 — no credentials, nothing to exfiltrate
The hop limit (default 1) also stops a proxy or bridge-mode container from relaying the token off the instance.
Key terms: Instance profile, Service role, ECS task role, ECS task execution role, IMDSv2
Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.