IAM policy variables are quite cool and let you create generic policys to, for example, give users access to paths in an S3 bucket based on their username, like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["s3:GetObject","s3:PutObject","s3:DeleteObject"],
"Effect": "Allow",
"Resource": "arn:aws:s3:::fooCorp-user-files/${aws:username}/*"
},
{
"Action": "s3:ListBucket",
"Effect": "Allow",
"Resource": "arn:aws:s3:::fooCorp-user-files"
}
]
}
My question is, how can this be done using roles (attached to EC2 instances) instead of user accounts?
I have a number of app servers with unique IAM user accounts that are linked to a generic policy similar to the one above. This isolates the files accessible by each user/app without creating multiple policies.
I want switch these servers to use roles instead but there doesn't seem to be an equivalent IAM variable like aws:rolename.
The docs indicate that when using a role assigned to an EC2 instance the aws:username variable isn't set and aws:userid is [role-id]:[ec2-instance-id] (which isn't helpful either).
This really seems like something you should be able to do.. or am I coming at this the wrong way?
I've been looking for the same and after a lot of searching my conclusion was that it is not possible to use the role name as a variable in a IAM policy (I'd love to be proven wrong though).
Instead, I tagged my role with a name and ended up with this:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["s3:GetObject","s3:PutObject","s3:DeleteObject"],
"Effect": "Allow",
"Resource": "arn:aws:s3:::fooCorp-user-files/${aws:PrincipalTag/name}/*"
},
{
"Action": "s3:ListBucket",
"Effect": "Allow",
"Resource": "arn:aws:s3:::fooCorp-user-files"
}
]
}
(Cross-posted to AWS S3 IAM policy for role for restricting few instances to connect to S3 bucket based in instance tag or instance id)
Instead of using aws:SourceArn, use aws:userid!
The Request Information That You Can Use for Policy Variables documentation that you mentioned has a table showing various values of aws:userid including:
For Role assigned to an Amazon EC2 instance, it is set to role-id:ec2-instance-id
Therefore, you could use the Role ID of the role that is used to launch the Amazon EC2 instance to permit access OR the Instance ID.
For example, this one is based on a Role ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID123",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"aws:userid": [
"AROAIIPEUJOUGITIU5BB6*"
]
}
}
}
]
}
Of course, if you are going to assign permission based on a Role ID, then you can just as easily grant permissions within the Role itself.
This one is based on an Instance ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID123",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"aws:userid": [
"*:i-03c9a5f3fae4b630a"
]
}
}
}
]
}
The Instance ID will remain with the instance, but a new one will be assigned if a new instance is launched, even from the same Amazon Machine Image (AMI).
Related
In the CI/CD section of the AWS SAM tutorial workshop, when I ran
sam pipeline init --bootstrap and went through the configurations, a role was created with this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "*",
"Resource": "*",
"Effect": "Allow"
}
]
}
Doesn't this grant the role complete permission over my AWS account which is a big no no? Or is it fine because the permission is granted to an AWS service, and not a user?
This is the trust relationship:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudformation.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Having a role that exists with those permissionsis fine.
When you create a vanilla AWS Account (in other words I am not including those created by enterprise landing zones like Control Tower) it comes with a policy called AdministratorAccess and a role called Administrator.
The best practice is in who or what you allow to use that policy and when.
Roles are preferred over users, since roles provide security credentials. With a user you have durable credentials you need to secure.
In this case you are allowing CloudFormation to assume this role. This makes sense since CloudFormation often needs to be able to create and modify any resources including IAM roles. If you know you will not be creating or modifying IAM resources you can user a more restrictive role (least privilege), for example using the PowerUserAccess policy which looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"NotAction": [
"iam:*",
"organizations:*",
"account:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:CreateServiceLinkedRole",
"iam:DeleteServiceLinkedRole",
"iam:ListRoles",
"organizations:DescribeOrganization",
"account:ListRegions"
],
"Resource": "*"
}
]
}
I'd like to be able to all users to connect to EC2 instances using the AWS SSM (Systems Manager) 'startsession' command, but restrict which ones they can connect to through tags on the EC2 instances. IAM users belonging to a 'webserver-dev' group would have a policy allowing them to aws ssm start-session --target i-12341234 to any EC2 instance with a tag name of 'SSMTag' and a value of 'WebServer'. Any users in the devserver-dev group would be able to connect to instances with SSMTag = 'DevServer', etc.
I have a policy that allows access to connect to any EC2 instances, but as soon as I add in a 'condition' clause to the policy JSON, access is always denied (or always allowed).
I've tried adding conditions with various different syntaxes for the policy, aws:TagKeys, ssm:ResourceTag, ec2:ResourceTag, and a few others, but none seem to allow me to do what I want. The documentation seems to indicate that I can do exactly this, but either I don't understand how tagging works, or am misunderstanding the documents.
My current policy for development servers looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringEquals": {
"ssm:ResourceTag/SSMTag": "DevServer"
}
}
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "ssm:TerminateSession",
"Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
}
]
}
I've also tried for the condition line variations on:
"Condition": {
"StringEquals": {
"aws:ResourceTag/SSMTag": "DevServer"
}
}
and
"Condition": {
"ForAllValues:StringEquals": {
"ec2:ResourceTag:SSMTag": "DevServer"
}
}
What I want is if the user is not part of the webserver-dev group they cannot run aws ssm start-session and connect to any ec2 instances unless they are tagged with a tag SSMTag with the value of WebServer.
The results are either the user that is part of the group that the policy is attached to either gets access denied, or is allowed to connect to any instance, regardless of the tags attached to it.
I've read a lot of solutions to similar issues are basically "some functions don't support resource level tagging, but the documentation seems to explicitly say that it does.
I got an email from AWS support and it looks like it was an issue with the way with the "StringEquals" part of the condition. This policy works correctly:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"ForAnyValue:StringEqualsIfExists": {
"ssm:resourceTag/SSMTag": "DevServer"
}
}
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "ssm:TerminateSession",
"Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
}
]
}
The syntax difference being "ForAnyValue:StringEqualsIfExists": {
Hope this helps someone in the future.
I need to create a cross account role to access the resources of S3 bucket from another aws account that I owns.
Please help me to implement this using the cross account IAM role without using Access or secret keys.
Let's say you have:
Role A in Account A
Instance A in Account A that is associated with Role A
Bucket B in Account B
You wish to allow an application on Instance A to access the content of Bucket B.
The Request Information That You Can Use for Policy Variables documentation has a table showing various values of aws:userid including:
For Role assigned to an Amazon EC2 instance, it is set to role-id:ec2-instance-id
Therefore, you could use the Role ID of the role associated with the Amazon EC2 instance to permit access OR the Instance ID.
For example, this bucket policy is based on a Role ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID123",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::MYBUCKET",
"arn:aws:s3:::MYBUCKET/*"
],
"Principal": "*",
"Condition": {
"StringLike": {
"aws:userid": [
"AROAIIPEUJOUGITIU5BB6*"
]
}
}
}
]
}
To obtain the Role ID, use:
aws iam get-role --role-name ROLENAME
This bucket policy is based on an Instance ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID123",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::MYBUCKET",
"arn:aws:s3:::MYBUCKET/*"
],
"Principal": "*",
"Condition": {
"StringLike": {
"aws:userid": [
"*:i-03c9a5f3fae4b630a"
]
}
}
}
]
}
The Instance ID will remain with the instance, but a new one will be assigned if a new instance is launched, even from the same Amazon Machine Image (AMI).
Of course, you'd probably want to restrict those permissions to just s3:GetObject rather than s3:*.
(This answer based on Granting access to S3 resources based on role name.)
I have an IAM user that launches a CloudFormation stack containing an
- EC2 Instance with an
- IAM Instance Profile associated with an
- IAM Role
in the AWS::CloudFormation::Init block, the EC2 instance performs some actions that require it to call some ec2:* API actions. However, this instance should ONLY be able to call these actions for that instance itself.
The user that launches the stack has the permission to attach only a set of predefined policies and to create roles. Something like this
"CloudFormationStackLauncher": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"Description": "Allows attached entity to attach and detach required policies from roles it creates.",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:AttachRolePolicy",
"iam:DetachRolePolicy"
],
"Resource": "*",
"Condition": {
"ArnEquals": {
"iam:PolicyArn": [
"arn:aws:iam:::policy/InstanceThatCanManageItself",
]
}
}
},
{
"Effect": "Allow",
"Action": [
"iam:CreateRole"
],
"Resource": "*"
}
]
}
}
}
So I need a definition for the policy InstanceThatCanManageItself (which needs to be defined ahead of time by a user with full admin permissions). Ideally, it would look something like:
{
"Effect": "Allow",
"Action": [
"ec2:*"
],
"Resource": [
"${ec2:SourceInstanceARN}"
]
}
But it says this policy isn't valid because the policy variable ec2:SourceInstanceARN isn't in the format of a valid ARN. I've tried using tags on the EC2 instance and adding Conditions to the policy, but it doesn't seem to work when the condition is dynamic, like this:
{
"Effect": "Allow",
"Action": [
"ec2:*"
],
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"ec2:ResourceTag/role" : "${aws:userid}"
}
}
}
in the above, I'm dynamically adding a tag to the launched EC2 instance with the format "RoleId:InstanceId" as defined for the value specified for {aws:userid}, based on the description here: http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html. This approach validates, but doesn't work...either because it's dynamic...or because the action types aren't supports for the ResourceTag context key maybe...
Is there any way to accomplish this?
Thanks.
Resource tag-based authorizations will work only for certain operations. See, for example: EC2 Supported IAM actions. For example, all Describe operations are not supported and would have to be permissioned via a separate policy statement.
As an example of operations that support resource tags, attaching/detaching volumes (see same link above for supported operations and their requirements), so the following policy would work:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:DetachVolume"
],
"Resource": "*",
"Condition": {
"StringLike": {
"ec2:ResourceTag/policyuser": "${aws:userid}"
}
}
}
]
}
, provided both the volume and ec2 instance are tagged with tag 'policyuser' and value equal to role-id:ec2-instance-id (see IAM User Guide Reference Policy Variables), where role-id is the unique identifier of the role, obtained via e.g.
aws iam get-role --role-name rolename
I have a AWS S3 already associated with all the instances for read privileges to all S3 buckets. Now I need to add a policy to the roles for write privileges(Put object) so that a few of these instances can have write permissions to certain folders in the S3. Is there any way to achieve it through instance tag(better option for me) or instance id.
I tried adding an IAM policy but when I set the condition, my instances are not getting the required privileges.
The IAM policy I used is:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1456567757624",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::testbucket/testfolder1/*",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:ec2:eu-west-1:<accountno>:instance/<instanceid1>"
}
}
},
{
"Sid": "Stmt1456567757625",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::testbucket/testfolder2/*",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:ec2:eu-west-1:<accountno>:instance/<instanceid2>"
}
}
}
]
}
Here's an alternative, based on hints given in Granting access to S3 resources based on role name...
Instead of using aws:SourceArn, use aws:userid!
The Request Information That You Can Use for Policy Variables documentation has a table showing various values of aws:userid including:
For Role assigned to an Amazon EC2 instance, it is set to role-id:ec2-instance-id
Therefore, you could use the Role ID of the role that is used to launch the Amazon EC2 instance to permit access OR the Instance ID.
For example, this one is based on a Role ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID123",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"aws:userid": [
"AROAIIPEUJOUGITIU5BB6*"
]
}
}
}
]
}
Of course, if you are going to assign permission based on a Role ID, then you can just as easily grant permissions within the Role itself.
This one is based on an Instance ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID123",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"aws:userid": [
"*:i-03c9a5f3fae4b630a"
]
}
}
}
]
}
The Instance ID will remain with the instance, but a new one will be assigned if a new instance is launched, even from the same Amazon Machine Image (AMI).
The IAM Policy Elements Reference documentation says:
aws:SourceArn – To check the source of the request, using the Amazon Resource Name (ARN) of the source. (This value is available for only some services.)
However, the documentation does not state which services can use it.
There are examples available for its use with SQS and SNS, with a sourceARN of an Amazon S3 bucket and also using sourceARN with Lambda. However, it does not appear to be supported with Amazon EC2.