AWS IAM Managed Policies — Use them wisely, or do not use them at all

Artiom Levinton
XM Cyber
Published in
5 min readDec 7, 2020

Convenience vs security — the eternal dilemma…

As a part of SecOps or DevOps team, you probably face this dilemma every day >> How to grant a certain user or application precisely the number of permissions they need to run. No more, no less.

Principal entities are people or applications that are authenticated
using an IAM identity (user or role).

The access management portion of AWS Identity and Access Management (IAM) helps you define what these principal entity is allowed to do in an account. Access management process is often referred to as an authorization. You manage access in AWS by creating policies and attaching them to IAM identities (users, groups of users, or roles) or AWS resources. A policy is an object in AWS that, when associated with an identity or resource, defines their permissions. AWS evaluates these policies when a principal uses an IAM entity (user or role) to make a request. Permissions in the policies determine whether the request is allowed or denied.

In general, AWS has two types of identity-based policies — Managed by AWS and Managed by User

AWS offers built-in managed policies which can be attached to Roles or Users, granting those entities the permission defined by the policy.

Here are some basic examples of how such policies are used:

Let’s say we want to grant access for a specific EC2 instance to a specific S3 bucket

We will have to attach a role to the instance with the following custom (user managed) policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::your-bucket-name-goes-here/*"
}
]
}

In this example, by attaching a role containing this policy to this specific ec2 instance, we will grant that instance access to this specific bucket. This is a simple but strict policy which permits the required action and nothing more.

But let’s say you want to implement a more complicated policy. For example you want to grant a certain IAM User permission to maintain the EC2 fleet in your AWS account. This user should be able to perform only basic operations on each instance, for example:

- Launch instance - Start/Stop instance- Modify instance type- Modify EBS properties- Create EC2 Tags- Create snapshots- Terminate instance

In this case, each action should be granted separately (in one policy). In many cases a more convenient way would involve granting full EC2 access to avoid “access denied” errors while performing simple maintenance tasks. For this, you may use the AWS managed policy called “AmazonEC2FullAccess”.

Example of AWS managed policy

This policy contains several wildcard actions, the main one is :

"Statement": [
{
"Action": "ec2:*",
"Effect": "Allow",
"Resource": "*"
}
]

This wildcard action means that any operation starting with “ec2” will be allowed; you may find the list of actions here -

https://docs.aws.amazon.com/AWSEC2/latest/APIReference/OperationList-query-ec2.html

This example shows how easily we can grant full access over EC2 to certain user so he can perform the required tasks, yet let us take a closer look and try to understand the security risk in this convenient but dangerous use of managed policy.

While giving allow permission to wildcard actions like ec2:* you are also granting some extremely dangerous actions like :

ec2:AssociateIamInstanceProfile

In this case the user can attach (almost) any role, including administrative roles to any EC2 instance! They can also access any EBS disk and add any public key to the authorized_keys file. User can have access to instances with admin privileges to IAM service and even create a user with an administrator policy.

The result? — from user that should have basic privileges to EC2 instances ends up with Super User who can access any AWS service on this account!

The best practice in this case? Building a custom policy for every user/role and never using AWS Managed Policies that are often too permissive.

It may take some time to create those policies — but it is worth the effort, as this will help avoid severe and completely unanticipated security breaches.

A common problem with creating a custom policy is knowing in advance which action will be in use. Yet there is a simple solution for this : Give the user/role minimum permission and gradually add permission by request.

In the new management console graphical user interface the permission error text is encoded. While this can be frustrating it can be solved easily. Just run the following command :

# aws sts decode-authorization-message — encoded-message “Your encoded message goes here”

Here is an error message which can be easily decoded by the command:

Example of authorization failure message

The decoded message will look like this:

“DecodedMessage”: “{\”allowed\”:false,\”explicitDeny\”:false,\”matchedStatements\”:{\”items\”:[]},\”failures\”:{\”items\”:[]},\”context\”:{\”principal\”:{\”id\”:\”AROAYMDKORDTCPU2UUFKE:hidden-info@xmcyber.com\”,\”arn\”:\”arn:aws:sts::hidden-info:assumed-role/AWSReservedSSO_Some-Team_5a6a4b964d824ffd/hidden-info@xmcyber.com\”},\”action\”:\”ec2:RebootInstances\”,\”resource\”:\”arn:aws:ec2:sa-east-1:hidden-info:instance/i-0f0cbd365xxxxxxxx\”,\”conditions\”:{\”items\”:[{\”key\”:\”ec2:MetadataHttpPutResponseHopLimit\”,\”values\”:{\”items\”:[{\”value\”:\”1\”}]}},{\”key\”:\”ec2:InstanceMarketType\”,\”values\”:{\”items\”:[{\”value\”:\”on-demand\”}]}},{\”key\”:\”aws:Account\”,\”values\”:{\”items\”:[{\”value\”:\”hidden-info\”}]}},{\”key\”:\”ec2:AvailabilityZone\”,\”values\”:{\”items\”:[{\”value\”:\”sa-east-1c\”}]}},{\”key\”:\”ec2:ResourceTag/Name\”,\”values\”:{\”items\”:[{\”value\”:\”hidden-info\”}]}},{\”key\”:\”ec2:InstanceType\”,\”values\”:{\”items\”:[{\”value\”:\”m5.2xlarge\”}]}},{\”key\”:\”hidden-info:Phase\”,\”values\”:{\”items\”:[{\”value\”:\”1\”}]}},{\”key\”:\”aws:Region\”,\”values\”:{\”items\”:[{\”value\”:\”sa-east-1\”}]}},{\”key\”:\”aws:Service\”,\”values\”:{\”items\”:[{\”value\”:\”ec2\”}]}},{\”key\”:\”ec2:MetadataHttpTokens\”,\”values\”:{\”items\”:[{\”value\”:\”optional\”}]}},{\”key\”:\”aws:Type\”,\”values\”:{\”items\”:[{\”value\”:\”instance\”}]}},{\”key\”:\”ec2:Tenancy\”,\”values\”:{\”items\”:[{\”value\”:\”default\”}]}},{\”key\”:\”hidden-info:Name\”,\”values\”:{\”items\”:[{\”value\”:\”hidden-info\”}]}},{\”key\”:\”hidden-info:Customer\”,\”values\”:{\”items\”:[{\”value\”:\”Energisa\”}]}},{\”key\”:\”ec2:ResourceTag/Phase\”,\”values\”:{\”items\”:[{\”value\”:\”1\”}]}},{\”key\”:\”ec2:ResourceTag/ServerType\”,\”values\”:{\”items\”:[{\”value\”:\”North\”}]}},{\”key\”:\”aws:Resource\”,\”values\”:{\”items\”:[{\”value\”:\”instance/i-0f0cbd36xxxxxxx\”}]}},{\”key\”:\”ec2:ebsOptimized\”,\”values\”:{\”items\”:[{\”value\”:\”true\”}]}},{\”key\”:\”ec2:RootDeviceType\”,\”values\”:{\”items\”:[{\”value\”:\”ebs\”}]}},{\”key\”:\”ec2:InstanceProfile\”,\”values\”:{\”items\”:[{\”value\”:\”arn:aws:iam::hidden-info:instance-profile/automation-ec2-to-s3-access\”}]}},{\”key\”:\”ec2:MetadataHttpEndpoint\”,\”values\”:{\”items\”:[{\”value\”:\”enabled\”}]}},{\”key\”:\”hidden-info:Stage\”,\”values\”:{\”items\”:[{\”value\”:\”Customer_success\”}]}},{\”key\”:\”ec2:InstanceID\”,\”values\”:{\”items\”:[{\”value\”:\”i-0f0cbd365dxxxxxx\”}]}},{\”key\”:\”ec2:ResourceTag/Stage\”,\”values\”:{\”items\”:[{\”value\”:\”Customer_success\”}]}},{\”key\”:\”ec2:ResourceTag/Customer\”,\”values\”:{\”items\”:[{\”value\”:\”Energisa\”}]}},{\”key\”:\”ec2:Region\”,\”values\”:{\”items\”:[{\”value\”:\”sa-east-1\”}]}},{\”key\”:\”hidden-info:ServerType\”,\”values\”:{\”items\”:[{\”value\”:\”North\”}]}},{\”key\”:\”aws:ARN\”,\”values\”:{\”items\”:[{\”value\”:\”arn:aws:ec2:sa-east-1:hidden-info:instance/i-0f0cbd365xxxxxxx\”}]}}]}}}”

You may see that the denied action was :

“action”:”ec2:RebootInstances

Just add this action to the custom policy and the problem is solved

Summary

Using AWS Managed Policies can speed things up in the short run by granting wide-open permissions to users or other resources. However, in most cases, using these AWS managed policies may introduce potential security risks.

Another disadvantage of using policies not created by the user, is that AWS often changes the content of these built-in policies without any announcement. This may cause service disruption by revoking a required pre-defined permission.

If you are planning a new security architecture from scratch, or reviewing your current AWS account security configuration I recommend reading this “Security best practices in IAM” guide at least once. Doing so will help you get a better perspective and understanding about working with IAM services.

Next Step

Now that we understand the value and importance of using user managed policies there several more restriction that could be added to these policies to tighten the access even more.

Use condition based restrictions

For example deny access to specific Region:

{
"Sid": "DenyIrelandRegion",
"Effect": "Deny",
"Action": "ec2:*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "eu-west-1"
}
}
}

By tagging your resources correctly on creation, you can deny access to production/sensitive environments:

{
"Sid": "DenyProdEnvAccess",
"Effect": "Deny",
"Action": "ec2:*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/Environment": "Prod"
}
}
}

You may find a more detailed list of conditions here, to help securing your resources.

--

--