|
@@ -0,0 +1,351 @@
|
|
|
+# 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.
|
|
|
+
|