Do we have evaluation order for AWS IAM? - amazon-web-services

I am trying to find out one problem with IAM policy of one of my user.
I have policy which says deny access if the service is not in our list. I am trying to grant access to my user for all action of apigateway. I have provided below policy. But it is denying access for all action other than GET, OPTIONS, HEAD. I have the wild character * in the beginning, will be individual actions override the wild character settings. I am trying to understand the evaluation order for IAM. Does the individual action override the *?
{
"Statement": [
{
"Resource": "*",
"Effect": "Deny",
"NotAction": [
"apigateway:*"
"apigateway:GET",
"apigateway:OPTIONS",
"apigateway:HEAD"
]
}
]
}

In AWS IAM, all requests are denied by default. So, you only need to grant permissions to the desired actions while all other actions will be denied by default.
When a request is made, the AWS service decides whether a given
request should be allowed or denied. The evaluation logic follows
these rules:
By default, all requests are denied. (In general, requests made using the account credentials for resources in the account are always allowed.)
An explicit allow overrides this default.
An explicit deny overrides any allows.
The order in which the policies are evaluated has no effect on the outcome of the evaluation. All policies are evaluated, and the result is always that the request is either allowed or denied.
See Determining Whether a Request is Allowed or Denied.
So, usually it should be enough to write your Allow policy as:
{
"Statement": [
{
"Resource": "*",
"Effect": "Allow",
"Action": [
"apigateway:GET",
"apigateway:OPTIONS",
"apigateway:HEAD"
]
}
]
}
However, if you suspect that other policies might be granting the user undesired actions, you could write your Deny policy as:
{
"Statement": [
{
"Resource": "*",
"Effect": "Deny",
"NotAction": [
"apigateway:GET",
"apigateway:OPTIONS",
"apigateway:HEAD"
]
}
]
}

This line was helpful from the AWS docs:
An explicit deny in any policy overrides any allows

Related

Writing AWS Organizations SCP to properly account for info in request that may not always exist?

Our organization has tightly restricted AWS Organization Service Control Policies (SCPs) around certain actions, one of which is around Resource Access Manager (RAM). We attempted to use the following policy which we hoped would allow us to associate any accounts in our Organization and particular OU unit as principals to resource shares, and also associate the particular types of resources, Transit Gateways and PrefixLists.
{
"Sid": "AllowSpecificPrincipalRAMActions",
"Effect": "Deny",
"Action": "ram:AssociateResourceShare",
"Resource": "*",
"Condition": {
"StringNotEqualsIfExists": {
"ram:Principal": [
"arn:aws:organizations::123456789:organization/o-xxxxxx",
"arn:aws:organizations::123456789:ou/o-xxxxxx/ou-xxxx-xxxxxx",
]
}
}
},
{
"Sid": "AllowSpecificRAMResourceActions",
"Effect": "Deny",
"Action": "ram:AssociateResourceShare",
"Resource": "*",
"Condition": {
"StringNotEqualsIfExists": {
"ram:RequestedResourceType": [
"ec2:TransitGateway",
"ec2:PrefixList"
]
}
}
}
The reason we need both conditions, is because request bodies for ram:AssociateResourceShare have different information depending on if a principal association is being made to the resource share, or a resource association; we cannot always guarantee an instance of ram:AssociateResourceShare has either a ram:Principal, or a ram:RequestedResourceType.
We were confused why the policy didn't work as intended, then noticed this caveat in AWS documentation:
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html
" If you are using an "Effect": "Deny" element with a negated condition operator like StringNotEqualsIfExists, the request is still denied even if the tag is missing."
In effect, StringNotEqualsIfExists is not behaving as we need it to within a Deny, if either the ram:Principal or ram:RequestedResourceType is missing from the request (which is expected, as described), it will be denied instead of ignored.
How can we write this SCP statement to match our intent, and allow us to freely associate both principals and requested resource types?

How to Create a Admin Group with AdministratorAccess access that CANNOT create new users or groups

I am new to AWS and find it unnecessarily disorganized and complicated.
I would like to give a developer access to the account at the AdministratorAccess level but limit that access by not allowing him to create additional users or groups. Without limiting this, he can create a user that has access to billing. I want to make sure no one has access to billing or can create users that can access billing.
How do I do that?
You can create a customer managed IAM policy based on Administrator Access and add an explicit Deny statement similar to the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"iam:CreateGroup",
"iam:CreateUser",
...
],
"Resource": "*"
}
]
}
Note: The above restrictions will not be sufficient, they only demonstrate the general principle. To effectively restrict users you would also have to deny actions that attach managed policies or put inline policies to users or groups and actions that change already attached policies.
In general, it is advisable to follow the Principle of Least Privilege and give users only the permissions that they actually need. Only in rare cases you should start with AdministratorAccess and then incrementally restrict the permissions. It is considered best practice to start with no permissions and then incrementally add what is needed.
P.S.: You could also implement a mechanism that automatically attaches the following policy to all users to effectively deny all cost explorer and billing-related actions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"aws-portal:*Billing",
"aws-portal:*Usage",
"aws-portal:*PaymentMethods",
"ce:UpdatePreferences",
"ce:CreateReport",
"ce:UpdateReport",
"ce:DeleteReport",
"ce:CreateNotificationSubscription",
"ce:UpdateNotificationSubscription",
"ce:DeleteNotificationSubscription",
"cur:DescribeReportDefinitions",
"cur:PutReportDefinition",
"cur:ModifyReportDefinition",
"cur:DeleteReportDefinition",
"purchase-orders:*PurchaseOrders"
],
"Resource": "*"
}
]
}

AWS IAM Policy grant permissions for some EC2 instances

I want to restrict access for a specific user to see just few EC2 instances. I created a new user in IAM Roles and I attached a new Policy to it. The content of that Policy is attached below. I tried to look over documentation and to do it myself like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": [
"arn:aws:ec2:eu-west-1:ACCOUNT_ID:instance/i-INSTANCE_ID1",
"arn:aws:ec2:eu-west-1:ACCOUNT_ID:instance/i-INSTANCE_ID2"
]
}
]
}
I placed my region,ACCOUNT_ID(the owner id, not of the new user created) and instance-id, but when I connect with that user and I go to list all Instances I got this An error occurred fetching instance data: You are not authorized to perform this operation..
After I placed the code in JSON editor, in Policy Review step I got this message:
This policy defines some actions, resources, or conditions that do not
provide permissions. To grant access, policies must have an action
that has an applicable resource or condition. For details, choose Show
remaining Learn more
The AWS documentation mention exactly the same configuration or these examples.
I assume you connect as that user in the console (but it would be the same with CLI) Here is what I think is happening:
To list all the instances, the console most probably calls the DescribeInstances API. As per the list of action/resources/tags that can be used in IAM policy, this API does not support the resource filter in IAM.
This means your user has no authorization to list instances and they will not be shown in the console. You can validate this theory by using the CLI to request the details of a specific instance id, if my hypothesis is correct, it will be authorized.
As DescribeInstances can not be restricted by resource or tags, I don't think it is possible to filter the instance list for a user.
To have the console working, you'll need to add the following statement in your IAM policy
"Statement": [
{ your existing statement },
{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
}
]
Please report if I was right :-) The example you mentioned in your question shows exactly that : Resources = * on DescribeInstances and Resources specific InstanceId on other operations.
The previous answer is wrong, you can Conditionally allow access to ec2:DescribeInstances by tag names. It's an AWS best practice as well. Also explicitly deny access to the ec2:CreateTags and ec2:DeleteTags actions to prevent users from creating or deleting tags to take control of the instance.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/UserName": "${aws:username}"
}
}
},
{
"Effect": "Deny",
"Action": [
"ec2:CreateTags",
"ec2:DeleteTags"
],
"Resource": "*"
}
]
}
DescribeInstances action does not support condition.
https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html

S3 Principal Bucket Policy Permissions

I have a cloudformation template up in an S3 bucket (the url follows the pattern but is not exactly equal to: https://s3.amazonaws.com/bucket-name/cloudform.yaml). I need to be able to access it from CLI for a bash script. I'd prefer that everybody in an organization (all in this single account) has access to this template but other people outside of the organization don't have access to the template. A bucket policy I've tried looks like:
{
"Version": "2012-10-17",
"Id": "Policy11111111",
"Statement": [
{
"Sid": "Stmt111111111",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::7777777777:root"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
}
With this policy, I and a couple other people in my office are unable to access the url. Even when I'm logged in with the root account I'm getting Access Denied.
Also, this change (only setting Principal to *) makes the bucket accessible to anybody:
{
"Version": "2012-10-17",
"Id": "Policy11111111",
"Statement": [
{
"Sid": "Stmt111111111",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
}
Obviously the signs point to my Principal field being misconfigured. 777777777 is the replacement for the Account ID I see under the My Account page.
So, do I need to worry about this on the IAM front? Considering that I am logged in as the root user, I'd guess I should have access to this as long as I put in a bucket policy. Any help would be much appreciated.
Short and sweet:
The bucket policy doesn't allow you to do what you want because of a wildcard limitation of the Principal element. Your best bet is to create an IAM group and put all IAM users into that group if they need access.
Long version:
Just to make it clear, any request to https://s3.amazonaws.com/bucket-name/cloudform.yaml MUST be signed and have the necessary authentication parameters or the request will be rejected with Access Denied. The only exception is if the bucket policy or the bucket ACL allows public access, but it doesn't sound like this is what you want.
When you say "everybody in an organization (all in this single account)" I assume you mean IAM users under the account who are accessing the file from the AWS console, or IAM users who are using some other code or tool (e.g. AWS CLI) to access the file.
So what it sounds like what you want is the ability to specify the Principal as
"Principal": {
"AWS": "arn:aws:iam::777777777777:user/*"
}
since that is what the pattern would be for any IAM user under the 777777777777 account id. Unfortunately this is not allowed because no wildcard is allowed in the Principal unless you use the catch-all wildcard "*". In other words "*" is allowed, but either "prefix*" or "*suffix" is not. (I wish AWS documented this better.)
You could specify every IAM user you have in the bucket policy like so:
"Principal": {
"AWS": [
"arn:aws:iam::777777777777:user/alice",
"arn:aws:iam::777777777777:user/bob",
"arn:aws:iam::777777777777:user/carl",
...
"arn:aws:iam::777777777777:user/zed",
}
But you probably don't want to update the policy for every new user.
It would be easiest to create an IAM group that grants access to that file. Then you would add all IAM users to that group. If you add new users then you'll have to manually add them to that group, so it is not as convenient as what you originally wanted.

Why does applying a condition to ec2:DescribeInstances in an IAM policy fail?

When trying to configure which instances can be listed using policies, I remark the following issue:
When the condition is not implemented, all instances are visible.
When any condition is implemented, nothing is visible.
The example policy with condition is included:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1461235889000",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances"
],
"Resource": [
"*"
],
"Condition": {
"StringEquals": {
"ec2:InstanceType": "r3.xlarge"
}
}
}
]
}
What is wrong here?
The ec2:DescribeInstances action does not support resource-level permissions or applying conditions.
From the linked documentation above:
...to use these actions in an IAM policy, you must grant users permission to use all resources for the action by using a * wildcard for the Resource element in your statement. You cannot use Amazon EC2 condition keys for these actions.
So your usage of the * wildcard without a condition is valid, but applying any condition (as of this writing) will unfortunately not work as expected.
Further Reading:
Supported Resource-Level Permissions for Amazon EC2 API Actions