AWS IAM: Allow EC2 instance to stop itself - amazon-web-services

I'm trying to allow all EC2 instances in our AWS account to stop themselves (using an automated script that uses the aws cli). I try to do so by creating an AWS IAM role with the propper policy. However, I can't find how to define the policy to only Allow instances to stop itself (and not other instances).
I tried with the following policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:StopInstances"
],
"Resource": [
"${ec2:SourceInstanceARN}"
]
}
]
}
But on validation, this gives me the error This policy contains the following error: The following resources are invalid : ${ec2:SourceInstanceARN}
Is there a way to allow an instance to stop itself (and only itself)? If so, how should I do it?

Shutdown behavior solves the problem with termination but there might be other scenarios that require limited access to API requests (i.e. self tagging). So here's a solution with IAM policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:DeleteTags",
"ec2:DescribeTags",
"ec2:CreateTags",
"ec2:TerminateInstances",
"ec2:StopInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ARN": "${ec2:SourceInstanceARN}"
}
}
}
]
}

As Mark B suggested in the comments, I solved my problem by changing my script to use shutdown rather than aws ec2 stop-instances. This makes the use of any policy needless, as any system can execute shutdown on itself (and only itself).

Related

Is aws:SourceVpc condition key present in the request context when interacting with S3 over web console?

I have a Bucket Policy (listed below) that is supposed to prevent access to an S3 bucket when accessed from anywhere other than a specific VPC. I launched an EC2 instance in the VPC, tested and confirmed that S3 access works fine. Now, when I access the same S3 bucket over web console, I get 'Error - Access Denied' message.
Does this mean that aws:SourceVpc condition key is present in the request context when interacting with S3 over web console as well?
My assumption is that it is present in the request context as otherwise policy statement would have failed such that the statement's "Effect" does not apply because there is no "Ifexists" added to StringNotEquals - Asking this question as I could not find this information in AWS Documentation. Even after adding "Ifexists" to StringNotEquals, results are same - can someone confirm?
{
"Version": "2012-10-17",
"Id": "Policy1589385141624",
"Statement": [
{
"Sid": "Access-to-specific-VPC-only",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::abhxy12bst3",
"arn:aws:s3:::abhxy12bst3/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": "vpc-0xy915sdfedb5667"
}
}
}
]
}
Yes, you are right. I tested the following bucket policy, the operations from the AWS S3 console are denied.
{
"Version": "2012-10-17",
"Id": "Policy1589385141624",
"Statement": [
{
"Sid": "Access-to-specific-VPC-only",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::abhxy12bst3",
"arn:aws:s3:::abhxy12bst3/*"
],
"Condition": {
"StringLike": {
"aws:sourceVpc": "vpc-30*"
}
}
}
]
}
It means there is definitely some vpc id present in the request. It might be same for each account or it could be different.
This will apply to all requests interacting with S3. The console just provides a GUI on top of the AWS API.

Writing an IAM policy to restrict AWS SSM connections to EC2 instances by EC2 tags

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.

AWS EC2: IAM policy for ec2:RequestSpotInstances

I need to create policy that would allow user to create spot requests, but with specific subnet and security group only. This is what I did:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:RequestSpotInstances",
"Resource": [
"arn:aws:ec2:us-east-1:123456789012:image/ami-*",
"arn:aws:ec2:us-east-1:123456789012:subnet/subnet-af016c92",
"arn:aws:ec2:us-east-1:123456789012:subnet/subnet-12a34d3c",
"arn:aws:ec2:us-east-1:123456789012:subnet/subnet-f0e844cd",
"arn:aws:ec2:us-east-1:123456789012:subnet/subnet-026ae728",
"arn:aws:ec2:us-east-1:123456789012:key-pair/*",
"arn:aws:ec2:us-east-1:123456789012:security-group/sg-b5dd94cd",
"arn:aws:ec2:us-east-1:123456789012:security-group/sg-3bda8c42"
]
}
]
}
But my spot request creation still fails:
botocore.exceptions.ClientError: An error occurred (UnauthorizedOperation) when calling the RequestSpotInstances operation: You are not authorized to perform this operation.
What is the minimum subset of permissions for RequestSpotInstances action?
Is there some possibility to debug this?
I know this is an old issue, but I just ran across the same issue in my environment. The solution for me was adding an IAM permission for "PassRole"
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1479335761363",
"Action": [
"ec2:DescribeInstances",
"ec2:RequestSpotInstances",
"ec2:RunInstances",
"iam:PassRole"
],
"Effect": "Allow",
"Resource": "*"
}]
}
According to the EC2 docs (here), ec2:RequestSpotInstances is an action which falls into the category of "Unsupported Resource-Level Permissions." Unfortunately, you will have to set the resource tag to all resources, like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:RequestSpotInstances",
"Resource": [ "*" ]
}
]
}
As far as debugging goes, don't forget about the IAM policy simulator, which can be accessed from the AWS Console => IAM => User page.

Granting access to S3 resources based on role name

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).

how to limit instance launch by instance type in AWS using IAM service

I am using the policy to limit RunIstances only to a specific instance types and a specific region. When I run the launch wizard or simulation under a test user I am getting "implicitly denied" error.
Here is the policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:us-east-1::instance/*"
],
"Condition": {
"StringEquals": {
"ec2:InstanceType": [
"t1.micro",
"m1.small"
]
}
}
},
{
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:us-east-1::image/ami-*",
"arn:aws:ec2:us-east-1::subnet/*",
"arn:aws:ec2:us-east-1::network-interface/*",
"arn:aws:ec2:us-east-1::volume/*",
"arn:aws:ec2:us-east-1::key-pair/*",
"arn:aws:ec2:us-east-1::security-group/*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:CreateSecurityGroup",
"ec2:DeleteSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:CreateKeyPair"
],
"Resource": [
"*"
]
}
]
}
could somebody point to the issue?
In your resource blocks, insert an asterisk between the two ":" in the arn lines, to specify all accounts, or replace it with your account number.
"arn:aws:ec2:us-east-1:*:instance/*"
"arn:aws:ec2:us-east-1:*:image/ami-*",
"arn:aws:ec2:us-east-1:*:subnet/*",
"arn:aws:ec2:us-east-1:*:network-interface/*",
"arn:aws:ec2:us-east-1:*:volume/*",
"arn:aws:ec2:us-east-1:*:key-pair/*",
"arn:aws:ec2:us-east-1:*:security-group/*"
Please see the IAM policy generator tool. Your code does not look like the right syntax.
First, Allow all actions in EC2. Next, Deny specific actions in EC2.
Example to Allow all actions in EC2:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"NotAction": "ec2:*",
"Resource": "*"
}
]
}
Example to Deny creating resources in specific regions:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyTheseActions",
"Effect": "Deny",
"Action": [
"ec2:RunInstances",
"ec2:StartInstances"
],
"Resource": "arn:aws:ec2:us-west-1",
"Resource": "arn:aws:ec2:us-east-1",
"Resource": "arn:aws:ec2:eu-west-1",
"Resource": "arn:aws:ec2:sa-east-1",
"Resource": "arn:aws:ec2:ap-northeast-1",
"Resource": "arn:aws:ec2:ap-southeast-1",
"Resource": "arn:aws:ec2:ap-southeast-2"
}
]
}
Currently the easier way to control access to AWS regions, EC2 and RDS Instances sizes and types would probably be using IAM policies with the Condition (Optional) policy element – lets you specify conditions for when a policy is in effect.
We'll setup an AWS IAM Policy using a Condition statement, which will allow full AWS EC2 and RDS services, strictly for the 3 more cost effective -> AWS regions which are:
us-east-1 (North Virginia, USA)
us-east-2 (Ohio, USA)
us-west-2 (Oregon, USA)
All other regions will not be allowed, this policy also sets up a conditional access control for starting both EC2 and RDS instances. Instead of specifying all of the possible type/class instances to run, we use Deny effect in the statement, which allows for a short and simple policy limiting by instances sizes (micro, small, medium and large). This has the further effect of preventing any other policies from overriding the block.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MultiServiceFullAccessCustom",
"Effect": "Allow",
"Action": [
"ec2:*",
"rds:*"
],
"Resource": [
"*"
],
"Condition": {
"StringEquals": {
"aws:RequestedRegion": [
"us-east-1",
"us-east-2",
"us-west-2"
]
}
}
},
{
"Sid": "Ec2RunInstanceCustomSize",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:*:*:instance/*"
],
"Condition": {
"ForAnyValue:StringNotLike": {
"ec2:InstanceType": [
"*.nano",
"*.micro",
"*.small",
"*.medium",
"*.large"
]
}
}
},
{
"Sid": "RdsFullAccessCustomSize",
"Effect": "Deny",
"Action": [
"rds:CreateDBInstance",
"rds:CreateDBCluster"
],
"Resource": [
"arn:aws:rds:*:*:db:*"
],
"Condition": {
"ForAnyValue:StringNotLike": {
"rds:DatabaseClass": [
"*.micro",
"*.small",
"*.medium",
"*.large"
]
}
}
}
]
}
What does this policy not protect us from?
This does not impose limits on the size of instances deployed though other services - most importantly, through an auto-scaling group or EKS.
We are not limiting in any way the total number of instances spun up by a given user. The normal limits help here, but there isn't a way to determine how many instances are currently running through just policy.
We're not capping costs, just adding some protections. If you want to set alerts for cost overruns or spikes, you need to look into the Billing controls.
terraform-aws-cost-billing-alarm
terraform-aws-cost-budget
terraform-aws-lambda-nuke
terraform-aws-lambda-scheduler-stop-start
We do not require any Multi Factor Authentication (MFA) for the role switch. The assumption is that users logging in are already using some form of second factor for authentication. This is mostly due to the increased complexity around using the CLI with MFA, and the fact that the role change is only to allow an override, not to enable an elevation to another security domain. You might want to add the feature; it is not difficult to add, and can bring reasonable protections.
We are only looking at instance startup, but you may consider who can shutdown or terminate instances, as that may result in downtime or data loss.
Reference links
https://blog.vizuri.com/limiting-allowed-aws-instance-type-with-iam-policy
https://blyx.com/2016/03/24/how-to-restrict-by-regions-and-instance-types-in-aws-with-iam/
https://aws.amazon.com/blogs/security/easier-way-to-control-access-to-aws-regions-using-iam-policies/
https://medium.com/faun/100-days-of-devops-day-10-restricting-user-to-launch-only-t2-instance-509aaaec5aa2
https://aws.amazon.com/blogs/security/back-to-school-understanding-the-iam-policy-grammar/