AWS KMS Policy implicitly allows cross-account access - amazon-web-services

It seems that KMS key policies implicitly allow public access to services.
My setup is as follows:
Account A (my account)
Account B (external Account)
KMS key, customer-managed
SNS Topic
SQS Queue, encrypted
The SQS-Queue subscribes to the SNS topic. Pretty basic stuff.
The KMS key policy allows Account A, the SNS service, and the SQS service to use this key:
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::[AccountA]:root"},
"Action": "kms:*",
"Resource": "*"
},{
"Effect": "Allow",
"Principal": {"Service": "sns.amazonaws.com"},
"Action": ["kms:*"],
"Resource": "*"
},{
"Effect": "Allow",
"Principal": {"Service": "sqs.amazonaws.com"},
"Action": ["kms:*"],
"Resource": "*"
}
The SNS topic is granted access to the the SQS Queue via the queue's Resource Policy:
{
"Effect": "Allow",
"Principal": {"Service": "sns.amazonaws.com"},
"Action": "sqs:*",
"Resource": "arn:aws:sqs:[region]:[AccountA]:sqs-queue-a",
"Condition": {"ArnEquals": {"aws:SourceArn": "arn:aws:sns:[region]:[AccountB]:sns-topic-b"}}
}
Here comes the twist: This works. Wait, but we never allowed Account B to use the KMS key. Usually I'm glad when things work. But from a security perspective I'm a bit concerned. My question is:
Why has the Account B SNS-Topic access to the KMS key of Account A, without ever mentioning Account B in the KMS key policy?
The additional security layer in this particular case is the queue's resource policy, so that's fine. But just from the KMS key policy, every AWS account can now use my key, when using SNS or SQS. I thought, AWS policies implicitly deny calls from other accounts. But it seems that "Principal": {"Service": "sns.amazonaws.com"} makes the key available to public?
A follow-up question would be: How can I restrict KMS access to specific accounts?
My first try was this constraint in the KMS key policy:
},{
"Effect": "Allow",
"Principal": {"Service": "sns.amazonaws.com"},
"Action": ["kms:*"],
"Resource": "*"
"Condition": {"ArnEquals": {"aws:SourceArn": "arn:aws:sns:[region]:[AccountB]:sns-topic-b"}}
},{
However, AWS makes it clear that the conditions aws:SourceAccount and aws:SourceArn do not work in KMS policies.
Thanks for reading! I appreciate every help <3

Related

AWS IAM and KMS policy 'muddlement'

I'm hoping some AWS policy expert may be able to help me decode what's going on here.
I've been playing with IAM and resource policies in AWS. According to AWS's own documentation, unless there are any explicit denies in all of the policies, the resource policy should take precedence over the IAM policy. See the attached link showing AWS's policy evaluation logic. If the resource policy is an 'allow', then the IAM policy shouldn't be evaluated.
Policy Evaluation Logic
The challenge I'm struggling to get to grasps with (when using KMS) is this. I have defined an user IAM policy that looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:DeleteAlias"
],
"Resource": "*"
}
]
}
Its only purpose is to permit a user to delete a KMS CMK alias. And, I have created a KMS CMK (resource policy), that looks like this:
{
"Version": "2012-10-17",
"Id": "key-consolepolicy-3",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::xxxxxxxxxxxx:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow access for Key Administrators",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::xxxxxxxxxxxx:user/user1"
]
},
"Action": [
"kms:Create*",
"kms:Delete*"
],
"Resource": "*"
},
}
The problem I'm having, despite the KMS resource policy saying I can 'CreateAlias', AWS is not allowing me to do it unless the IAM policy explicitly has it included too.
I'm hoping someone may be able to explain to me how AWS's policy logic actually works and whether I may be doing something wrong.
Many thanks in advance!
This is because kms alias actions are unique and require both KMS key and IAM policy permissions. Specifically kms:CreateAlias must be allowed in both key policy and IAM policy of your user1:
This means that KMS key policies apply only to keys, not aliases.
I believe that the culprit could be that you are missing the kms:DescribeKey in both the IAM and the resource policy. It is listed as required in Controlling access to Aliases document.
kms:CreateAlias for the KMS key. This permission must be provided in a key policy or in an IAM policy that is delegated from the key policy.
{
"Sid": "Key policy for 1234abcd-12ab-34cd-56ef-1234567890ab",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::111122223333:user/KMSAdminUser"},
"Action": [
"kms:CreateAlias",
"kms:DescribeKey"
],
"Resource": "*"
}

Attach IAM Policy to SNS Topic

I am trying deliver a message from an unencrypted SNS topic to an encrypted SQS queue following this guide. I was able to complete the "Configure KMS permissions for AWS services" step, but I am having trouble with the "Configure KMS permissions for producers
" step. I have created the IAM role, however attaching this role to my SNS topic is where I am specifically confused. Here are some questions I have which my own research was unable to answer:
Can an IAM role be attached to a specific item (SNS topic, SQS queue, etc...)? If not, what other way is there to grant permissions to a specific item?
When the instructions mention "producer", is this referring to the SNS topic or the AWS account which owns the SNS topic?
Any and all help is greatly appreciated.
Edit:
Here is my current AWS KMS key policy:
{
"Version": "2012-10-17",
"Id": "key-consolepolicy-3",
"Statement": [
{
"Sid": "Allow administration of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${aws_account_id}:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow SNS to use KMS",
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "*"
}
]
}
Whenever I add the following statements to my KMS key policy in the Statement list, I get the error "MalformedPolicyDocumentException - Policy contains a statement with no principal":
{
"Effect": "Allow",
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "${kms_customer_managed_key_arn}"
},
{
"Effect": "Allow",
"Action": [
"sqs:SendMessage"
],
"Resource": "${sqs_queue_arn}"
}
If not, what other way is there to grant permissions to a specific item?
The permissons from the link are attached to your KMS CMK key policy.
When the instructions mention "producer", is this referring to the SNS topic or the AWS account which owns the SNS topic?
The producer is anyone or anything that sends messages. It can be a lambda function, an ec2 instance or IAM user/role. In that case you give the producer permissions to sendMessage and use the KMS key. For lambda it would be in lambda execution role, for instance it would be in an instance role.

AWS allow any user of a specific account to send messages to a queue

I am trying to configure the SQS policy for a queue to authorize all principles of an account to send messages to this queue, according to the documentation here:
{
"Version": "2012-10-17",
"Id": "sqspolicy",
"Statement": [
{
"Sid": "Sqs policy1",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::123456789:root"
]
}
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:eu-west-1:123456789:my_queue"
}
]
}
Will this allow any principle of this account to send messages to my_queue or will it only allow the root user?
Or should I use the below policy with a condition instead?
{
"Version": "2012-10-17",
"Id": "sqspolicy",
"Statement": [
{
"Sid": "Sqs policy1",
"Effect": "Allow",
"Principal": {
"AWS": "*"
}
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:eu-west-1:123456789:my_queue"
"Condition": {
"StringEquals": {
"AWS:SourceAccount": "123456789"
}
}
}
]
}
TIA
The documentation says:
When you use an AWS account identifier as the principal in a policy, you delegate authority to the account. All identities inside the account can access the resource if they have the appropriate IAM permissions attached to explicitly allow access. This includes IAM users and roles in that account.
According to this, your first approach will allow all of your users to send messages.
Only first policy is valid. The second policy will not work, the way you may think. The reason is that aws:SourceAccount is only used for service-to-service requests, not IAM users or roles. The most common example of when aws:SourceAccount is used is for S3:
For example, when an Amazon S3 bucket update triggers an Amazon SNS topic post, the Amazon S3 service invokes the sns:Publish API operation. The bucket is considered the source of the SNS request and the value of the key is the account ID associated with the bucket.
Send-messages requests made by IAM users/roles in the second account will be denied because for these entities there is no aws:SourceAccount.

KMS Not found Exception in AWS Cross Account S3 PutObject encrypted by AWS Managed Key

I am trying to put a dummy file from Glue which is in Account B to S3 bucket in account A. S3 bucket(test-bucket) is having AWS-KMS encryption with aws/s3 Managed Key enabled.
I added below permissions in Account A- S3 bucket (test-bucket):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Deny PutObject if NOT using correct KMS Encryption Key",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::test-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "",
"s3:x-amz-server-side-encryption-aws-kms-key-id": "<ARN_KMS_ACCOUNT_A>"
}
}
},
{
"Sid": "Allow Glue Role in Application account to put objects in the S3 bucket",
"Effect": "Allow",
"Principal": {
"AWS": "<IAM_Glue_Role_ARN>"
},
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::test-bucket",
"arn:aws:s3:::test-bucket/*"
]
},
{
"Sid": "Only allow writes to my bucket with bucket owner full control",
"Effect": "Allow",
"Principal": {
"AWS": "<IAM_Glue_Role_ARN>"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::test-bucket/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
Added below policy to IAM Glue role in Account B
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:Get*",
"s3:List*",
"s3:Put*"
],
"Resource": "arn:aws:s3:::test-bucket*",
"Effect": "Allow"
},
{
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "<ARN_KMS_ACCOUNT_A>",
"Effect": "Allow"
}
]
}
This is my Glue Code:
s3.put_object(
Bucket='output',
Key='_SUCCESS',
ServerSideEncryption='aws:kms',
SSEKMSKeyId='<ARN_KMS_ACCOUNT_A>'
)
Getting below error while running this code from Account B Glue:
ClientError: An error occurred (KMS.NotFoundException) when calling the PutObject operation: Invalid arn ap-southeast-2
Any thoughts on this.?
The AWS managed CMK aws/s3 can only be used in the same account i.e. where the key exists (in your case, its Account A). You can either try to use the aws/s3 CMK from Account B OR create a customer managed CMK in Account A and share it with Account B following the steps here.
There's a couple of things:
For the policy on the bucket, Deny permissions should be always at the end after all the Allow permissions. And remove the condition on the Deny permissions. You want to block all traffic that's not authorized.
Use a managed KMS key. And on that key, grant kms:decrypt to the glue role on the key's policy.
As documented here you must use the full ARN of the encryption key so cross-account succeeds. Using an alias or key ID does not work.
Be aware of the following when using encryption for cross-account operations:
The AWS managed key (aws/s3) is used when a AWS KMS key Amazon Resource Name (ARN) or alias is not provided at request time, nor via the bucket's default encryption configuration.
If you're uploading or accessing S3 objects using AWS Identity and Access Management (IAM) principals that are in the same AWS account as your KMS key, you can use the AWS managed key (aws/s3).
Use a customer managed key if you want to grant cross-account access to your S3 objects. You can configure the policy of a customer managed key to allow access from another account.
If specifying your own KMS key, you should use a fully qualified KMS key key ARN. When using a KMS key alias, be aware that AWS KMS will resolve the key within the requester’s account. This can result in data encrypted with a KMS key that belongs to the requester, and not the bucket administrator.
You must specify a key that you (the requester) have been granted Encrypt permission to. For more information, see Allows key users to use a KMS key for cryptographic operations in the AWS Key Management Service Developer Guide.
The S3 bucket should look something like this

How to access AWS resources without using IAM Role or IAM User Access

Let assume we have Ec2 instance and there are two applications. only one application should be able to access S3 bucket and other application
shouldn't be able to access the S3 bucket.
1) I don't want to use an IAM user Access key ID and Secret access key for this issue, because it's difficult manage. That is not recommended. (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html)
2) But I can't use IAM role . Because it's associate with the Ec2 instance and It will allow access to every applications inside that Ec2.
You can apply a bucket policy to restrict access on same of HTTP header request. allows s3:GetObject permission with a condition, using the aws:referer key, that the get request must originate from specific webpages. The following policy specifies the StringLike condition with the aws:Referer condition key.
"Version": "2012-10-17",
"Id": "http referer policy example",
"Statement": [
{
"Sid": "Allow get requests referred by www.example.com and example.com.",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::examplebucket/*",
"Condition": {
"StringLike": {"aws:Referer": ["http://www.example.com/*","http://example.com/*"]}
}
},
{
"Sid": "Explicit deny to ensure requests are allowed only from specific referer.",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::examplebucket/*",
"Condition": {
"StringNotLike": {"aws:Referer": ["http://www.example.com/*","http://example.com/*"]}
}
}
]
}
AWS Reference Link