Identity and Access Management (IAM) in AWS is a crucial component for ensuring that the right individuals have the appropriate access to resources. With the complexity of cloud architectures and the need for fine-grained access control, AWS introduced Condition Context Keys. This feature allows for enhanced security by setting conditions under which IAM policies grant or deny permissions.
IAM Conditions are a powerful tool in the AWS security arsenal. They allow administrators to define and enforce conditional, attribute-based access control for AWS resources. Imagine a scenario where you'd want to grant temporary access to a developer to debug a production issue, but only if they're making requests from the corporate network. IAM Conditions make such scenarios possible.
Using condition context keys is a common practice for limiting the methods a principal can use to access resources. For example, an infrastructure team may provision resources for a development team using CloudFormation. The infrastructure team usually has elevated permissions to create whatever resources are requested, such as ECS Services, Lambda functions, or DynamoDB tables.
You may find yourself in the situation where you want to allow the development team to delete the resources, but not modify or create new resources. This is often needed in lower sandbox or ephemeral environments.
This gets a little tricky, because let’s say you don’t know in advance which resources CloudFormation will be managing. How can you create a policy that allows a team to delete a stack, and any resources that the stack manages?
The Solution? Using Global Condition Contexts
When a request is made to the AWS API, a request context is included. Inside the request context is the service name that is performing the action. Knowing this, we can use the aws:CalledVia key to allow access to resources, but only when called on behalf of CloudFormation. We then limit access to CloudFormation to control which stacks can be deleted.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowViaCloudFormation",
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:CalledViaFirst": "cloudformation.amazonaws.com"
}
}
},
{
"Sid": "CloudFormationAccessForSpecificStacks",
"Effect": "Allow",
"Action": "cloudformation:DeleteStack",
"Resource": "arn:aws:cloudformation:<REGION>:<ACCOUNT_NUM>:stack/<STACK_NAME>/*"
},
{
"Sid": "ExplicitDenyCreateServiceLinkedRole",
"Effect": "Deny",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "*"
},
{
"Sid": "DenyCertainActionsEvenInCloudFormation",
"Effect": "Deny",
"Action": [
"cloudtrail:Stop*",
"cloudtrail:Delete*",
"iam:PassRole"
],
"Resource": "*"
}
]
}
In this policy we deny all actions that are not called via CloudFormation. We allow all actions on all resources when called via CloudFormation, but we then limit access to CloudFormation allowing only the DeleteStack action on one particular stack.
AWS will inspect the request context. When a principal makes an API request to an AWS service, that service may make requests on the principal’s behalf. The services are stored in an ordered service chain . aws:CalledVia ensures that if there are one or more services in the service chain, and if one of them is CloudFormation, the request will be denied.
To get even more granular, we can use aws:CalledViaFirst which ensures that CloudFormation is the first service in the service chain.
In summary, any requests that are made outside of CloudFormation will be implicitly denied. All actions that include CloudFormation as the first service in the service chain are allowed.
The next statement in our policy ensures that, even though CloudFormation can do just about anything on behalf of the principal, when the user calls the CloudFormation API they are limited to stack deletions of a particular stack.
We explicitly deny the creation of Service Linked Roles. Service Linked Roles are roles that are assigned to an AWS service, and the service manages policy attachments to the role. Since we’re already limiting AWS API access to Cloudformation stack deletions, this isn’t strictly necessary. However, this statement prevents the AWS console from generating a security warning.
We explicitly deny CloudTrail Stop and Delete actions, even when called via CloudFormation.
When we create a CloudFormation stack with a service role , IAM PassRole access is required so that CloudFormation can assume a role to perform create or update actions. IAM PassRole is needed anytime you are passing a new or different role to a service. However, when deleting a stack, you are not passing a new or different role to the service. When deleting a stack, the CloudFormation service role is already associated, so it does not need to be passed.
Therefore, we can deny IAM PassRole actions because they are not required for stack deletion or deletion of resources and their associated roles.
We can use the Policy Simulator to verify the intended behavior of our policy.
As shown above, the policy is unable to make changes to AWS Lambda directly.
This second screenshot shows that the user is allowed to delete a stack, but only when the stack resource satisfies the name specified in the policy.
In the real world, your stack may create buckets, and those buckets may contain objects. The policy we created would not allow deletion of buckets created by CloudFormation if they contain objects. There are multiple workarounds to this issue. One option is to update the policy to allow direct access to the buckets created by the stack, and to have an external script that deletes objects before the stack deletion.
You want to follow the principle of least-privilege, but when working with infrastructure as code you often need to create composable templates and automation.
It is common for different teams or roles within an engineering organization to have different levels of access to AWS resources. In this article I showed how global condition contexts, specifically aws:CalledVia, can enable a team to have full access to delete their own resources through CloudFormation, even when the resources that the stack manages are not known in advance.
At StratusGrid , we architect developer platforms using advanced AWS capabilities to ensure both security and developer productivity are maximized. Contact us for a free consultation today!