Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS::KMS::KeyPolicy is desired #322

Open
rix0rrr opened this issue Jan 2, 2020 · 14 comments
Open

AWS::KMS::KeyPolicy is desired #322

rix0rrr opened this issue Jan 2, 2020 · 14 comments
Labels
enhancement New feature or request security identity compliance IAM, Cognito, Secrets Manager, GuardDuty, etc.

Comments

@rix0rrr
Copy link

rix0rrr commented Jan 2, 2020

AWS::KMS::Key supports configuring a resource policy as a property on the object, but not as its own resource. Given that by default, keys must have a statement both in the key resource policy as well as on the IAM identity policy to allow an operation such as iam:Encrypt, this makes it impossible to create a Key with restrictive permissions in Stack 1, and a Role in Stack 2 that can use that key.

This is because at the time of Key creation (Stack 1), when we're setting the key policy, we won't know the name of the Role yet that will be created in Stack 2, so we can't properly reference it.

What we would like to be able to write is this:

==== STACK 1 ============
Resources:
    MyKey:
        Type: AWS::KMS::Key
        Properties: 
            Policy: ... # <-- can't refer to MyRole here!
Outputs:
    KeyArn: 
        Value: { Fn::GetAtt: [MyKey, Arn] }
        Export: SharedKeyArn

==== STACK 2 ============
Resources:
    MyRole: 
        Type: AWS::IAM::Role
        Properties: # ...
    RolePolicy:
        Type: AWS::IAM::Policy
        Properties:
            PolicyDocument:
                Version: 2012-10-17
                Statement:
                - Effect: Allow
                    Action: 'kms:Decrypt'
                    Resource: { Fn::ImportValue: SharedKeyArn }
            Roles:
                - {Ref: MyRole}
    ChangeKeyPolicy:  # <-- MUST encode this operation in Stack 2
        Type: AWS::KMS::KeyPolicy
        Properties:
            KeyArn: { Fn::ImportValue: SharedKeyArn }
            PolicyDocument:
                Version: 2012-10-17
                Statement:
                - Effect: Allow
                  Action: 'kms:Decrypt'
                  Principal: { Fn::GetAtt: [MyRole, Arn] }

Compare: AWS::S3::BucketPolicy, AWS::SQS::QueuePolicy, etc, which encapsulate the operation of adding to a resource's policy, so that this operation can be done in a cross-stack fashion.

5. Helpful links

A grant must be present in both the key policy and the identity's IAM policy. Source:

https://docs.aws.amazon.com/kms/latest/developerguide/control-access-overview.html#managing-access

IAM policies by themselves are not sufficient to allow access to a CMK.

6. Category (required) - Will help with tagging and be easier to find by other users to +1

  1. Security (IAM, KMS...)
@rix0rrr
Copy link
Author

rix0rrr commented Jan 2, 2020

The suggested workaround for this at the moment is to set up the key policy to allow blanket permissions to anyone in the account, but that seems less than desirable for something as sensitive as encryption keys:

{
  "Sid": "Enable IAM User Permissions",
  "Effect": "Allow",
  "Principal": {"AWS": "arn:aws:iam::111122223333:root"},
  "Action": "kms:*",
  "Resource": "*"
}

https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html

KMS team thoughtfully built in the restriction that users need to be very explicit about access permissions to sensitive objects (changing behavior from every other AWS service on purpose), only for their efforts to be undone because the restrictions are so cumbersome to work with in practice that people don't bother and just enable all access again.

@luiseduardocolon luiseduardocolon added the security identity compliance IAM, Cognito, Secrets Manager, GuardDuty, etc. label Jan 3, 2020
@acdebaca
Copy link

acdebaca commented Jan 8, 2020

Hi! We studied this issue in depth at my organization and converged on the following approach. We always create role stacks first, KMS CMK stacks second, and all other resources subsequently based on dependency order.

This design not only permits us to import role arns into key policies, but we can also layer in role policies that specify the KMS keys as well because multiple role policies can be created separate resources:

1. Role  <---+
    ^        |----  3. Role Policy
2. Key   <---+

We further build on this model by layering in additional inline role policies on resources created later. For example, consider how we might provide read access to a CF-created role to an S3 bucket using SSE-KMS encryption with a KMS CMK with both role and resource policies:

1. Role <-+------------------+--------------------------------+
    ^     |- 3. Role Policy  |- 4. SSE-KMS S3 bucket+policy <-+- 5. Role Policy
2. Key  <-+------------------+
  1. Role
  2. Key + Policy referencing role (e.g. kms:Decrypt, kms:GenerateDataKey*)
  3. Role Policy referencing role and key (e.g. kms:Decrypt, kms:GenerateDataKey*)
  4. S3 Bucket referencing key + policy referencing role (NB: bucket policies are separate resources, but you can only have one bucket policy per bucket) (e.g. s3:GetObject)
  5. Role Policy referencing role and bucket (e.g. s3:GetObject)

Note that resource policies reference roles as AWS principals, whereas policies reference roles via the Roles property in an AWS::IAM::Policy stack template.

The general pattern:

  • Create roles first
  • For each resource (bucket, KMS CMK, etc)
    • Create the resouce alongside the resource policy
    • Create an IAM policy attached to the corresponding role

@rix0rrr
Copy link
Author

rix0rrr commented Dec 24, 2020

Found a use case where a {"AWS": "arn:aws:iam::111122223333:root"} grant wouldn't even suffice: encrypted queues. See linked issue.

@kz974
Copy link

kz974 commented Mar 18, 2021

Yes please.
Would help a lot in avoiding circular dependencies between resources when deploying stacks!

@zoomzoomcloud
Copy link

Pretty please. It makes no sense that AWS::KMS::KeyPolicy resource type doesn't exist.

@jpr5
Copy link

jpr5 commented Jun 17, 2021

I too have the circular dependency problem.

I read all the comments and, more importantly, tried to grok @acdebaca 's response in the context of what I was trying to do. Must have scratched a hole in my chin, trying so hard to understand - and to no avail.

My view of the problem is simple: if I can do it in the console, I should be able to do it via CloudFormation. I figured out what I needed to do in the console, then wrote the equivalent in CloudFormation YAML - which resulted in a circular dependency. Then I went to Former2, which accesses your AWS account and produces somewhat-usable YAML to describe the architectural bits you select. Funny, that - it too produced something with a circular dependency.

Simplification of what I was doing:

Bucket:
    - encrypt with Key

Key:
    - inline: grant (ec2 instance) Role permission to use key

Role:
    - inline: ec2 instance can assume Role
    - reference Policy

Policy: 
    - grant permission to access Bucket

There's the circle -- Bucket -> Key -> Role -> Policy -> Bucket. It's what I constructed manually in the console, and what Former2 produced as well. However, simplistically, to break the circle, one of the dependencies just needs to point in the other direction. Then there's no more circle.

I took what the OP @rix0rrr was saying as essentially asking for a way to point the Key's policy in the other direction - to create a policy separately and associate it post-facto key creation (and post-facto the Role it was referencing). I agree with them, why this doesn't exist -- and really, why the AWS team thought their time was better spent exploring and producing some frankly convoluted way to achieve an outcome that basically means the user needs to take on a lot more work and totally change their mental model, rather than just add the mechanism -- is all beyond me. I still haven't figured out how to apply what they suggested, and I'm honestly not that stupid.

That all said, I found another spot to point a dependency in the other direction. I removed the Policy reference from Role, and used the Roles: property of Policy to point back to the Role. Now I get:

Bucket:
    - encrypt with Key

Key:
    - inline: grant (ec2 instance) Role permission to use key

Role:
    - inline: ec2 instance can assume Role

Policy: 
    - grant permission to access Bucket
    - roles: Role

And voila, the circle broken.

Later I decided to move "grant Role permission to use Key" from Key to Policy as "grant permission to use Key" (no Principal), which in retrospect makes more logical sense. FWIW.

Hopefully this helps someone. Cheers.

@WaelA WaelA added the enhancement New feature or request label Aug 5, 2021
@JohnPreston
Copy link

New use-case

  • When using SSM for ECS logging etc, and you want to define the KMS Key for the LogGroup, you have to know in advance the name of the log group, where it would be much easier to build up the kms key policy once you have the ARN of all the resources that might need access to the key, especially the AWS Services

@andreaschiappacasse
Copy link

My use case:

I want to produce a script to migrate the content of a kms encrypted bucket from one source account to a target account.

I would like to have a cdk stack that is only deployed when a migration occurs (and destroyed after it finishes) and that is capable of adding the required permissions for the target account to run CopyObject via Boto3 on the source account bucket.

The stack should manage S3 permissions on existing buckets (via BucketPolicy) and KMS permissions on existing keys, but the latter seems impossible right now, and would be easily solved having a KeyPolicy which can be used by the stack dedicated to the temproary migration resources.

@shearn89
Copy link

shearn89 commented May 3, 2023

Yep, (possibly not, but throwing in my 2p) another use case:

  • I want to allow automation roles and admin fallback role permission to manage they key - roles exist before use, fine
  • I want to allow Lambdas in my stack permission to use the key with specific actions - as above, all sorts of dependency/ordering fun based on whether the role exists, key already been created, etc etc.

Having this resource allows me to break up the ordering the same way I do with IAM::Role and IAM::Policy.

@chchang6
Copy link

chchang6 commented Jul 13, 2023

I'll add another current use case, but I guess it's basically the generalization of this issue.

  • A KMS key policy that only permits certain IAM roles to access the key
  • IAM roles that are limited to accessing the specific key

I don't want anyone (role) touching the key, only specific principals, AND
I don't want the role touching any key, only the key relevant to it.

It comes down to necessity AND sufficiency. Possessing the role should be necessary to access the key, and sufficient to do so. If I drop the key policy, no one can touch it; if I generalize, then everyone can touch it. If I generalize the role policy, then it can touch too many keys. If we're going to have this two-sided approach (policy on key, and policy on role, both must match up), then we should not have circular dependency be an issue.

Ditto to jpr5's comment--if something can be done in console, there should be a way to do it in CloudFormation. Adding to that, common things should be easy. As security becomes a bigger concern and more people are actually paying attention to IAM and key policies, this particular problem should get rectified.

@twixr
Copy link

twixr commented Apr 5, 2024

I seem to have the same issue.
My use case:

  • I have a SNS topic that uses a KMS key as a "masterKey" to encrypt messages send via SNS
  • So I have to create KMS before SNS.
  • I then have to give access in the KMS resource policy to allow certain actions from that SNS topic.

But that already results in a circular dependency.

@benbridts
Copy link

@twixr For that specific case you don't have to give the SNS Topic permissions to use the key, but the producers / consumers of that topic, so you would:

  • Create IAM Roles
  • Create a KMS Key, giving those roles Encrypt/Decrypt permissions (depending), or delegate those permissions to IAM
  • Create the SNS topic
  • Create a AWS::IAM::Policys or AWS::IAM::ManagedPolicys to give IAM permissions on both the Key and the Topic to the Roles

@twixr
Copy link

twixr commented Apr 5, 2024

@benbridts thanks, but is it possible to attach a role to a SNS Topic, I thought it wasn't possible.
So then I would have a role with a policy to access KMS. But how do I tell SNS that it has to have that role?

Edit: Ah I misread, so the permission to encrypt should be granted to the resource that publishes to the topic?
Although, when I publish via the AWS Console, my user / role would have access to the KMS key but the SNS message isn't published correctly. So not sure how that works then.

@benbridts
Copy link

Edit: Ah I misread, so the permission to encrypt should be granted to the resource that publishes to the topic?
Although, when I publish via the AWS Console, my user / role would have access to the KMS key but the SNS message isn't published correctly. So not sure how that works then

Indeed.

If this fails, you might be missing the delegation to IAM on the KMS key itself (in contrast with s3 bucket policies, for KMS you need permissions in both the Resource policy and Principal policy)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request security identity compliance IAM, Cognito, Secrets Manager, GuardDuty, etc.
Projects
None yet
Development

No branches or pull requests