Lambda cannot access KMS Key - amazon-web-services

When I run my lambda code, I get the following error:
The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.
I have mostly followed this to create the stack using aws-sam-cli, and the relevant sections of the template are below the code.
The relevant code is:
const ssm = new AWS.SSM();
const param = {
Name: "param1",
WithDecryption: true
};
const secret = await ssm.getParameter(param).promise();
The relevant part of the template.yaml file is:
KeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: 'param1Key'
TargetKeyId: !Ref Key
Key:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Id: default
Statement:
- Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- 'kms:Create*'
- 'kms:Encrypt'
- 'kms:Describe*'
- 'kms:Enable*'
- 'kms:List*'
- 'kms:Put*'
- 'kms:Update*'
- 'kms:Revoke*'
- 'kms:Disable*'
- 'kms:Get*'
- 'kms:Delete*'
- 'kms:ScheduleKeyDeletion'
- 'kms:CancelKeyDeletion'
Resource: '*'
Sid: Allow root account all permissions except to decrypt the key
Version: 2012-10-17
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../
Handler: app.lambda
Runtime: nodejs8.10
Policies:
- DynamoDBReadPolicy:
TableName: !Ref Table
- KMSDecryptPolicy:
KeyId: !Ref Key
- Statement:
- Action:
- "ssm:GetParameter"
Effect: Allow
Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/param1"
Does the KMSDecryptPolicy not allow the use of the key? What am I missing? Thanks!
EDIT: Changing the template to below works, but I'd really like to use the KMSDecryptPolicy in the lambda definition if possible.
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../
Handler: app.lambda
Runtime: nodejs8.10
Policies:
- DynamoDBReadPolicy:
TableName: !Ref Table
- KMSDecryptPolicy:
KeyId: !Ref Key
- Statement:
- Action:
- "ssm:GetParameter"
Effect: Allow
Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/param1"
Key:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Id: default
Statement:
- Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- 'kms:Create*'
- 'kms:Encrypt'
- 'kms:Describe*'
- 'kms:Enable*'
- 'kms:List*'
- 'kms:Put*'
- 'kms:Update*'
- 'kms:Revoke*'
- 'kms:Disable*'
- 'kms:Get*'
- 'kms:Delete*'
- 'kms:ScheduleKeyDeletion'
- 'kms:CancelKeyDeletion'
Resource: '*'
Sid: Allow root account all permissions except to decrypt the key
- Sid: 'Allow use of the key for decryption by the LambdaFunction'
Effect: Allow
Principal:
AWS: !GetAtt LambdaFunctionRole.Arn
Action:
- 'kms:Decrypt'
Resource: '*'
Version: 2012-10-17

The question itself contains the answer. The change is that instead of giving KMS permissions in the lambda role only (identity based way), it has also given permissions to the lambda role in the key policy (resource based way).
Here is the AWS official resource on why this is happening - https://docs.aws.amazon.com/kms/latest/developerguide/iam-policies.html
According to this
All KMS CMKs have a key policy, and you must use it to control access to a CMK. IAM policies by themselves are not sufficient to allow access to a CMK, though you can use them in combination with a CMK's key policy.

Related

CloudFormation claims KMS policy statement principals are invalid

I have the following SAM template:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
test lambda
Globals:
Function:
Timeout: 3
Tracing: Active
Api:
TracingEnabled: True
Resources:
NotesFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Zip
CodeUri: notes/
Handler: app.lambdaHandler
Runtime: nodejs18.x
Policies:
- AmazonDynamoDBFullAccess
Architectures:
- x86_64
Events:
FetchNotes:
Type: Api
Properties:
Path: /notes
Method: get
GiveNotes:
Type: Api
Properties:
Path: /notes
Method: post
Users:
Type: Api
Properties:
Path: /notes/users
Method: get
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
KmsKey:
Type: AWS::KMS::Key
Properties:
Description: CMK for encrypting and decrypting
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: Allow administration of the key
Effect: Allow
Principal:
AWS: arn:aws:iam::<MY_ACCOUNT>:role/aws-service-role/cks.kms.amazonaws.com/KMSKeyAdminRole
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: '*'
- Sid: Allow use of the key
Effect: Allow
Principal:
AWS: !Ref NotesFunctionRole
Action:
- kms:DescribeKey
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey
- kms:GenerateDataKeyWithoutPlaintext
Resource: '*'
NotesDynamoDB:
Type: AWS::DynamoDB::Table
Properties:
TableName: experimental-notes
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
StreamSpecification:
StreamViewType: NEW_IMAGE
Outputs:
NotesApi:
Description: "API Gateway endpoint URL for dev stage for Notes function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/dev/notes/"
NotesFunction:
Description: "Notes Lambda Function ARN"
Value: !GetAtt NotesFunction.Arn
NotesFunctionIamRole:
Description: "Implicit IAM Role created for Notes function"
Value: !GetAtt NotesFunctionRole.Arn
NotesDynamoDB:
Description: "DynamoDB table backing the Lambda"
Value: !GetAtt NotesDynamoDB.Arn
When I build + deploy this template I get the following CloudFormation errors:
Resource handler returned message: "Policy contains a statement with one or more invalid principals....
Obviously I have redacted my actual account ID and replaced it with <MY_ACCOUNT> (!).
But it doesn't say what's "invalid" about which principals. The idea is that the 2nd policy statement gets applied/hardcoded to an existing role (KMSKeyAdminRole). And that the 3rd Statement gets applied to the role of the NotesFunction Lambda created up above.
Can anyone spot where I'm going awry?
This ended up working perfectly and fixing the CF error:
- Sid: Allow use of the key
Effect: Allow
Principal:
AWS: !GetAtt FeedbackFunctionRole.Arn
Action:
- kms:DescribeKey
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey
- kms:GenerateDataKeyWithoutPlaintext
Resource: '*'
Use this https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_testing-policies.html to test if your policies are valid

Usage of AWS Lake Formation with CloudFormation

I want to set up an additional security layer on top of my S3 / Glue Data Lake
using Lake Formation. I want to do as much as possible via Infrastructure as Code, so naturally I looked into the documentation of the CloudFormation implementation of Lake Formation which is currently, frankly speaking, very useless.
I have a simple use case: Granting admin permission to one IAM-User on one bucket.
Can someone help me out with an example or anything similar?
This is what I found out:
Setting a data lake location and granting data permissions to your data bases is currently possible. Unfortunately it seems like CloudFormation doesn't support Data locations yet. You will have to grant your IAM Role access to the S3 Bucket by hand in the AWS Console under Lake Formation -> Data locations. I will update the answer as soon as CloudFormation supports more.
This is the template that we are using at the moment:
DataBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: InfrequentAccessRule
Status: Enabled
Transitions:
- TransitionInDays: 30
StorageClass: INTELLIGENT_TIERING
GlueDatabase:
Type: AWS::Glue::Database
Properties:
CatalogId: !Ref AWS::AccountId
DatabaseInput:
Name: !FindInMap [Environment, !Ref Environment, GlueDatabaseName]
Description: !Sub Glue Database ${Environment}
GlueDataAccessRole:
Type: AWS::IAM::Role
Properties:
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: ''
Effect: Allow
Principal:
Service: glue.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: AccessDataBucketPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- glue:*
- lakeformation:*
Resource: '*'
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:ListBucket
- s3:DeleteObject
Resource:
- !Sub ${DataBucket.Arn}
- !Sub ${DataBucket.Arn}/*
DataBucketLakeFormation:
Type: AWS::LakeFormation::Resource
Properties:
ResourceArn: !GetAtt DataBucket.Arn
UseServiceLinkedRole: true
DataLakeFormationPermission:
Type: AWS::LakeFormation::Permissions
Properties:
DataLakePrincipal:
DataLakePrincipalIdentifier: !GetAtt GlueDataAccessRole.Arn
Permissions:
- ALL
Resource:
DatabaseResource:
Name: !Ref GlueDatabase
DataLocationResource:
S3Resource: !Ref DataBucket

How to add policy to access secret with lambda function AWS SAM

I am trying to give access permission of secret manager to my lambda function in SAM template but it is giving me error that policy statement is malformed.
Policies:
- Statement:
- Sid: AWSSecretsManagerGetSecretValuePolicy
Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: <arn >
Can some one let me know the correct way of adding policy to my lambda function.
I am using SAM template (Type: AWS::Serverless::Function)
This policy only accepts ARN of a secret, so secret name will not work. https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-template-list.html#secrets-manager-get-secret-value-policy
Below works for me.
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: MyProject/
Handler: app
Policies:
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: 'arn:aws:secretsmanager:####'
or passing it as a parameter
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Ref RdsSecretArn
There are SAM Policy Templates where one of them is AWSSecretsManagerGetSecretValuePolicy you can use them directly in the definition.
Or if you wanna manage the policies yourself.
QueryFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_handler.lambda
Policies:
- AmazonDynamoDBFullAccess
- AWSLambdaVPCAccessExecutionRole
- SSMParameterReadPolicy:
ParameterName: parameter_name
- Statement:
- Effect: Allow
Action:
- dynamodb:*
Resource: 'resource_arn'
Runtime: python3.7
Try this :
Policies:
- Version: '2012-10-17'
Statement:
- Sid: AWSSecretsManagerGetSecretValuePolicy
Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: <arn >
This policy on the lambda works for me (YAML)
Policies:
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn:
Ref: THE_NAME_YOU_GAVE_YOUR_SECRET_RESOURCE

Add statements to serverless "KeyPolicy" on KMS Resource

I have a serverless application, which creates a KMS Resource:
# serverless.yml 1
resources:
Resources:
SomeLambdaRole:
Type: AWS::IAM::Role
AnotherLambdaRole:
Type: AWS::IAM::Role
TheKey:
Type: AWS::KMS::Key
DeletionPolicy: Retain
Properties:
Description: The key
Enabled: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Allow use of the key
Effect: Allow
Principal:
AWS:
- Fn::GetAtt: [SomeLambdaRole, Arn]
- Fn::GetAtt: [AnotherLambdaRole, Arn]
Action:
- 'kms:Encrypt'
- 'kms:Decrypt'
- 'kms:ReEncrypt'
- 'kms:GenerateDataKey*'
Resource: '*'
From another serverless application where some roles are created, I want to give these new roles the same permissions that SomeLambdaRole and AnotherLambdaRole have on the "TheKey" Resource
# serverless.yml 2
resources:
Resources:
YetAnotherLambdaRole:
Type: AWS::IAM::Role
# Do something to let this role have the same permission as "SomeLambdaRole" and "AnotherLambdaRole" for the "TheKey" Resource
Is this possible or should I try another approach?

AWS SAM managed policy for SSM get parameter

Is there any managed policy similar to DynamoDBReadPolicy for the ssm:GetParameter* permission for a Lambda function? I'm using aws-sam-cli and trying to follow this, but when I try to fetch the parameters when using sam local start-api, I get the following error:
InvalidAction: The action or operation requested is invalid. Verify that the action is typed correctly.
Here is the snippet where I try to get the parameter:
const ssm = new AWS.SSM();
const param = {
Name: "param1",
WithDecryption: true
};
const secret = await ssm.getParameter(param).promise();
The relevant template sections are below. Thanks!
KeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: 'param1Key'
TargetKeyId: !Ref Key
Key:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Id: default
Statement:
- Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- 'kms:Create*'
- 'kms:Encrypt'
- 'kms:Describe*'
- 'kms:Enable*'
- 'kms:List*'
- 'kms:Put*'
- 'kms:Update*'
- 'kms:Revoke*'
- 'kms:Disable*'
- 'kms:Get*'
- 'kms:Delete*'
- 'kms:ScheduleKeyDeletion'
- 'kms:CancelKeyDeletion'
Resource: '*'
Sid: Allow root account all permissions except to decrypt the key
Version: 2012-10-17
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../
Handler: app.lambda
Runtime: nodejs8.10
Policies:
- DynamoDBReadPolicy:
TableName: !Ref Table
- KMSDecryptPolicy:
KeyId: !Ref Key
# I think I need the ssm policy here
The available SAM policy templates are listed in their Github repository. None of these policy templates grants permissions for any SSM operation, so you can't use a SAM policy template to grant your AWS Lambda function access to SSM parameters as of now.
What you can do as a workaround is to manually add the required policy statement inline to your policies. That would look like:
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../
Handler: app.lambda
Runtime: nodejs8.10
Policies:
- DynamoDBReadPolicy:
TableName: !Ref Table
- KMSDecryptPolicy:
KeyId: !Ref Key
- Statement:
- Action:
- ssm:GetParameter
Effect: Allow
Resource: arn:aws:ssm:region:account-id:parameter/parameter_name
You should also consider opening a pull request for adding a policy template for SSM parameter access to SAM, as such a template would of course be a more convenient way to express such permissions. From my experience the developers are very friendly and always welcome such additions.
Update: There is a SSMParameterReadPolicy now available in AWS SAM, so instead of using the workaround you can now simply do:
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../
Handler: app.lambda
Runtime: nodejs8.10
Policies:
- DynamoDBReadPolicy:
TableName: !Ref Table
- KMSDecryptPolicy:
KeyId: !Ref Key
- SSMParameterReadPolicy:
ParameterName: parameter_name