Open rix0rrr opened 4 years ago
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.
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 <-+------------------+
kms:Decrypt
, kms:GenerateDataKey*
)kms:Decrypt
, kms:GenerateDataKey*
)s3:GetObject
)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:
Found a use case where a {"AWS": "arn:aws:iam::111122223333:root"}
grant wouldn't even suffice: encrypted queues. See linked issue.
Yes please. Would help a lot in avoiding circular dependencies between resources when deploying stacks!
Pretty please. It makes no sense that AWS::KMS::KeyPolicy resource type doesn't exist.
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.
New use-case
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.
Yep, (possibly not, but throwing in my 2p) another use case:
Having this resource allows me to break up the ordering the same way I do with IAM::Role and IAM::Policy.
I'll add another current use case, but I guess it's basically the generalization of this issue.
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.
I seem to have the same issue. My use case:
But that already results in a circular dependency.
@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:
AWS::IAM::Policy
s or AWS::IAM::ManagedPolicy
s to give IAM permissions on both the Key and the Topic to the Roles@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.
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)
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 asiam:Encrypt
, this makes it impossible to create aKey
with restrictive permissions in Stack 1, and aRole
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 theRole
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:
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
6. Category (required) - Will help with tagging and be easier to find by other users to +1