AWS IAM Permission Boundaries: Deep-dive and Practical Conclusions

AWS IAM Permission Boundaries: Deep-dive and Practical Conclusions


ChangeLog:

  • Dec 15 2024: Added a new additional "Section 5".


/0 Intro

Permission Boundaries (PBs) are one of the strategies available in IAM when working to secure our users and workloads in AWS cloud environments.

Let's dive into how they actually work and answer an important question: Can they be universally recommended for all, or even most, customer use cases?

At the end of the article, we'll explore some practical conclusions about PBs based on many years of working with very different AWS environments, customer needs, and IAM security philosophies.


/1 Permission boundaries: The Basics

At a basic level, PBs work with IAM identity-based policies as part of an IAM user or IAM role. In this article, we will use them with IAM Roles, but the same principles apply to IAM Users.

PBs cannot be used without IAM Identity policies because they cannot "grant access" on their own. Let's use an example to illustrate these important concepts.

Suppose an IAM Role has no IAM Identity-based policies associated with it, with the following PB policy set:

{
    "Effect": "Allow",   
    "Action": "*",
    "Resource": "*",
}        

The end result is that the IAM Role has effectively no permissions to do anything at all.

Moving on, PBs provide the "maximum" permissions possible for a given IAM Role. This is a very important point, so let's take an example to illustrate it.

Let's say that an IAM Role has the "Administrator" IAM policy associated with it, and the following PB policy:

 {
    "Effect": "Allow",   
    "Action": "S3:PutObject",
    "Resource": "*",
}        

Here, the final effect is that the IAM Role can only perform S3:PutObject, and absolutely nothing else.

OK, let's flip things around: Our PB policy would have the "Administrator" IAM policy attached to it, and our IAM Role would have the following IAM Identity policy attached to it:

{
    "Effect": "Allow",   
    "Action": "S3:PutObject",
    "Resource": "*",
}        

What would be the end result? In this case, the IAM Role would only be able to perform "S3:PutObject" on all resources.

So whatever we have in the PB policy would effectively be the "maximum" permissions potentially allowed for an IAM role, but which is also influenced by the attached IAM Identity policy.

Before wrapping up the basics, please note that PBs can also be used as part of the AWS Identity Center permission sets, which is a useful feature when dealing with IAM across a large AWS Organization.


/2 Permission boundaries: Advanced Concepts and Usage Paterns

Let's assume that an IAM Role has an "Administrator" IAM Identity policy associated with it, with the following PB policy set:

{
  "Effect": "Deny",
  "Action": "s3:*",
  "Resource": "*"
}         

In this case, the end effect of the IAM Role is that it cannot actually do anything, because the "maximum" it can do is a deny (whether it was on S3 or something else would not really matter). It is effectively similar to a blackhole.

Next, we will cover a unique concept for using PBs. Again, let's take the IAM Role that has an "Administrator" IAM policy attached to it, and the following PB policy:

{
  "Effect": "Allow",   
   "Action": "*",
   "Resource": "*",
},
{
  "Effect": "Deny",
  "Action": "iam:CreateUser", "S3:Replicate*"
  "Resource": "*"
}         

This is interesting. The PB acts as a sort of "explicit deny" list here. So whatever we add to this deny list would also be denied to any principal assuming the IAM Role. But let's not forget: I can achieve the exact same result by adding an SCP (applied to either specific AWS accounts or the entire AWS org, depending on the use case), or by simply adding an IAM Identity policy with a deny list to the IAM Role.


/3 Permission Boundaries: Different Limitations

Let's explore some limitations in regards to PBs:

  • Since PBs must contain all of the maximum potential permissions required by the IAM Role(s) to which they are attached to, this is where the limitations begin: PBs are specifically limited to 6,144 characters -- when a Customer-Managed IAM policy is used as a PB policy. So if we need a PB for a developer or an AWS service that requires a lot of API actions and conditions, we are pretty much out of luck here. But what if we include "*" as part of the PB policy, like "s3:*", to save some policy space? Well, because we do not necessarily want to make S3:* available to ALL AWS services and AWS users that share the same IAM Role and PBs. That would also not be in the spirit of least-privilege, and saving PB policy space in this case would result in a weaker security posture.
  • To follow up on the previous point, in many Organizations, DevOps engineers are sometimes allowed to attach and modify IAM identity policies to the IAM Roles they are using. However, with this flexibility agreement, they are also expected to fine-grain the IAM actions and/or resources of the IAM Identity policies, knowing that the PB still has the final say on the maximum potential permissions of an IAM Role. But what if they do not follow this agreement, and simply append IAM Identity policies with "Administrator" permissions? Then we may actually be granting more IAM actions to resources in the PB policy than we have originally anticipated, from a security perspective of course. Unfortunately, we don't live in a perfect world, and not all Organizations have a security-conscious culture, hence why the usage (or not) of PBs might be directly related to internal processes, security cultures, awarness, and so on.
  • Currently, we can only attach a single PB policy to an IAM Role. I am hoping this changes in the future.


/4 Permission Boundaries: Are there Valid use-cases?

One of the valid use cases I have seen so far for PBs is to use them to restrict the creation of new IAM Roles unless they contain a specific PB, such as the following policy:

   "Effect": "Deny",
   "Action": "iam:CreateRole",
   "Resource": "*"
   "Condition": {
      "StringNotLike": {
         "iam:PermissionsBoundary": "arn:aws:iam::*:policy/Central-permission-boundary>"
      }
   }        

The above policy can be included in an SCP, where the PB policy would allow certain things and/or deny others. This SCP can either be applied to a specific set of AWS accounts individually, or to the entire OU. Not a bad concept overall, and it would work for certain customers who might have a use case for it.

We might ask: are there alternatives to this approach? Most likely yes! I have not tested the following yet, but we could trigger an EventBridge rule based on the creation and modification of both new and existing IAM Roles. Then, a custom lambda will automatically append an IAM policy of our choice, ensuring that the desired IAM identity policy is always present, just as a PB would. We can reinforce parts of this logic with an SCP (as long as the PolicyARN condition is supported for the given IAM actions). If you have implemented this approach and it works properly, please let me know.


/5 Permission Boundaries: IAM Users vs. IAM Roles in IAM Role Trust Policies

Let's say we have an IAM Role called "AdminPipeline" (and therefore, an Administrator IAM policy attached to it), with the following Trust Relationship Policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::111222333444:user/PowerUser",
                    "arn:aws:iam::111222333444:role/PowerAdminRole"
                ]
            },
            "Action": "sts:AssumeRole",
            "Condition": {}
        }
    ]
}        

Pretty simple so far.

Both the IAM User named "PowerUser", and the IAM Role named "PowerAdminRole" have exactly the same effective permissions, as follows:

  • An IAM Identity Policy that allows EC2_Full_Access,
  • and the following Permission Boundary policy set for both:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:*"
            ],
            "Resource": "*"
        }
    ]
}        

In this scenario, the IAM User "PowerUser" will actually be able to assume the "AdminPipeline" Role and become an Admin, even though there is no implicit Allow for sts:AssumeRole anywhere in its Identity Policy or its Permission Boundary. On the other hand, the "PowerAdminRole" IAM Role will not be able to assume the "AdminPipeline" IAM Role, and will therefore always be restricted to EC2:*.

The reason has to do with how IAM users and resource policies both work in different ways. For a detailed "why", see the following documentation.

Finally, it's important to remember that all of this applies only to in-account access; cross-account access behaves differently.

/6 Practical Conclusions and Strategies

Understanding how PBs work is the most important goal here, because only then can we begin to consider them or not as part of our overall enterprise IAM strategy. In my experience, PB policies themselves, as well as the logic surrounding them, should be protected by SCPs.

Here are some important considerations and conclusions, listed by pros and cons, that we should keep in mind when working with PBs in general:

Pros:

  • If PBs work for your specific IAM strategy, environment, and overall requirements, please continue to use them, unless the general limitations and conclusions in this article may have some long-term implications.
  • PBs can be used as part of permission sets in AWS Identity Center (see below for some limitations).
  • We have seen one valid use case for PBs, but another would be to use them "as an explicit deny list" attached to IAM Roles. That is, the PB policy will first contain an Allow list, followed by a list of specific denies. In a sense, and for some scenarios, this could save some valuable SCP space across the AWS Organization.
  • Another potential use case for PBs is when a specific set of permissions needs to be granted and maintained in exactly the same way throughout the lifecycle of specific projects. For example, if we want all of our AWS services (and cloud engineers) within a project to only ever use a specific list of permissions, PBs would ensure that this is always the case.


Cons:

  • The limitations mentioned in Section 3 are important to consider before deploying PBs across an AWS Organization.


  • PB policies are not automatically pushed by Identity Center to child accounts that use the permission sets. These PB policies would need to be pushed to all child accounts in advance (e.g., through automation).
  • Since PB policies must be protected by SCPs, if there are many variations of them for different use cases, the SCPs must be updated accordingly. This might not be necessary with Identity Center's permission-sets though.

  • There are a few ways to "bypass" or "cheat" PBs, especially by indirectly using certain AWS service Roles that do not (or cannot) have PBs associated with them. In such cases, SCPs prove to be more powerful than PBs.


  • We should consider using SCPs (as well as Explicit-deny Identity Policies) instead of PBs where applicable, especially if the PB was intended to "explicitly deny" a list of IAM actions across all IAM principals in the specified Accounts. If the SCPs are about to run out of space, we can apply them at the AWS account level as well as the OU level, nearly doubling the available SCP space (minus those inherited from "above").


  • For many customers, PBs cannot practically live as a permanent and immutable policy throughout their lifecycle. PBs need to be adjusted frequently, either to add more privileges (e.g., for new AWS services) or to remove them. However, because changes to PB policies automatically apply to every IAM role (and user) to which they are associated with, we must constantly check that our PB policy updates do not give too many or too few privileges to running production workloads (or human engineers).
  • PBs do not work well for every use case, even within the same AWS Organization, whether medium or large. This is very similar to why a single IAM policy would not scale well for every workload, environment, data classification, and business requirement in a large Organization. In some cases, the PB policy may have "too many" maximum permissions, and in other cases, "too few" maximum permissions to be used across multiple cloud engineering teams and projects. Here are two examples:


Example 1: We create a PB with a specific list of permissions, one of which is "s3:GetObject" on * resources. Later in the project, a new IAM role is created for a Lambda, which of course is forced to inherit the PB. But what if this particular Lambda does not need to use "S3:GetObject" at all? Suddenly, the PB could be perceived as not enforcing the overall least privileges required for some services or use cases, which is a security flaw in itself. Again, we cannot presume that the IAM Role associated with Lambda is always perfect, otherwise we would not technically need to use PBs in the first place.


Example 2: Same as above, but the logic is reversed. For example, what if my PB has a deny on ec2:CreateVPC for a particular lambda, while another lambda actually needs it?


Thank you for reading and I hope you enjoyed this article! To check out all the other articles, click here.

Ziyad Almbasher

AWS Security Research, Strategy & Leadership | AWS Community Builder_Security

3 个月

Dec 15 2024: Added a new additional "Section 5", which highlights interesting behaviors for IAM Users vs. IAM Roles when it comes to Permission Boundaries and IAM Role trust relationships.

赞
回复

要查看或添加评论,请登录

Ziyad Almbasher的更多文章

社区洞察