# Thinking about IAM It seems like an unsolved problem: How do you provide effective permissions that enable your administrators, operators, and developers to do amazing things in AWS without giving them the keys to the kingdom? There are all sorts of excellent videos on solving pieces of this, but when you go to actually implement them, you're likely to run into areas where it doesn't quite work like you'd want. Regardless, I'm going to make an attempt, and bring you along for the ride. ## The Goals I want a set of groups, assignable via SAML (i.e. a user can only be signed into one group at a time), that empower users but also keep things secure. I want to delegate responsibility for designing and managing IAM roles for EC2, lambda, and other services to the teams that are working with them. But I want these roles to be limited. Additionally, I'm in a highly regulated environment where only certain AWS services are approved, so I want to deny the rest of them. Further, I like it when users can troubleshoot their own issues, so every role will have access to the AWS console to _look at_ how things are set up. This is how they learn. There is some security to be gained by keeping settings obscured (yes, security through obscurity does have some value. Go ahead and flame me in the comments), but the benefit is drowned by the loss in productivity. Lastly, if a user doesn't have a need to access the _data_, then I don't want them to be able to access it. ## Goals, the Shorter Version I want best people to be able to do their best, and not be held back by unnecessary hoops. At the same time, I want my worst people to be protected from doing something devastating. I especially don't want them to be able to ruin somebody else's day. Because this isn't a perfect world, there will be hoops. Whenever possible, I want to avoid barriers for the sake of barriers, but sometimes I'm going to put up barriers just to make sure people really want to do what they are trying to do. ## Questions Here are the main questions that we need to answer: * What services are allowed by the organization? * What are the main groups we're going to need? * For those groups ## Some quick rules * No user should be able to assign a role that has more access than they do. (i.e. if the user has `iam:*` permissions, they may as well just have admin). * Tags are our friend. If we can reuse something by using a tag, that's great for us. ("Don't Repeat Yourself" aka "DRY" is a good thing) ## Methodology Everything I create is going to begin `FIAM-*` (for "Fred's IAM"). First, I need a role. I'm going to start with an imagined developer-type. Specifics will obviously vary by what exactly your people need to do, but mostly I'm interested in creating a skeleton of policies that can be refined as we find more things that developers need to do. I have an additional goal of keeping things segmented and reusable. There are some people who would be upset by the number of policies I'm creating (and allowing to be created!), but I see no problem with having as many policies as are needed, and even many more, provided they meet my criteria listed below (and I expect I'll have config rules to enforce them in case somebody does manage to slip something by). To create this role through the console, choose 'Create Role', and use the "Another AWS Account" option. For the account number, enter the same account. In production, this role would be assumed via SAML instead. It should be easy to modify later. Then I created a user `testuser` to actually login and test things. This user has a simple inline policy allowing it to assumerole to any FIAM-* role: ``` { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::012345678901:role/FIAM-*" } ] } ``` I'm still a CLI amateur, so most tests will be run from the web UI. (If you haven't used an account like this before, it's easy! Sign into testuser, who has basically zero permissions. From that account, you can use the dropdown to "Switch role" to specify whichever FIAM-* role you want to test). ## Some Preliminaries ### One quick big win All groups will be granted the built-in `ViewOnly` role. This lets them use the full AWS console to view configuration, and will solve a lot of headaches that can be caused by not being able to `List*` or `Describe*` something. ### Deny Regions We only operate in certain regions. AWS has helpful documentation about [controlling access to regions](https://aws.amazon.com/blogs/security/easier-way-to-control-access-to-aws-regions-using-iam-policies/), but we expand on that here by denying all. This is not well tested, so we may have to go back. This _should_ allow any non-region specific activity, and _should_ (as I understand it) deny any region-specific activity in the wrong region. TODO: Try to exploit this, especially through the command-line. Policy: FIAM-RestrictRegions ``` { "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Action": "*", "Resource": "*", "Condition": { "StringNotEquals": { "aws:RequestedRegion": [ "us-east-1", "us-east-2", "us-west-1" ] } } } ] } ``` ### Allowed Services In order to allow specific services with an attachable policy, it's easiest to think in double negatives. That is, we're going to _deny_ all of the actions that aren't an allowed service. Of note, this negates some of the benefit of the `ViewOnly` role. (Suggestion: Use an existing AWS policy such as `ReadOnly` for a good list of the AWS services and prune down to those you want) Note that this is not granting any permissions to these services, it is simply denying anything outside of these services. Policy: FIAM-RestrictServices ``` { "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "NotAction": [ "acm:*", "autoscaling:*", "cloudformation:*", "cloudfront:*", "cloudtrail:*", "cloudwatch:*", "codebuild:*", "codecommit:*", "codedeploy:*", "codepipeline:*", "codestar:*", "cognito-idp:*", "cognito-identity:*", "cognito-sync:*", "config:*", "directconnect:*", "dynamodb:*", "ec2:*", "events:*", "glacier:*", "iam:*", "kms:*", "lambda:*", "logs:*", "organizations:*", "rds:*", "route53:*", "route53domains:*", "s3:*", "ses:*", "sns:*", "sqs:*", "swf:*", "trustedadvisor:*", "waf:*", "waf-regional:*" ], "Resource": "*" } ] } ``` ### So far, so good I now have an IAM group with policies that appropriately restrict what's allowed. I can attach a built in role such as `NetworkAdministrator`, `AmazonVPCFullAccess`, or even `AdministratorAccess`, and the user will nonetheless be restricted by our policies. Now things are going to get more complicated. ### Reusable Policy #1 - IAM I _like_ users to be able to create and assign IAM roles for their EC2 instances, lambda functions, and other services. What I don't like is that they can create a role that has more access than they have themselves. And I certainly don't want them to be able to edit roles outside of their area. To solve these issues, we're going to use two additional tools. * Group Tags - We will tag the group with a variable `IAMPrefix`. We will require that any policy or role created, modified, or assigned by this group must have a name that starts with that prefix. * Permissions Boundary - We will require that any role created or modified by this group has this assigned permissions boundary. We will create this boundary in a moment. #### Assigning Tags For flexibilities sake, I assigned two tags: `IAM:NamePrefix` set to `FIAM-DEV-`: Required prefix for all roles and policies. `IAM:PermissionsBoundary` set to `FIAM-Boundary-Developer`: This will be the name of the permissions boundary required for all roles. By setting this separately, I can reuse permissions boundaries between roles. IMPORTANT NOTE: Do NOT name the PermissionsBoundary with the NamePrefix. To do so would allow the user to edit their own permissions boundary. #### The IAM Creation Policy Now I want a reusable policy that takes advantage of those tags, which delegates permissions to create roles to the developers. We want hands off. The good developers will assign IAM policies that are restrictive. And most likely, they'll eventually hit a frustrating bug, update the policy to grant `Action=*` permissions, and never look back. Pushing these developers to go back is just part of it. But it can happen after. IMO, there's no reason they shouldn't be allowed to do `Action=*` while they're figuring things out. And that's why we have permissions boundaries. For now, we need to allow developers to create, destroy, and modify IAM roles and policies subject to the contraints: * They must be named starting with their prefix. * They must have the permissions boundary assigned. The actual assigning IAM roles to resources is going to have to wait for actual policies, as decisions on ec2/lambda/etc access may be role dependent. Wishlist: * We should probably restrict the trust policies. Creating a role could provide a path into the account from any other IAM user. That's bad. Policy: FIAM-IAM-Restricted-Role-Management ``` { "Version" : "2012-10-17", "Statement" : [ { "Sid": "SetPermissionsBoundary", "Effect": "Allow", "Action": [ "iam:CreateRole", "iam:AttachRolePolicy", "iam:DetachRolePolicy" ], "Resource": "arn:aws:iam::082012130604:role/${aws:PrincipalTag/IAM:NamePrefix}*", "Condition": { "StringEquals": { "iam:PermissionsBoundary": "arn:aws:iam::082012130604:policy/${aws:PrincipalTag/IAM:PermissionsBoundary}" } } }, { "Effect": "Allow", "Action": "iam:DeleteRole", "Resource": "arn:aws:iam::082012130604:role/${aws:PrincipalTag/IAM:NamePrefix}*" }, { "Sid": "CreateAndEditPermissionsPolicy", "Effect": "Allow", "Action": [ "iam:CreatePolicy", "iam:CreatePolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion" ], "Resource": "arn:aws:iam::082012130604:policy/${aws:PrincipalTag/IAM:NamePrefix}**" }, { "Sid": "MiscellaneousRequiredIAM", "Effect": "Allow", "Action": [ "iam:Get*", "iam:List*", "iam:GenerateServiceLastAccessedDetails" ], "Resource": "*" } ] } ``` There's also a policy to grant EC2 Specific IAM Permissions Policy: FIAM-IAM-Restricted-Role-Management-EC2Specific ``` { "Version" : "2012-10-17", "Statement" : [ { "Sid": "InstanceProfiles", "Effect": "Allow", "Action": [ "iam:CreateInstanceProfile", "iam:DeleteInstanceProfile", "iam:AddRoleToInstanceProfile", "iam:RemoveRoleFromInstanceProfile" ], "Resource": "arn:aws:iam::082012130604:instance-profile/${aws:PrincipalTag/IAM:NamePrefix}*" } ] } ``` NEXT STEPS: Add passrole permissions Repeat for lambda? Repeat for others.