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
Related
So here is the situation:
I have a Cloudformation that creates CodeCommit repositories with some extra resources for other devops processes to work.
I got the requeriment to block users from doing a push to a specific branch, in this case master, I have found the policy that does that. source: https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-conditional-branch.html
So I write a role and policy with the following:
Resources:
CodeCommitRepository:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Sub '${ProjectCode}-${ProjectName}-${ComponentName}'
RepositoryDescription: !Ref CodeCommitRepositoryDescription
Tags:
- Key: fdr:general:project-code
Value: !Ref ProjectCode
- Key: fdr:general:project-name
Value: !Ref ProjectName
DenyPushRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${ProjectCode}-${ProjectName}-${ComponentName}-DenyPush-Role'
ManagedPolicyArns:
- !Ref DenyPushToMasterPolicy
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- codecommit.amazonaws.com
DenyPushToMasterPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub '${ProjectCode}-${ProjectName}-${ComponentName}-DenyPush-Policy'
Description: Policy to deny push to master
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- codecommit:GitPush
- codecommit:PutFile
- codecommit:DeleteBranch
- codecommit:MergePullRequestByFastForward
Effect: Deny
Resource: !GetAtt CodeCommitRepository.Arn
Condition:
StringEqualsIfExists:
codecommit:References:
- refs/heads/master
'Null':
codecommit:References: 'false'
As I understand which I wouldn't say is much, by creating the Role with the Policy and the sts:AssumeRole I thought that any user using that repository will assume that role that denys them the ability to push to master but that wasn't the case.
I guess that we may be overcomplicating things and we should put that policy unto all users directly on IAM but the idea is to have it done very granular. What am I doing wrong or is it even possible?.
Best regards
DenyPushRole is not for any users. You specified it to be only for codecommit.amazonaws.com which is incorrect.
Users do not automatically assume any roles. They have to explicitly assume your DenyPushRole using AssumeRole API call. Your users must also have permission to sts:AssumeRole.
Thus your role, in a general form, should be:
DenyPushRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${ProjectCode}-${ProjectName}-${ComponentName}-DenyPush-Role'
ManagedPolicyArns:
- !Ref DenyPushToMasterPolicy
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
AWS:
- !Ref AWS::AccountId
Once the role exist, and the users have sts:AssumeRole to assume it, they will use the AssumeRole command to actually assume the role. This will give then new, temporary AWS credentials to perform any actions specified by the role. In your case, the role only denies, so they will not be able to do anything anyway. You would need to add some allow statements to the role for your uses to be actually able to do something, not only deny.
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?
I am trying to access RDS mysql database via lambda function. I am deploying as SAM template. I have a lambda function attached to an execution role as the following:
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: "/"
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- rds:*
Resource: "*"
CreateTaskFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./components/lambdaFunctions/createTask
Handler: createTask.handler
Runtime: nodejs12.x
Role: !GetAtt LambdaExecutionRole.Arn
Timeout: 500
Events:
ProxyApiRoot:
Type: Api
Properties:
RestApiId: !Ref ApiGatewayApi
Path: /
Method: ANY
ProxyApiGreedy:
Type: Api
Properties:
RestApiId: !Ref ApiGatewayApi
Path: /{proxy+}
Method: ANY
Layers:
- !Ref NodeModulesLayer
After deploying the stack the lambda can't connect to RDS, and I found only the cloudwatch logs roles in the permission section of lambda:
As you see the RDS permission is not listed. Any suggestions?
Oh my bad. I figured it out, it was a VPC issue. Lambda has to be attached to a VPC, and a security group that is allowed by the security group of the database.
I have to add retention policy to API Gateway Cloudwatch logs, hence I cannot use the aws provided policy to do so i.e. arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
So instead I created my own role with custom policy :
ApiGatewayCloudWatchLogsRole:
Type: 'AWS::IAM::Role'
DependsOn: APIGFunctionLogGroup
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action: 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: APIGatewayPushLogsPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
Effect: Allow
Action:
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
- 'logs:DescribeLogGroups'
- 'logs:DescribeLogStreams'
- 'logs:GetLogEvents'
- 'logs:FilterLogEvents'
Resource: '*'
And then created LogGroup with retention as :
APIGFunctionLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
RetentionInDays: 30
LogGroupName: !Join
- ''
- - API-Gateway-Execution-Logs_
- !Ref MyRestApi
And passed the above created role to AWS::ApiGateway::Account
ApiGatewayAccount:
Type: 'AWS::ApiGateway::Account'
DependsOn: APIGFunctionLogGroup
Properties:
CloudWatchRoleArn: !GetAtt
- ApiGatewayCloudWatchLogsRole
- Arn
But while deploying my API Gateway I am getting error as :
I have the trust policy as well but API Gateway Account is not getting created.
If you create the log group yourself, before APIgateway does you should be able to use the existing policy/service role.
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.