AWS iam:PassRole Security and Compliance

What is iam:PassRole?

iam:PassRole is a permission that can be included in AWS IAM policies. It is required if you are creating certain types of resources that need to run as a different role than the the user creating them. A common occurrence of this is if you have Continuous Integration (CI) creating EC2 instances that need to have an Instance Profile, or if you use a Lambda function to create AWS resources. In these cases, you create the resource with one role (probably an administrator type role), but the resource itself has a different role (a "run time" role). In order to give the resource the role it needs, you "pass the role" to it with the PassRole permission.

The problem with iam:PassRole is that it's actually incredibly powerful and can lead to all manner of privilege escalation issues. In that sense then, it is pretty deceptive, and the unwary can fall for simple statements such as "add the iam:PassRole permission to make that work properly" (often seen in answers to questions on the Internet!). Even AWS itself can give error messages which hint that you should add iam:PassRole, but don't give any warnings about what that might imply.

A Common Mistake

A common mistaken pattern is below. That is, it's an IAM policy to allow a CI user to create EC2 instances. Don't use this policy - it opens up some privilege escalation issues which aren't at all obvious by looking at it!

    {
      "Sid": "CiEc2PermissionsInsecureDoNotUse",
      "Effect": "Allow",
      "Action": [
        "iam:PassRole",
        "ec2:AttachVolume",
        "ec2:CreateSnapshot",
        "ec2:CreateTags",
        "ec2:DeleteTags",
        "ec2:CreateVolume",
        "ec2:DeleteSnapshot",
        "ec2:DeleteVolume",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeRegions",
        "ec2:DescribeSnapshots",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVolumes",
        "ec2:DetachVolume",
        "ec2:RunInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "ec2:DescribeImages",
        "ec2:DescribeInstanceAttribute",
        "ec2:DescribeInstanceCreditSpecifications"
      ],
      "Resource": "*"
    },

This looks safe enough - it's only applied to the CI user/role, and it only allows a relatively small set of EC2 privileges. We know CI can create instances, because that's what we want it to do, and these permissions don't let it (say) make changes to DNS or whatever.

However, this policy will throw up Critical issues with Kics and other compliance tools. They will complain that you shouldn't use Resource: "*" with iam:PassRole. As we said, don't use this policy!

The reason this is a problem is because Resource: "*" allows the CI user to create an instance with far greater permissions than we intend, and far greater permissions than the CI user has. All the CI user would need to do is create an EC2 instance with the AdministratorAccess role and they have pretty much full access to all of AWS.

A Better Solution

The above policy can be improved considerably as follows:

    {
      "Sid": "CiEc2RolePermissions",
      "Effect": "Allow",
      "Action": [
        "iam:PassRole"
      ],
      "Resource": [
        "arn:aws:iam::123456123456:role/first-server-role",
        "arn:aws:iam::123456123456:role/second-server-role"
      ],
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": [
              "ec2.amazonaws.com"
          ]
        }
      }
    },
    {
      "Sid": "CiEc2Permissions",
      "Effect": "Allow",
      "Action": [
        "ec2:AttachVolume",
        "ec2:CreateSnapshot",
        "ec2:CreateTags",
        "ec2:DeleteTags",
        "ec2:CreateVolume",
        "ec2:DeleteSnapshot",
        "ec2:DeleteVolume",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeRegions",
        "ec2:DescribeSnapshots",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVolumes",
        "ec2:DetachVolume",
        "ec2:RunInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances",
        "ec2:DescribeImages",
        "ec2:DescribeInstanceAttribute",
        "ec2:DescribeInstanceCreditSpecifications"
      ],
      "Resource": "*"
    },

Here we've broken the iam:PassRole out to its own stanza, and have specifically tied down the roles that can be passed to the EC2 instances with this policy. It means our CI user can only create instances with the roles we have specifically allowed, so that means we can control what those instances can do. Most importantly though, it is now not possible to create an instance with more permissions than the user creating it.

We could go further here and limit the regions that instances can be created in, or even the size of those instances. These are both useful additions because they limit the financial exposure of any misuse, although less likely to cause Compliance issues as they're not privilege escalation or security per-se.

Conclusions

iam:PassRole is deceptively simple, yet is actually dangerously powerful. We've seen a common use-case for iam:PassRole, and how it can leave an opportunity for significant privilege escalation security issues. We've also seen a relatively modest improvement which dramatically reduces the security risks, whilst still allowing all the access required.

If you need help getting cleaned up for Compliance or just in figuring out how to make AWS work for you, please contact us - we can help you figure out what you need and make it work for you.