Angular
Securing Amazon EC2 Instances - Controlling External (Application) Access

Working With IAM Roles

PRO
Outline

Now I'll demonstrate that IAM role I described earlier. That is, I'll assign a role to an EC2 instance that'll allow an application running on the instance to read and write files in a specified S3 bucket. Why just one bucket? After all, there should be nothing happening on this instance that works against my account's interests. Well that's true, but this is an excellent illustration of the principle of least privilege and least access, which pushes us to allow users and entities only the very minimum of authority that they need to do their job. Even if it's hard to visualize something going wrong with a permission like account-wide S3 access, if we get into the habit of splashing it around, it'll eventually come back to bite us.

I'll begin by creating a JSON-formatted file on my local machine called ec2-role-trust-policy.json. What's a trust policy? It's a statement of which account principals are trusted to assume the role we're defining. In this case, it covers EC2 instances.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "ec2.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }
  ]
}

I'll then create a new role using the create-role command. I'll name the role S3access and then point to that trust policy file.

aws iam create-role \
   --role-name S3access \
   --assume-role-policy-document file://ec2-role-trust-policy.json

Next, I'll create the policy that'll make up the second half of our role that'll define permissions. The file should be named ec2-role-access-policy.json. Where did this content come from? You can generate templates from the AWS console or describe what you want to do in an internet search. The good news is that basic templates are all over the place and you just need to copy one, edit it for details, and paste it. Naturally, this one will be included in this lesson among the code snippets.

At any rate, specifying Allow for the Effect statement tells AWS that I want to permit the principal - an EC2 instance in our case - to execute a ListBucket Action. That'll list all the objects in a bucket. If I wanted to permit access to all buckets, I would have used a single asterisk here. But I'm a careful kind of guy, so I'll limit access to just this one bucket, called private-bucket-389 that I created a few minutes ago.

The second section of the role permits put, get, and delete actions directly against individual objects in the bucket. There's only a single text file within this bucket. Since the bucket and its object have locked-down and private permissions, we'll be able to use it to test our rule later.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": ["arn:aws:s3:::private-bucket-389"]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": ["arn:aws:s3:::private-bucket-389/*"]
    }
  ]
}

Next, we'll run the put-role-policy command to take the contents of that ec2-role-access file and name a new policy using those contents, S3Permissions. I'll then attach the policy to the S3access role. So to summarize the action so far, we now have a role called S3access that contains the trust policy found in the ec2-role-trust-policy.json file and the access policy found in the ec2-role-access-policy.json file. So our role is now complete.

aws iam put-role-policy \
   --role-name S3access \
   --policy-name S3Permissions \
   --policy-document file://ec2-role-access-policy.json

Now we'll need to take that role and turn it into an instance profile. Or, to put it an different way, an EC2 instance profile is a kind of container for an IAM role. We'll create the profile with the create-instance-profile command, giving it s3access-profile for a name. Of course, all these names are up to us: just try to make them as descriptive as possible.

aws iam create-instance-profile \
   --instance-profile-name s3access-profile

One more step: we need to add our role to the new profile. This command takes two arguments, the name of our new profile, and the name of the role we'd like to use.

aws iam add-role-to-instance-profile \
   --instance-profile-name s3access-profile \
   --role-name S3access

All it'll take from here is this associate-iam-instance-profile command, where I specify the instance by its ID (which I took from the console earlier), and the name of the profile. All that's simple enough. But did it actually work?

aws ec2 associate-iam-instance-profile \
   --instance-id i-05150ee96ce4220a6 \
   --iam-instance-profile Name=s3access-profile

So let me SSH into my instance, pointing to the SSH key I specified during the launch process. I'll update the software repo index because I need to install the AWS CLI. Although, on second thought, that won't really be necessary, because I'm going to use snap to install the CLI. I think the package I'm looking for is called awscli. Nope. Sorry about that. It actually has a dash. Oh yeah. And I need to add --classic to get out of the standard snap sandbox. Ok. That worked at last.

 

I finished! On to the next chapter