6

I'm currently developing and launching a relatively simple cloudformation stack. Just some simple RDS stuff, triggered through and external CI+CD service.

However, my current cycle for development is incredibly inefficient, purely because I don't understand what the IAM permissions need to be for the Continuous Integration IAM group I set up.

I'll try runing the deployment of the Cloudformation template, only to get some error setting something up and start a rollback. The rollback will then fail, because it requires a different permission to remove what it's managed to create so far. I add the two new permissions I've discovered, delete the stack because it's in the ROLLBACK_FAILED state, and try again.

I can just wildcard all the permissions for all the services I need, but that can't be best practice when handing some AWS credentials to an external service.

Because of this. Is there a way to know what IAM Permissions I specifically need to set up based on a cloudformation template? Alternatively, is there some list of IAM permissions expected for each Cloudformation Resource? Am I being too pedantic about trying to limit as many permissions as possible? Or am I forever doomed to this trial of IAM permission tuning?

SCB
  • 161
  • 6
  • 1
    This would be a great feature in CloudFormation. I have have also burned many hours on the same cycle. Fingers crossed!! – CarlR Jan 29 '18 at 15:38
  • Did you ever find a proper answer? I spent several hours today compiling a list of permissions by re-running a failing Cloudformation deploy. – SimpleJ Apr 09 '20 at 05:07
  • @SimpleJ the answers below provide some hints as how to figure it out yourself, but so far nothing as simple as an automated list. – SCB Apr 11 '20 at 02:44

3 Answers3

2

There is a work around for this which I love. It is:

  1. Login with a specific test user
  2. Create the resources manually from console using this user
  3. Go to CloudTrail and watch the events history and observe the values of `eventName.

This eventName corresponds exactly to the API call names from boto3 and to Action in IAM policies for the related permission. So you would know all the steps that need to be done in order to create a specific type of resource.

Next you only need to remember and understand the CloudFormation actions itself and that's it.

1

I've had the exact same question myself but couldn't find the answer I wanted.

Instead, I've drawn a diagram which contains all the relevant parts, thought about each part in terms of:

- What does it need to do?
- Which resources does it touch/manage?

Then, for each part of the diagram I've compiled a list of IAM access I thought would be required and wrote a specific policy which contains everything which would be needed.

Then I replayed CloudFormation stack creation, and I still missed some permissions but mostly was already covered by the policy I created.

I hope my answer helps you.

Itai Ganot
  • 10,424
  • 27
  • 88
  • 143
0

This is a challenging problem as well as an important one to get right, particularly in your case where credentials are being provided to a third party.

It's not really possible/practical to fully automate this because the permissions required can be quite broad, especially when you consider that changes to your cloudformation template can easily result in more permissions being required, depending on what you happen to change. Often, updates to a stack may also trigger resource replacement, which essentially require all the permissions for deleting, creating, and updating. Utilizing a feature in one AWS service may require permissions of several other services, and so on.

Alternatively, is there some list of IAM permissions expected for each Cloudformation Resource

Because CloudFormation can do basically anything, this would quickly amount to wildcard permissions for the service/resource. Further, due to the countless interactions that are used between services, even within a single service offering, it's increasingly difficult to predict what permissions are needed. Things also change a lot depending on your environment. For example, you may or may not have already created a service-linked role required for managing services like RDS or ECS -- or you may have encryption enabled, requiring potential access to KMS or customer-managed keys or CloudHSM.

So, in short, the answer to your specific question is that it's not possible/practical to do this. As others have stated, you may be able to perform a set of tasks then observe which IAM actions were used, but this may be incomplete if you don't exhaust all the cases CFN might run into.

However, depending on your goals, there are several strategies you might use to strike a balance between ease of configuration and security:

Only allow actions via CloudFormation.

Using the aws:CalledViaFirst global condition key, you can make sure that permissions granted by the policy are only in effect when they are taken via CloudFormation. In this case, the principal would not be able to take actions via the API directly, but only allows CloudFormation to perform those actions on behalf of the principal.

If you do this in combination with limiting which CloudFormation stack(s) the principal has permissions to Create/Delete/Update, that might make for a fairly well-defined permission boundary or at least a good place to start.

For example, part of your policy may be the following:

{ 
   "Version":"2012-10-17",
   "Statement":[ 
      { 
         "Sid":"AllowDDBAndKMSActionsViaCFN",
         "Effect":"Allow",
         "Action":[ 
            "dynamodb:GetItem",
            "dynamodb:BatchGetItem",
            "dynamodb:PutItem",
            "dynamodb:UpdateItem",
            "dynamodb:DeleteItem",
            "kms:Encrypt",
            "kms:Decrypt",
            "kms:ReEncrypt*",
            "kms:GenerateDataKey",
            "kms:DescribeKey"
         ],
         "Resource":[
            "arn:aws:dynamodb:region:111122223333:table",
            "arn:aws:kms:region:111122223333:key/example"
         ],
         "Condition":{ 
            "StringEquals":{ 
               "aws:CalledViaFirst":[ 
                  "cloudformation.amazonaws.com"
               ]
            }
         }
      }
   ]
}

This way, the principal can't directly access DynamoDB or your KMS keys, for example, but a CloudFormation change launched requiring these permissions would still be permitted.

Even with this condition, because CloudFormation can manage just about anything, including IAM, you obviously would still want to be careful about which actions you grant, even via CFN.

Scope resources appropriately

This one is pretty basic. If you properly scope the Resource: keys for your policies, this may help you reduce the blast radius of what this principal can manage. Additionally, you may consider condition keys for similar purpose. For example, you may have a naming convention that will allow you to specify resources as stack-name* or use a condition key like a resource tag.

Define which actions you don't want permitted

Sometimes it's easier to define what you don't want an IAM role to be able to do, rather than defining what you want to allow. One obvious way is with "deny" policies, but a more powerful tool can be to use NotAction to define this with more flexibility.

For example, you might want to allow every action except for certain 'dangerous' actions.

Statement": [
  {
    "Effect": "Allow",
    "NotAction": [
      "iam:*",
      "organizations:*",
      "account:*"
    ],
    "Resource": "*"
  }, ...

This will also give you the flexibility to use an additional statement to allow certain actions that we did not allow in the previous policy:

{
  "Effect": "Allow",
  "Action": [
    "iam:CreateServiceLinkedRole",
    "iam:DeleteServiceLinkedRole",
    "iam:ListRoles",
    "account:ListRegions"
  ],
  "Resource": "*"
}

This would have not been possible if we had used Deny for iam:* in the previous policy. Using NotAction with Allow, we have more flexibility.

Restrict direct actions by source IP

Sometimes, your CICD will still need permissions for direct actions. Another layer of security can be added for direct actions by using the aws:SourceIp condition key. If you self-host your CICD system or if your provider publishes its public IP addresses, you can use this condition to prevent your credentials being used from other systems.

Keep in mind that, when AWS services call other services on your behalf (like with CFN, but with many others), the SourceIp key does not exist, so formulate your policies accordingly.

Example:

    "Statement": [
        {
            "Sid": "PrincipalPutObjectIfIpAddress",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-service-bucket/*",
            "Condition": {
                "Bool": {"aws:ViaAWSService": "false"},
                "IpAddress": {"aws:SourceIp": "123.45.167.89"}
            }
        }

Use short-lived credentials

In some CICD systems, it may be possible to inject temporary AWS credentials that are obtained via an AssumeRole call -- or you may automate this yourself using an API, for example. This way, if credentials are exfiltrated in a one-off circumstance, they won't be valid forever.

In summary, using techniques like these may help you quickly define policies that strike a good balance of being able to do most/all of what you need without being too permissive while mitigating risks that stem from the credentials being compromised or potential abuse/accidents.

sytech
  • 111
  • 4