I am trying to run some code inside a lambda function that will issue a signed URL that was signed with a set of temporary credentials that have a custom policy.
The basic idea being that the signature allows the user to connect to AWS IOT but only with a specific client ID and with restrictions to the subscription topics as well as received messages.
If I perform a signature using the credentials that are natively assigned to the Lambda function (i.e. before they are assumed by the Lambda function the code works). However, running it inside of the Lambda function does generate an assumed role and does generate a set of temporary credentials but the resulting signature will not establish a connection to the AWS IOT endpoint.
The basic code is:
AWSSecurityTokenService tokenService =
AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new EnvironmentVariableCredentialsProvider())
.build();
AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest();
assumeRoleRequest.setRoleSessionName(UidGenerator.generateId(16));
assumeRoleRequest.setRoleArn("arn:aws:iam::xxxxxxxxxxx:role/websocket_signer");
assumeRoleRequest.setDurationSeconds(60 * 60);
assumeRoleRequest.setPolicy(policy);
AssumeRoleResult assumeRoleResult = tokenService.assumeRole(assumeRoleRequest);
// Create signed url
WebSocketUrlSigner signer = new WebSocketUrlSigner(
this.request.getStage(),
assumeRoleResult.getCredentials().getAccessKeyId(),
assumeRoleResult.getCredentials().getSecretAccessKey());
The role that I am trying to assume websocket_signer
has no policies assigned to it natively but does have a trust relationship with arn:aws:sts::xxxxxxxxx:assumed-role/lambda_role/lambda_function_name
.
The role that is assigned to the Lambda function (before its assumed) has the following trust relationship:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
To eliminate any potential problems with the policy I am passing in the following policy dynamically to the assumed role (that should be being merged with the existing role policy which is nothing):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Connect",
"iot:Subscribe",
"iot:Receive"
],
"Resource": [
"*"
]
}
]
}
What I should be ending up with is a signed URL that can connect using any client ID, to any topic and receive any messages. There are no exceptions in the function and AWS returns everything correctly so I must have misconfigured something.
The permissions of the role and the passed policy are intersected, not merged: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html
If the starting role does not have any permission, then the same is true for the aasumed role.