s3-credentials
s3-credentials copied to clipboard
Command for creating roles
If you want to access S3 from a Lambda function, AWS recommend you create a dedicated role that the Lambda function can then use. But... you still need to attach JSON policies to it!
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-execution-role-s3-bucket/
A similar mechanism is available for EC2 instances: https://aws.amazon.com/premiumsupport/knowledge-center/ec2-instance-access-s3-bucket/
So a command which can create a role using the same --read-only
and suchlike options as the other commands would be really useful.
Maybe something like:
s3-credentials create-role name-of-role name-of-bucket1 name-of-bucket2 --read-only
Probably need a list-roles
command too for this, which could get a bit weird because it will list roles outside of the domain of S3 buckets.
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html#IAM.Client.create_role
response = client.create_role(
Path='string',
RoleName='string',
AssumeRolePolicyDocument='string',
Description='string',
MaxSessionDuration=123,
PermissionsBoundary='string',
Tags=[
{
'Key': 'string',
'Value': 'string'
},
]
)
Looks like only RoleName
and AssumeRolePolicyDocument
are required (the latter is the JSON policy string).
Tempted to add a --tag key value
multi option which can be used to add tags to the created role - maybe add that to other commands for e.g. tagging the bucket created by s3-credentials create
as well.
If you run this command twice:
s3-credentials create-role name-of-role name-of-bucket1 --read-only
s3-credentials create-role name-of-role name-of-bucket1 name-of-bucket2
Should it redefine the name-of-role
role to instead grant read-write access to those two buckets - essentially replacing the JSON policy for the existing role?
I think it should... but it should maybe show an interactive warning that you are about to replace an existing role (since the command is called create-role
) - and -y
can be used to automatically say yes to that prompt.
I need to try this out myself first - ideally by shipping a lambda function that can read/write to an S3 bucket via a role created using the procedure I would use to implement this command.
Maybe name the command role
instead of create-role
to solve for that confusion about whether it's creating a new role or updating an existing one.
Current list of commands from s3-credentials --help
:
create Create and return new AWS credentials for specified...
delete-user Delete specified users, their access keys and their...
get-object Download an object from an S3 bucket
list-bucket List content of bucket
list-buckets List buckets - defaults to all, or pass one or more...
list-user-policies List inline policies for specified user
list-users List all users
policy Generate JSON policy for one or more buckets
put-object Upload an object to an S3 bucket
whoami Identify currently authenticated user
I think role
would fit with the existing commands well.
If I do this it would be useful to be able to try out the newly created roles with the list-bucket
and get-object
and put-object
commands.
They could grow a --role
option which uses STS.assume_role()
to assume the role and then performs their actions under that role.
Also maybe have a way of returning temporary authentication credentials for a specified role. Something like this:
s3-credentials auth-role name-of-role 15m
# Returns JSON or INI credentials
Could also offer an option to s3-credentials role
which causes it to output temporary credentials once the role has been created - not sure what I would call that option though since --auth
is already taken. Probably better to leave it as a separate command.
This is beginning to make the create
command look like it has the wrong name. Might fix that and set up the old name as an alias.
Annoying that assume_role()
requires an ARN, not just a role name - something like RoleArn='arn:aws:iam::123456789012:role/demo'
- so code will have to look up that (maybe construct it using the account ID) if it's going to allow users to use the roles name directly with --role
.
AssumeRolePolicyDocument
is not what I thought it was. I thought it was where you put the kind of policy document I've been generating for this tool - but actually looking at examples using my list-roles
prototype command they ALL look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "access-analyzer.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
So they aren't policies for what the role is allowed to do - they are policies that control who or what is allowed to assume that role.
Re-reading the docs, that field is described as "The trust relationship policy document that grants an entity permission to assume the role." - but it's also a required field! So you can't create a role without having that document. I guess that means the s3-credentials role
command needs a --service access-analyzer.amazonaws.com
option or similar to help populate the assume role policy?
OK, it looks like you have to create a role, then attach policies to it using https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html#IAM.Client.attach_role_policy
response = client.attach_role_policy(
RoleName='string',
PolicyArn='string'
)
That requires a policy ARN - but the docs say:
Use this operation to attach a managed policy to a role. To embed an inline policy in a role, use PutRolePolicy . For more information about policies, see Managed policies and inline policies in the IAM User Guide .
So it looks like I should use put_role_policy()
instead: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html#IAM.Client.put_role_policy
response = client.put_role_policy(
RoleName='string',
PolicyName='string',
PolicyDocument='string'
)
The PolicyName
doesn't matter, similar to the code I wrote here:
https://github.com/simonw/s3-credentials/blob/722bf523be83ec78df43fb7b88b86f8cf68b557c/s3_credentials/cli.py#L421-L424
https://github.com/simonw/s3-credentials/blob/722bf523be83ec78df43fb7b88b86f8cf68b557c/s3_credentials/cli.py#L446-L450
Looking at my existing code, I have this: https://github.com/simonw/s3-credentials/blob/7fb4db197820851db353a90d38499ece488e73d2/s3_credentials/cli.py#L727-L748
Where did I get that AssumeRolePolicyDocument
JSON from? That assigns a "Principal" of "AWS": "arn:aws:iam::{}:root".format(account_id)
?
That came out of my research in:
- https://github.com/simonw/s3-credentials/issues/26#issuecomment-964691114
After puzzling over the AssumeRolePolicyDocument
for a while, I tried creating a role using the AWS web console and then retrieving that role definition using get_role()
- which is how I found that roles created directly within that console use that as their AssumeRolePolicyDocument
.
A useful clue: in the web console it's called trusted entities, as seen in this screenshot:
Here's what the AWS CLI create-role
command looks like: https://docs.aws.amazon.com/cli/latest/reference/iam/create-role.html#create-role
create-role [--path <value>] --role-name <value> --assume-role-policy-document <value> [--description <value>] [--max-session-duration <value>] [--permissions-boundary <value>] [--tags <value>] [--cli-input-json <value>] [--generate-cli-skeleton <value>]
Design question: what should the options to the command be for generating the AssumeRolePolicyDocument
?
I think the following:
-
--root
- adds"AWS": "arn:aws:iam::{}:root".format(account_id)
-
--service lightsail.amazonaws.com
- can be used multiple times, with different service names -
--assume-role-policy POLICY
- for completely custom policies, similar tos3-credentials create --policy POLICY
- which accepts a filename or-
or a JSON literal.
At least one of these three options is required, or the command returns an error.
Detailed documentation about what can go in "Principal"
here: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html
Multiple values can be provided, for example:
"Principal": {
"AWS": [
"arn:aws:iam::AWS-account-ID:user/user-name-1",
"arn:aws:iam::AWS-account-ID:user/user-name-2"
]
}
"Principal": {
"Service": [
"ecs.amazonaws.com",
"elasticloadbalancing.amazonaws.com"
]
}
Presumably it's possible to have both AWS
and Service
keys, though I would have to test that to make sure.
I could offer --arn "arn:aws:iam::AWS-account-ID:user/user-name-1"
as an option for enabling the role for specific ARNs - in this case specific user IDs.