Endeavouring to apply the principle of least privilege to a CMK I'm creating, the goal is to create a CloudFormation template that can be used via StackSets to the entire organisation. As a result, I want a key that can be used (kms:Encrypt, kms:Decrypt etc.) for general encryption tasks in the account, but that cannot be modified by principals in the account (specifically kms:PutKeyPolicy but not only).
I have a working policy lifted from a hand crafted key. The CloudFormation template works fine, the StackSet initiates the resource creation.
But only when I don't restrict the account principal with any conditions, which removes the least privilege principle. The CreateKey and PutKeyPolicy API calls both have an option BypassPolicyLockoutSafetyCheck for those of us idiotic enough to think they know better! Except CloudFormation doesn't expose that for AWS::KMS::Key :(
So unless I basically leave the key policy wide open, I get the following error in the Stack:
Resource handler returned message: "The new key policy will not allow you to update the key policy in the future. (Service: Kms, Status Code: 400, Request ID: xxxx, Extended Request ID: null)" (RequestToken: xxxx, HandlerErrorCode: InvalidRequest)
I've tried a variety of options for the principal, with the Condition removed (as below) the key is created, with it in no joy.
- Sid: AllowUpdatesByCloudFormation
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- kms:Describe*
- kms:List*
- kms:PutKeyPolicy
- kms:CreateAlias
- kms:UpdateAlias
- kms:UpdateKeyDescription
- kms:EnableKey
- kms:DisableKey
- kms:EnableKeyRotation
- kms:DisableKeyRotation
- kms:GetKey*
- kms:DeleteAlias
- kms:TagResource
- kms:UntagResource
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: '*'
# Condition:
# StringEquals:
# "aws:PrincipalAccount": !Sub ${AWS::AccountId}
# "kms:ViaService": !Sub "cloudformation.${AWS::Region}.amazonaws.com"
I've tried with different principal settings, including AWS: "*" and a few different Service options, and different settings on the Condition. I've tried with and without the region in the service name. I must be missing something, but I've lost a few hours scratching my head over this one.
Web searches, AWS forum searches have turned up nothing, so I'm hoping the good bhurgers of StackOverflow can guide me - and future me's searching for the same help :)
It doesn't seem possible to link to the table section on the AWS KMS API page for the condition keys on CreateKey or PutKeyPolicy but I don't think I've missed a trick with those?
Thanks in advance - Robert.
Try giving the root user all kms permissions - (kms:*)
The principle of least privilege still applies when giving root all access.
That will enable IAM User permissions.
Add additional policies to each role or user or user group.
Add a policy for key administration.
Add a policy for usage.
That is where you can fine tune your access.
Try working with this key policy and tweak it.
This is a key I use for RDS encryption in a cfn stack.
(Yes! I know that policies should be applied to user groups, not users directly for best practices... but this is inside a sandbox environment I have access to from 'aCloud guru')
KeyPolicy:
Id: key-consolepolicy-3
Version: "2012-10-17"
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action: kms:*
Resource: '*'
- Sid: Allow access for Key Administrators
Effect: Allow
Principal:
AWS:
- !Sub "arn:aws:iam::${AWS::AccountId}:role/admin"
- !Sub "arn:aws:iam::${AWS::AccountId}:user/cloud_user"
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:TagResource
- kms:UntagResource
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: '*'
- Sid: Allow use of the key
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS"
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:DescribeKey
Resource: '*'
- Sid: Allow attachment of persistent resources
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS"
Action:
- kms:CreateGrant
- kms:ListGrants
- kms:RevokeGrant
Resource: '*'
Condition:
Bool:
kms:GrantIsForAWSResource: "true"
Related
I want to add AWS managed policy (AmazonSSMFullAccess) to a role using CloudFormation.
I tried to use AWS::IAM::ManagedPolicy but it creates a Customer Managed policy and I don't want that. I want it to be AWS managed.
Do you have any idea how can I do that?
I am trying to add AWS managed AmazonSSMFullAccess and here is the code I am using in mu CF template:
AmazonSSMFullAccess:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'cloudwatch:PutMetricData'
- 'ds:CreateComputer'
- 'ds:DescribeDirectories'
- 'ec2:DescribeInstanceStatus'
- 'logs:*'
- 'ssm:*'
- 'ec2messages:*'
Resource: '*'
- Effect: Allow
Action: 'iam:CreateServiceLinkedRole'
Resource: >-
arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*
Condition:
StringLike:
'iam:AWSServiceName': ssm.amazonaws.com
- Effect: Allow
Action:
- 'iam:DeleteServiceLinkedRole'
- 'iam:GetServiceLinkedRoleDeletionStatus'
Resource: >-
arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM*
- Effect: Allow
Action:
- 'ssmmessages:CreateControlChannel'
- 'ssmmessages:CreateDataChannel'
- 'ssmmessages:OpenControlChannel'
- 'ssmmessages:OpenDataChannel'
Resource: '*'
You can't create AWS managed policies, because only AWS can do it. You, as an AWS customer can only create customer managed policies.
I'm using serverless to manage my Lambda functions.
If I have to white-list all of the functions in given Serverless deployment I could use something like this.
provider:
name: aws
...
resourcePolicy:
- Effect: Deny
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
Condition:
NotIpAddress:
aws:SourceIp:
- '141.206.243.10/32' # Teradata IP
- '142.0.162.0/32' # Eloqua IPs
- Effect: Allow
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
In my scenario, I have two APIs, for example
GetEmployees
PutEmployees
I want to restrict GetEmployees to be available from a certain IP range 1.2.3.x/24 and PutEmployees to be available from another IP range 4.5.6.x/24.
How can I accommodate this serverless.yml?
Resource Policies are attached to API Gateways not individual paths, so you can only have one Resource Policy document per API Gateway.
But as the rules in a policy are an array of rules, you can have individual rules for different stages, paths and methods in the following format:
execute-api:/{stage}/{path}/{method}
So what you need in your Resource Policy, is to be a bit more specific.
The below example would apply the different IP restrictions to specific resources in your API for any stage and any method.
provider:
name: aws
...
resourcePolicy:
- Effect: Allow
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
- Effect: Deny
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/GetEmployees/*
Condition:
NotIpAddress:
aws:SourceIp:
- '1.2.3.x/24'
- Effect: Deny
Principal: '*'
Action: execute-api:Invoke
Resource:
- execute-api:/*/PutEmployees/*
Condition:
NotIpAddress:
aws:SourceIp:
- '4.5.6.x/24'
I've been working with Serverless (the framework) and I've come across an issue. This might be down to my minimal knowledge of AWS and it's archicture but maybe someone can point me in the right direction.
I've created an S3 bucket with Terraform, it utilises AWS's KMS to give the bucket Server Side Encrpytion. Uploading to this bucket works fine from the CLI, but when using a Lambda created by serverless it returns and "Access Denied".
The serverless yaml has permissions for uploads to S3 and I've tested this with SSE turned off and it works fine.
What I don't understand is how to specify the key for AWS. I thought adding it to the top of the service might work (but to no avail).
Here is the yaml file:
service:
name: lambdas
awsKmsKeyArn: [KEY GOES HERE]
custom:
serverless-offline:
port: 3000
bucket:
name: evidence-bucket
serverSideEncryption: aws:kms
sseKMSKeyId: [ KEY GOES HERE]
provider:
name: aws
runtime: nodejs12.x
region: eu-west-2
iamRoleStatements:
- Effect: Allow
Action:
- s3:ListBucket
- s3:PutObject
- s3:PutObjectAcl
Resource: "arn:aws:s3:::${self:custom.bucket.name}/*"
- Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:DescribeKey
Resource: "[KEY GOES HERE]"
functions:
storeEvidence:
handler: handler.storeEvidence
environment:
BUCKET: ${self:custom.bucket.name}
events:
- http:
path: store-evidence
method: post
Do I need an additional plugin? There is a lot of information about creating a bucket with serverless but not for using an existing bucket with SSE? How do I get around this "Access Denied" message?
As jarmod in the comment said, you are missing the kms:GenerateDataKey. Here I am gonna show you what exact need to add to your existing yaml shown above:
# ...
provider:
name: aws
runtime: nodejs12.x
region: eu-west-2
iamRoleStatements:
- Effect: Allow
Action:
- s3:ListBucket
- s3:PutObject
- s3:PutObjectAcl
Resource: "arn:aws:s3:::${self:custom.bucket.name}/*"
- Effect: Allow
Action:
- kms:Encrypt
- kms:Decrypt
- kms:DescribeKey
- kms:GenerateDataKey # <------ this is the new permission
Resource: "[KEY GOES HERE]"
#...
And it is worth to note that if your code literally just use s3:PutObject to upload, you don't need to add Encrypt,DescribeKey permissions. See: https://aws.amazon.com/premiumsupport/knowledge-center/s3-access-denied-error-kms/
If your code involves multipart upload, you do need kms:DescribeKey, kms:Encrypt and more permissions(like kms:ReEncrypt*, kms:GenerateDataKey*...) See details: https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html
Try this:
iamRoleStatements:
- Effect: Allow
Action:
- s3:*
Resource: "arn:aws:s3:::${self:custom.bucket.name}/*"
- Effect: Allow
Action:
- kms:*
Resource: "[KEY GOES HERE]"
If this works, you know you were missing some action. Its then a painful process of finding that missing action, or if your happy, just leave the *s in.
I am trying to create an AWS KMS Key Policy and have been plagued trying to get Cloudformation to accept the key policy. Everything I have been able to find and read says this policy should be valid and the syntax is correct as it runs, but returns MalformedPolicyDocumentExceptionnull (Service: AWSKMS; Status Code: 400;
Has anyone else run into this, if so, any thoughts or suggestions on how I can resolve the errors? I've been stuck and banging my head on this one and can't see what I'm missing and my google-fu is failing me.
Code Snippet:
SnowflakeProdKMS:
Type: AWS::KMS::Key
Properties:
Description: KMS key used by Snowflake to encrypt/decrypt data stored in s3
Enabled: True
EnableKeyRotation: False
KeyPolicy:
Version: 2012-10-17
Id: key-default-1
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- kms:*
Resource: '*'
- Sid: Enable AWSAdminRole to have full permissions to KMS key
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${AWS::AccountId}:/role/AWSAdminRole
Action: kms:*
Resource: '*'
- Sid: Allow use of the key by other roles
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${AWS::AccountId}:role/AWSAdminRole
# - !Sub arn:aws:iam::${AWS::AccountId}:role/SnowflakeAccessRole
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt
- kms:GenerateDataKey
- kms:DescribeKey
Resource: '*'
- Sid: Allow attachment of persistent resources
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${AWS::AccountId}:role/AWSAdminRole
# - !Sub arn:aws:iam::${AWS::AccountId}:role/SnowflakeAccessRole
- !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- kms:CreateGrant
- kms:ListGrants
- kms:RevokeGrant
Resource: '*'
Condition:
Bool:
- kms:GrantIsForAWSResource: 'true'
After much trial and error and reaching out to other partners I found the solution for the above issue.
The Condition on snippet above was incorrect and should have been formatted as follows:
Condition:
Bool:
"kms:GrantIsForAWSResource": true
Once changed to this the policy went in without issue.
I am configuring an AWS Cognito Identity Pool using the severless framework, and I am editing a file in the yml configuration to add an unauthenticated role for users to upload an image to an s3 bucket.
The code was previously deployed without an unauthenticated role being specified, and the deployment went fine and was stable. After I went looking for a way to control the permissions regarding accessing the S3 bucket, I discovered that the only way to give write, but not read, permissions on an S3 bucket is to specify it in a user policy, so I have to add an unathenticated role to the identity pool. However, when I deploy the code, I get an error stating:
Serverless Error ---------------------------------------
An error occurred: CognitoIdentityPoolRoles - Resource cannot be updated.
I have managed to get around the problem in the dev environment but it required totally deleting the stack and rebuilding it from scratch.
I also do not want to go in and adjust the resources manually in the AWS console since resources should be managed in cloudformation or in the console, but doing it both ways leads to chaos.
So, at the moment, the options I see are to delete the entire stack and rebuild it with the new roles, or find a way to update through cloudformation.
Does anyone have a way to avoid the first option and allow me to update the stack without attaching the role in the console?
Relevant section of serverless.yml is below...
Resources:
# The federated identity for our user pool to auth with
CognitoIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
# Generate a name based on the stage
IdentityPoolName: ${self:custom.stage}MyIdentityPool
# Allow unathenticated users
AllowUnauthenticatedIdentities: true
# Link to our User Pool
CognitoIdentityProviders:
- ClientId:
Ref: CognitoUserPoolClient
ProviderName:
Fn::GetAtt: [ "CognitoUserPool", "ProviderName" ]
# IAM roles
CognitoIdentityPoolRoles:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId:
Ref: CognitoIdentityPool
Roles:
authenticated:
Fn::GetAtt: [CognitoAuthRole, Arn]
# Next two lines are the 2 lines of code which break everything
unauthenticated:
Fn::GetAtt: [CognitoUnAuthRole, Arn]
# IAM role for UN-authenticated users
CognitoUnAuthRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Federated: 'cognito-identity.amazonaws.com'
Action:
- 'sts:AssumeRoleWithWebIdentity'
Condition:
StringEquals:
'cognito-identity.amazonaws.com:aud':
Ref: CognitoIdentityPool
'ForAnyValue:StringLike':
'cognito-identity.amazonaws.com:amr': unauthenticated
Policies:
- PolicyName: 'CognitoUnAuthorizedPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action:
- 'mobileanalytics:PutEvents'
- 'cognito-sync:*'
- 'cognito-identity:*'
Resource: '*'
# Allow users to upload attachments to their
# folder inside our S3 bucket
- Effect: 'Allow'
Action:
- 's3:PutObject'
Resource:
- Fn::Join:
- ''
-
- Fn::GetAtt: [MediafilesBucket, Arn]
- '/submissions/'
Fixed.
I commented out the sections of serverless.yml related to the identity pool deployed (destroyed), then uncommented that section, redeployed, and restored from backup.
It seems to be a bit of a hack, but it worked.
I also feel like there should be a way to edit identity pool roles through cloudformation...