I've been working with the AWS PHP SDK and I seem to get everything except the IAM Roles and permissions.
Can someone please explain to me in the simplest term how the IAM roles work and explain the following terms: StatementId, Action, ARN and most importantly Principal in simple English?
To give you the source of my confusion, here is a problem I recently faced. I'm trying to create an API Gateway in which a Resource's method triggers a Lambda function. It wasn't working until I copy pasted this bit:
$lambdaClient->addPermission([
'FunctionName' => 'fn name',
'StatementId' => 'ManagerInvokeAccess',
'Action' => 'lambda:InvokeFunction',
'Principal' => 'apigateway.amazonaws.com',
]);
But in some other thread someone suggested to use the following for the same:
const permissions = {
FunctionName: target,
StatementId: 'api-gateway-execute',
Action: 'lambda:InvokeFunction',
Principal: 'apigateway.amazonaws.com',
SourceArn: 'arn:aws:execute-api:' + nconf.get('awsRegion') + ':' + nconf.get('awsAccountId') + ':' + nconf.get('apiGatewayId') + '/*'};
How come the the first one doesn't contain any account info but The second one does? Also then there is another person who has pasted something totally different to get the same working for him. There are so many keys in the last example (like "Fn::Join"), I don't even know where to begin and what it does.
How does one figure out where to find these policies? Do we just copy-paste them from somewhere is there is a way to ascertain them. If so what keys must always be specified.
Any help will be appreciated because I'm totally confused right now.
First of all, Welcome to the world of AWS !!! :-D
Let me try to explain your doubts about how to understand IAM(in general) with an analogy.
Think that there is an organization called ORG1.
Deparments of ORG1: HR-dept, Test-dept, DEV-dept
Employees of ORG1: EMP1, EMP2, EMP3 ... EMP10
Members of HR dept: HR1, HR2, HR3
Now I want to create a role for HR dept to give them permission to hire/suspend an employee. The policy will look like below:
{
"Version": "2012-10-17", // This is version of the template. Don't change this. This is NOT a date field for your use.
"Statement": [
{
"Sid": "SOME-RANDOM-ID-WITH-NUMBER-1P1PP43EZUVRM", // This is used as ID in some cases to identify different statments
"Principal": HR-dept, // the dept who is allowed to assume this role or the one who is allowed to invoke this role
"Effect": "Allow", // has only 2 values: ALLOW/DENY. Either You want to provided the below privileges or you want to striped off these privileges.
"Action": [
"hire",
"suspend",
], // these are privileges which are granted
"Resource": "EMP1", // the entity on whom do you want to apply those actions on. In this case employee EMP1.
"Condition": {
"ArnLike": {
"AWS:SourceArn": "HR*" // You want anyone from HR-dept whose id starts with HR to be able to execute the action.ie HR1,HR2 or HR3 .
}
}
}
]
}
Now try to understand the below code from the same perspective(Internally this code creates a template similar to above):
const permissions = {
FunctionName: target,
StatementId: 'api-gateway-execute', // This is just an ID. Dont sweat about it.
Principal: 'apigateway.amazonaws.com', //which entity group the invoker belongs to
Action: 'lambda:InvokeFunction', // The privilege you are giving to API gateway api's
SourceArn: 'arn:aws:execute-api:.. blah blah blah' // ie. the exact Id of api-gateway which all has rights to invoke lambda function
};
In AWS ARN is a unique ID of a resource. Kind of like EmployeeId in a company.This is unique globally.
Believe me, At first it may seem that what you are trying to do in AWS is difficult to comprehend, But at some point you will start getting comfortable as you go on crossing each hurdle you face. And then you will admire at how customizable AWS features are.
How does one figure out where to find these policies?
You need to refer the AWS Documentation for specific service to find out what are the principals, actions and statements they support. For example if you need to find out policies for DynamoDB, check DynamoDB API Permissions. It can be confusing at first, since AWS Need to cater using IAM to authorize all of their services, but it becomes straight forward over time.
Let me explain each part of the policy
StatementId(Sid) - Its just and optional statement identifier (e.g 1, 2, abcd & etc.) and for some services(e.g SQS, SNS) it requires uniqueness.
Action - What your policy allows to do on a AWS Service. e.g For DynamoDB you can allow creating Tables, Putting new items & etc. For EC2 instance, it can allow starting and stopping.
ARN(Amazon Resource Name) - This is a unique name to uniquely identify AWS resources like a EC2 server, S3 bucket, DynamoDB table and even IAM policy, Role & etc.
Principal - Principal is to restrict who is allowed to use this policy. It can be a user (IAM user, federated user, or assumed-role user), AWS account, AWS service, or other principal entity that is allowed or denied access to a resource.
In addition you need to include Resource parameter, where you can either use a wildcard '*' or a ARN with Account ID within it.
I think most of the answers are correct but here it is from the horse's mouth/the great AWS document (full credit)
Role: An IAM role is an IAM identity that you can create in your account that has specific permissions.
Policies: IAM policies define permissions for an action regardless of the method that you use to perform the operation
Typically you have a role and you assign polices to your role.
To answer last part of your question "How does one figure out where to find these policies". This is all depends on what you are trying to do but always start with the least amount of permission (same concept as linux file permission don't give 777 ). How do you define your policies there are standard one already defined in your AWS account but you can use a tool to customize yours policies using the below tool
https://awspolicygen.s3.amazonaws.com/policygen.html
Related
The situation
I am generating a KMS Key in CloudFormation. According to the KMS policy documentation, it is crucial to create a policy where the Principal is the account itself, in order for IAM policies to be able to grant access to the key.
The question
How can I create the ARN for the account root in CloudFormation?
For those who use YAML for their CloudFormation templates:
!Sub arn:aws:iam::${AWS::AccountId}:root
The answer
{
"Fn::Join":[
":",
[
"arn:aws:iam:",
{
"Ref":"AWS::AccountId"
},
"root"
]
]
}
Why does this work?
First, let's examine the line, "Ref":"AWS::AccountId". This is a pseudo parameter reference, which is a fancy way of saying that it is a parameter that comes out of the box with CloudFormation. There are many such parameters. This one happens to give us the account ID, which is crucial for constructing the ARN.
Now, the rest is just the creation of an ARN using this account ID. Fn::Join is simply a CloudFormation built-in that allows concatenation of strings. This is crucial when combining references with string constants (or other references) as we are doing here.
The result is something like...
arn:aws:iam::123456789012:root
I'd like to create a user in IAM that can basically do anything (create, modify, delete) to resources that are created by that user itself.
This would include creation of other roles and policies...but again only such ones that would allow controlling resources created by the parent user itself.
The purpose is to be able to create a CloudFormation template that can be run by a non admin user but still create all resources required (including things like instance profiles and lambda execution roles). All such resources could then only be managed by the owning user, thus allowing for autonomy and isolation.
I have a feeling this could be accomplished with conditions in the policy document, but not sure exactly how.
This is by no means perfect solution, and I'm not sure if that is what you need, but I recently did something similar and this is how I solved it:
We have a role that has AmazonElasticMapReduceFullAccess and Cloudformation read-only policies (managed by amazon) and one additional custom policy with coloudformation:DeleteStack permission (for deleting the EMR cluster resources).
You can restrict IAM policies on resource level. For example, the custom policy for deleting stacks looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudformation:DeleteStack"
],
"Resource": "arn:aws:cloudformation:*:*:stack/EMRCluster*"
}
]
}
The way it works is that when the user needs an EMR cluster, they assume the role and create a stack named EMRCluster<date>-<UUID> and when they are done they remove the cluster resources using Cloudformation. This is, strictly speaking, not really necessary, since the user already has the EMR full access and can remove the resources (not only his) from EMR web console or via boto3 EMR API... It just makes things easier and allows the user to just do a single call to Cloudformation instead of managing EMR directly. It may look a bit funny to create and delete clusters with Cloudformation instead of directly, but it is much easier to manage a single JSON template than your custom configuration...
If you don't like that your user should have the entire EMR full access permission (quite a lot), I suggest that you play around with the EMR full access policy to allow user to create only certain resources and restrict removing resources in similar manner. Maybe you can give the user only a permission to call Cloudformation with certain template instead of that? I'm not sure if that would work without other permissions though...
Additionally, you can set VisibleToAllUsers=False in your template (see the docs), so only the user that created it should be able to manage the cluster.
Can someone explain to me the difference between an AWS Policy and an AWS Managed Policy in the context of Cloud Formation?
More specifically, I'm trying to define an auto scaling template where:
Each instance in an auto scale configuration is assigned an IAM Instance Role that has a policy.
The same policy is applied to the user when they try and access these instances.
I'm trying to keep duplication to a minimum and it seems like I may be able to achieve it via a Policy linked to a role, and group of users. The role can then be associated with EC2 Instance via instance profile and users can be added to the groups which in turn are assigned the policy.
Why and under what circumstances would one use a ManagedPolicy?
Thank you for your assistance.
EDIT: It seems like Role requires a policy document irrespective. So even having a separate policy won't really help? Or am I missing something?
AWS::IAM::Role only requires a trust policy. The Policy/Managed Policy can be defined separately.
The difference between AWS::IAM::ManagedPolicy and AWS::IAM::Policy is that AWS::IAM::ManagedPolicy does not require you to assign a Group, Role or User when defining it. AWS::IAM::Policy does. In your use case, you're probably fine using AWS::IAM::Policy.
If I may add, testing Policy creation using CDK v2.12.0, groups, users or roles are not required. iam.ManagedPolicy creates a policy you can share, iam.Policy is created as an inline policy.
new iam.Policy(this, 'testPolicy2', {
statements: policyDocs,
//groups: [s3UserGroup],
policyName: 'testPolicy2'
})
new iam.ManagedPolicy(this, 'testPolicy3', {
statements: policyDocs,
//groups: [s3UserGroup],
managedPolicyName: 'testPolicy3'
})
I'm starting to use AWS IAM instance roles which is great for leveling up our security management but it adds some challenges maintaining a lightweight development flow.
The problem at hand is:
I define an AWS instance role and attach a security policy to this role. This policy specifies the permissions granted for new EC2 instances launched under this role, it's a long and complicated json document with rules such as "allow access to S3 bucket x" and "deny access to SQS y". This works well on EC2.
Next, I'd like to let developers run our code on their local boxes, when developing locally in the IDE, and use the exact same security policy as defined earlier. This is important b/c when developers produce new code I want them to test it WRT to the exact same security policy as they'd run with in production. If they run with different security policy there's a chance things will slip.
The problem is that I haven't found a way to do this. It's possible to define IAM Groups, and join the developers into the groups (e.g. "developers" group). In this group I can define an IAM security policy which applies to all developers in this group. But there's no way (that I found) to reuse the policy attached to a group in the context of an instance role. Or to use the policy attached to a role in the context of a group. (did I totally miss this?...)
So to sum up, what I want is: 1) define a security policy document once. 2) reuse this document in two places, one is in IAM instance role definition and the other is IAM groups.
Not being able to do so means I'll have to maintain two copies of a complicated JSON document (which is out of version control) for each type of service (and we have many such services) and for each environment (e.g. stage/prod). I can see how this becomes a maintenance nightmare very easily.
So far the best thing I came up with is perhaps to store the policy documents on disk, under version control, and write a tool that uses the aws api to upload the policy document to the both the instance role and the group. It's somewhat cumbersome so I was hoping for a little more agility.
Do you have a better advice for me?... Thanks!
Thanks #Steffen for pointing out CloudFormation, but I think I found a solution that works better for me.
AWS provide a Security Token Service which in short among other things, allows you to Assume a Role.
This is exactly what I was looking for since I want to define a role once (e.g. a set of AWS permissions) and then let AWS EC2 instances assume this role automatically (easy to do) as well as developer assume a role for a specific service they are developing. The developers part is a bit more involved, but I'll paste some Java code that shows how to do this below.
First when defining a role you have to say which principals are allowed to assume this role. You do so by editing the Trust Relationships section of the role (at the bottom of a role's definition page on AWS web UI)
So for example here's a Trust Relationships document that allows EC2 instances assume this role, as well as some of the users in my domain (replace your-service-id-number and your-user#example.com):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::your-service-id-number:user/your-user#example.com",
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
And next the Java code that assumes this role when running in local development:
This code checks whether I'm running on an EC2 instance, and if so will assume the role of the instance (or otherwise, whatever's defined by the DefaultAWSCredentialsProviderChain, but in my case, and the best practice is - the EC2 instance role). If running on a dev environment, e.g. outside of EC2 then it assumes the role as provided by roleName
AWSCredentialsProvider getCredentialsProvider(String roleName) {
if (isRunningOnEc2()) {
return new DefaultAWSCredentialsProviderChain();
}
// If not running on EC2, then assume the role provided
final AWSCredentials longLivedCredentialsProvider = new DefaultAWSCredentialsProviderChain().getCredentials(); // user credentials provided by env variables for ex.
// The String roleArn is defined at the top of the Role page titled "Role ARN" ;-)
final String roleArn = String.format("arn:aws:iam::your-service-id-number:role/%s", roleName);
final String roleSessionName = "session" + Math.random(); // not sure it's really needed but what the heck..
final STSAssumeRoleSessionCredentialsProvider credentialsProvider =
new STSAssumeRoleSessionCredentialsProvider(longLivedCredentialsProvider, roleArn, roleSessionName);
return credentialsProvider;
}
The utility isRunningOnEc2() is provided:
public static boolean isRunningOnEc2() {
try {
final InetAddress byName = InetAddress.getByName("instance-data");
return byName != null;
} catch (final UnknownHostException e) {
return false;
}
}
Using CloudFormation as Steffen suggested, might be useful in the general sense as well, mostly in order to retain consistency b/w my codebase and the actual AWS deployment, but that's something else.
One annoying point though: It's possible to define principals as actual usernames but not as Group of users, so you cannot actually say that "all developers are allowed to assume role x", but rather you have to list each and every developer specifically. This is pretty annoying, but I guess I'm not the only one with this complaint.
Update
AWS has just introduced Managed Policies for AWS Identity & Access Management, which provide a fresh approach to sharing and maintaining IAM policies across IAM entities, specifically aimed at reducing the current duplication of information and effort:
As the size and complexity of your AWS installation grows, you might find yourself editing multiple permission documents in order to add a new permission or to remove an existing one. Auditing and confirming permissions was also more difficult than necessary.
The security blog post An Easier Way to Manage Your Policies provides a walk through with more details.
The new feature is can be used via the AWS Management Console and the AWS Command Line Interface (AWS CLI) as usual (but presumably not via AWS CloudFormation yet).
Initial Answer
So far the best thing I came up with is perhaps to store the policy documents on disk, under version control, and write a tool that uses the aws api to upload the policy document to the both the instance role and the group. It's somewhat cumbersome so I was hoping for a little more agility.
A tool like this already exists and is in fact a major backing technology for many of AWS' own and 3rd party services - have a look at AWS CloudFormation, which gives developers and systems administrators an easy way to create and manage a collection of related AWS resources, provisioning and updating them in an orderly and predictable fashion.
More specifically, the AWS::IAM::Policy resource associates an IAM policy with IAM users, roles, or groups:
{
"Type": "AWS::IAM::Policy",
"Properties": {
"Groups" : [ String, ... ],
"PolicyDocument" : JSON,
"PolicyName" : String,
"Roles" : [ String, ...
"Users" : [ String, ... ],
}
}
There's much more to CloudFormation of course, it's a very powerful tool (see Getting Started with AWS CloudFormation).
I'm trying to constrain the images which a specific IAM group can describe. If I have the following policy for my group, users in the group can describe any EC2 image:
{
"Effect": "Allow",
"Action": ["ec2:DescribeImages"],
"Resource": ["*"]
}
I'd like to only allow the group to describe a single image, but when I try setting "Resource": ["arn:aws:ec2:eu-west-1::image/ami-c37474b7"], I get exceptions when trying to describe the image as a member of the group:
AmazonServiceException Status Code: 403,
AWS Service: AmazonEC2,
AWS Request ID: 911a5ed9-37d1-4324-8493-84fba97bf9b6,
AWS Error Code: UnauthorizedOperation,
AWS Error Message: You are not authorized to perform this operation.
I got the ARN format for EC2 images from IAM Policies for EC2, but perhaps something is wrong with my ARN? I have verified that the describe image request works just fine when my resource value is "*".
Unfortunately the error message is misleading, the problem is that Resource-Level Permissions for EC2 and RDS Resources aren't yet available for all API actions, see this note from Amazon Resource Names for Amazon EC2:
Important
Currently, not all API actions support individual ARNs; we'll add support for additional API actions and ARNs for additional Amazon EC2 resources later. For information about which ARNs you can
use with which Amazon EC2 API actions, as well as supported condition
keys for each ARN, see Supported Resources and Conditions for Amazon
EC2 API Actions.
In particular, all ec2:Describe* actions are absent still from Supported Resources and Conditions for Amazon EC2 API Actions at the time of this writing, which implies that you cannot use anything but "Resource": ["*"] for ec2:DescribeImages.
The referenced page on Granting IAM Users Required Permissions for Amazon EC2 Resources also mentions that AWS will add support for additional actions, ARNs, and condition keys in 2014 - they have indeed regularly expanded resource level permission coverage over the last year or so already, but so far only for actions which create or modify resources, but not any which require read access only, something many users desire and expect for obvious reasons, including myself.