I am fairly new in AWS specifics, so forgive me a stupid question. I was looking for the answers, but I did not find any question answering my issue.
I am building a project and I am trying to get the permissions done right using this AWS tutorial: https://aws.amazon.com/blogs/apn/partitioning-pooled-multi-tenant-saas-data-with-amazon-dynamodb/
I have created role and policy:
const tenantUserRole = new Role(this, "TenantUserRole", {
assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
});
table.grantReadWriteData(tenantUserRole);
Then, in my Lambda I am trying to:
const role = await stsClient.send(
new AssumeRoleCommand({
Policy: getPolicy("tenant"),
RoleArn: process.env.TENANT_ROLE_ARN,
RoleSessionName: "foo",
})
);
This does not work as I am getting an error in Lambda:
{ "errorType": "AccessDenied", "errorMessage": "User: arn:aws:sts::xxx:assumed-role/ProjectStack-createUserServiceRoleB9D8AADE-GRDI6MWXA5MY/ProjectStack-createUserC6ED88E6-K0S95UXTN9MH is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::xxx:role/ProjectStack-TenantUserRoleB79B8D3A-11N644X7UF0SR", "trace": [ "AccessDenied: User: arn:aws:sts::xxx:assumed-role/ProjectStack-createUserServiceRoleB9D8AADE-GRDI6MWXA5MY/ProjectStack-createUserC6ED88E6-K0S95UXTN9MH is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::xxx:role/ProjectStack-TenantUserRoleB79B8D3A-11N644X7UF0SR",
The target is to always resolve permissions based on the user context, so what should I do in my CDK stack to enable all my Lambdas to do what has to be done?
As described by @Maurice, the role should be trusted instead of the service principal(lambda.amazonaws.com
).
Below is the CDK code.
TenantUserRole
trusted by apiRole
which is being used by aws lambda
import * as cdk from '@aws-cdk/core';
import * as iam from '@aws-cdk/aws-iam';
export class CdkWorkshopStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// aws lambda role
const lambdaRole = new iam.Role(this, 'apiRole', {
roleName: 'apiRole',
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
// trusted by TenantUserRole for access
const tenantUserRole = new iam.Role(this, 'TenantUserRole', {
roleName: 'TenantUserRole',
assumedBy: new iam.ArnPrincipal(lambdaRole.roleArn),
});
// policy to allow assume role TenantUserRole
lambdaRole.addToPolicy(
new iam.PolicyStatement({
resources: [tenantUserRole.roleArn],
actions: ['sts:AssumeRole'],
})
);
}
}
Here it looks like in role's trust policy.
❯❯ aws iam get-role --role-name TenantUserRole
{
"Role": {
"Path": "/",
"RoleName": "TenantUserRole",
"RoleId": "AROA2WXKNDTKKGFADASD",
"Arn": "arn:aws:iam::1234567890:role/TenantUserRole",
"CreateDate": "2021-03-03T23:34:13+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::1234567890:role/apiRole"
},
"Action": "sts:AssumeRole"
}
]
},
"Description": "",
"MaxSessionDuration": 3600,
"RoleLastUsed": {}
}
}
Policy to allow aws lambda role to assume TenantUserRole
❯❯ aws iam get-role-policy --role-name apiRole --policy-name apiRoleDefaultPolicy771DC0DD
{
"RoleName": "apiRole",
"PolicyName": "apiRoleDefaultPolicy771DC0DD",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::1234567890:role/TenantUserRole",
"Effect": "Allow"
}
]
}
}
How to use trust policies with IAM roles
In lambda you need to after assuming the TenantUserRole
you need to use those credentials and create dynamodb
client and then use that client for making the api call putItem
import boto3
def lambda_handler(event, context):
sts = boto3.client('sts')
// assume TenantUserRole
creds = sts.assume_role(
RoleArn='arn:aws:iam::1234567890:role/TenantUserRole',
RoleSessionName="tempsession"
)
// Use Credentials from the assume call to make dynamodb client
tenant_user_dyanmodb_client = boto3.client(
'dynamodb',
aws_access_key_id=creds.get('Credentials').get('AccessKeyId'),
aws_secret_access_key=creds.get('Credentials').get('SecretAccessKey'),
aws_session_token=creds.get('Credentials').get('SessionToken')
)
// call put item
tenant_user_dyanmodb_client.put_item(TableName='fruitSalad', Item={'fruitName':{'S':'Banana'},'key2':{'N':'value2'}})