Reputation: 7679
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!
Upvotes: 3
Views: 3269
Reputation: 64741
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.
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).
Upvotes: 2
Reputation: 7679
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 [email protected]
):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::your-service-id-number:user/[email protected]",
"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.
Upvotes: 2