Introduction
Containment lets Field Effect cut off a compromised AWS identity - and undo it later. It is off until you complete the two steps below, and it stays off for every account where you do not.
The whole mechanism is one policy that you create and own. The grant below adds permission to attach and detach that policy, and nothing else. Containment also reads IAM before it writes, using the read-only access the integration already holds.
What Field Effect Can and Cannot do
With the grant below, Field Effect can attach a deny-all policy you wrote to an IAM user or role, and detach it again.
It cannot write or edit any policy document, cannot grant any permission, cannot detach a policy you attached, and cannot set, change or remove a permissions boundary.
Before it writes, it reads three things:
- the principal's tags, so a protected principal is left alone;
- The policies already attached to it, so an identity contained earlier is not contained twice;
- The containment policy itself, for the readiness check below. Those reads come from the read-only IAM access the integration already holds for user synchronization, so the grant below asks for no read permission. If that access was never granted, the integration already reports missing list-users permission, and containment will fail when it tries to read the principal.
The grant applies to any principal, so an identity an attacker creates after you onboard can still be contained. What limits it is the condition rather than the resource: the only policy it can attach or detach is the one you created.
It is not possible for that credential to escalate privilege in your account. The worst case is that principals are denied, which you can undo yourself in the console — though denying the wrong one is not always a person losing access, and the section on protected principals below says what to tag so that it cannot happen to your own automation.
Step 1: Create the Containment Policy
Create a customer-managed policy named exactly FieldEffectMDRContainment. Attach it to nothing; we attach it when we contain someone, and detach it to release them.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAll",
"Effect": "Deny",
"Action": "*",
"Resource": "*"
}
]
}Do not add a condition to this policy. An explicit deny overrides every allow, including permissions granted by a resource policy such as an S3 bucket policy, so this one statement is what makes containment complete. We check the document on a schedule and report the integration as unhealthy if it has been narrowed, because containment would then apply to less than it claims.
Step 2: Grant Attach and Detach on the Policy
Add this to the IAM identity you created for Field Effect. Replace <ACCOUNT_ID> with your account id, and <FIELD_EFFECT_PRINCIPAL> with the name of your containment identity.
If we sign in through your identity provider rather than with an access key, we operate as a role, so name that role as <FIELD_EFFECT_ROLE> — list whichever applies to you, or both.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AttachAndDetachContainmentPolicyOnly",
"Effect": "Allow",
"Action": [
"iam:AttachUserPolicy",
"iam:DetachUserPolicy",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy"
],
"Resource": "*",
"Condition": {
"ArnEquals": {
"iam:PolicyARN": "arn:aws:iam::<ACCOUNT_ID>:policy/FieldEffectMDRContainment"
}
}
},
{
"Sid": "NeverContainProtectedPrincipals",
"Effect": "Deny",
"Action": [
"iam:AttachUserPolicy",
"iam:AttachRolePolicy"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:ResourceTag/field-effect-mdr-response": "protected"
}
}
},
{
"Sid": "NeverContainTheFieldEffectPrincipal",
"Effect": "Deny",
"Action": [
"iam:AttachUserPolicy",
"iam:AttachRolePolicy"
],
"Resource": [
"arn:aws:iam::<ACCOUNT_ID>:user/<FIELD_EFFECT_PRINCIPAL>",
"arn:aws:iam::<ACCOUNT_ID>:role/<FIELD_EFFECT_ROLE>"
]
}
]
}The ArnEquals condition is what limits us to your one policy. The two Deny statements are enforced by IAM rather than by our software, so they hold even if our systems were compromised.
The last statement denies only the two attach actions, which is all it needs to stop us containing the identity we operate through. Denying iam:* on that ARN instead would also block the readiness check, which reads the containment policy and simulates the grant against our own principal.
Step 3 (recommended): tag the principals you never want contained
Tag break-glass administrators, CI roles, and anything your own security tooling runs as:
field-effect-mdr-response = protected
The second Deny statement above then makes those principals uncontainable. Anything you do not tag is in scope, so it is worth walking your automation roles specifically — log forwarding, backup, and automated remediation. Denying one of those does not expose credentials, but it creates a blind spot, which is its own kind of incident.
Restrict who can Apply that tag
Because IAM enforces the exclusion, write access to the field-effect-mdr-response key is a way out of containment: an identity holding iam:TagUser or iam:TagRole can tag a principal it controls, and from then on IAM refuses our attach. Restrict the key to a small set of administrators, with a service control policy or a permissions boundary. Adapt the partition and the administrator ARN to your organisation:
{
"Sid": "RestrictFieldEffectMdrResponseTag",
"Effect": "Deny",
"Action": [
"iam:TagRole",
"iam:UntagRole",
"iam:TagUser",
"iam:UntagUser"
],
"Resource": "*",
"Condition": {
"ForAnyValue:StringEquals": {
"aws:TagKeys": ["field-effect-mdr-response"]
},
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::<ACCOUNT_ID>:role/<TAG_ADMIN_ROLE>"
}
}
}"aws:TagKeys" matches the key named in the request, so this covers applying the tag and removing it. It is a different condition key from the iam:ResourceTag/... the grant uses, which matches a tag already on the principal.
Releasing a Containment Yourself
You never need us to undo this. In the IAM console, open the user or role and detach the FieldEffectMDRContainment policy. That is the whole undo — nothing else was changed, so the identity returns to exactly its prior state.
To find everything currently contained:
aws iam list-entities-for-policy \ --policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/FieldEffectMDRContainment
What Containment Does
An identity-based deny is evaluated on every request against the principal's current policies, so containment takes effect within seconds and applies to sessions that were already issued. A stolen token stays cryptographically valid and authorises nothing. This covers console sign-ins, long-lived access keys, and GetFederationToken credentials minted from those keys, AssumeRole sessions including EC2 instance profiles, SAML federation, and IAM Identity Center. Pre-signed S3 URLs already issued also stop working, because they are evaluated against the signer's current permissions.
Current Limitations
Containing a role affects everyone using it. For a federated or Identity Center identity there is no per-user lever from inside a member account — AWS provides no API to terminate an individual Identity Center session, and its own containment runbook has the same behaviour. IAM Identity Center creates a separate role per permission set per account, so the reach is everyone holding that permission set in that one account. We say who is affected in the alert.
Root cannot be contained. No IAM policy applies to the root user. If root is implicated we report it with manual steps instead.
A session that had already assumed another role is not covered. If the identity called sts:AssumeRole before containment landed, that second session is authorised against the second role and is untouched. It cannot be renewed once we deny the original principal, so the exposure closes on its own, but how long that takes depends on what was contained. Containing a role caps the onward session at an hour, because assuming a role from role credentials is limited to that. Containing an IAM user does not: the session it had already assumed can run for as long as that role's maximum session duration allows, up to twelve hours.
An identity that already holds IAM write permissions can undo containment. IAM takes a few seconds to propagate, and a principal that can grant itself administrative access can strip the policy inside that window. We re-apply a bounded number of times, and if it keeps being removed we report that as its own finding, because an adversary fighting containment is more serious than the alert that started it. No in-account mechanism can contain an in-account administrator; the answer is the service control policy below.
The protected tag can stop a containment before it starts. The Deny that honours the tag is enforced by IAM against our credential, so a principal that can tag itself field-effect-mdr-response = protected is uncontainable without ever touching the policy. Tagging takes less privilege than detaching, which is why Step 3 recommends restricting who can set that key.
We do not monitor a containment after we apply it. If you detach the policy yourself we will not know and will not put it back.
A principal already holding the maximum number of attached policies cannot take another, so the attach fails. We report that specifically rather than appearing to succeed.
For an administrative compromise: a quarantine SCP
The only reliable answer when the compromised identity can grant itself administrative access is a service control policy, because it is enforced from your organisation and nobody inside the member account can remove it.
Field Effect deliberately holds no organisation-level access, so this is yours to apply. It is worth creating it in advance, attached to nothing, and giving your break-glass incident-response role permission to attach it — then responding is one command against a policy you have already reviewed.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "QuarantineCompromisedPrincipal",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"ArnEquals": {
"aws:PrincipalArn": [
"arn:aws:iam::<ACCOUNT_ID>:user/<PRINCIPAL>"
]
}
}
}
]
}Alongside it, revoking the account's root credentials and rotating access keys are steps only you can take.
How we Report Readiness
Once FieldEffectMDRContainment exists we check both halves on a schedule and mark the integration unhealthy if either fails: that the document still denies everything unconditionally, and that the grant is actually in place. The second matters because the read access we already hold lets us see the policy, so a policy created without the grant would otherwise look fine until the moment it was needed.
While the policy does not exist we report nothing, because containment was never asked for.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article