How to rid yourself of AWS API access keys

Save yourself from expensive headaches

4 minute read

It’s fairly legacy these days to use hardcoded IAM Access Key ID and Secret Access Key values to call the AWS API, which has spawned a variety of different ways to obtain and utilise IAM credentials.

Wait, what’s wrong with hardcoded credentials?

The issues are numerous, some of which have come to the surface because of data breaches. See Dev put AWS keys on Github or leaked access keys resulting in a $6,000 bill for starters. The main issues are:

  • Security risk: Code which has these credentials baked in could be inadvertedly uploaded to a source code repository
  • Tracking usage: Disgruntled employees could leave with code snippets containing those credentials and spuriously spin resources up without your knowledge
  • Unmanagable: Best practice from AWS states that API keys should be rotated regularly - this becomes a challenge if they are held up in your source code

So how do I rid myself of them?

If your code is running within AWS, you almost certainly can leverage IAM roles.

An IAM role is an identity with a set of managed or inline IAM policies attached that other AWS services or indeed accounts can ‘assume’ to work with resources in your account. The main use we’ll talk about today is with EC2 instances but the same basic principles are true of a number of AWS services.

How do I give my EC2 instance an IAM role?

If you’re using the launch wizard for your EC2 instance, you’re given an option to choose an ‘IAM Role’ within the ‘Configure Instance Details’ page. You can either select a pre-existing role if you have one, or create a new role from this screen.

The good news is that once you’ve assigned an IAM role you can change the permissions it has whenever you like (give it more or less) and you don’t actually have to give your IAM role any permissions to start with - the only thing that’s crucial is it has a Trust Relationship to allow the EC2 service to assume it:

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

You can assign IAM policies just as you’ve done for your IAM user (either by group membership or individually assigning managed or inline policies to the role) but eitherway you should create policies that are as restricted down as possible to the services and actions it needs to perform, like doing an s3:GetObject from one of your S3 buckets.

Of course it’s easier to just give your IAM role the canned “AdministratorAccess” managed policy but that means you’re effectively handing the keys over to your account to your EC2 instance, and if it gets compromised then you’re almost back in as bad a position as having hardcoded credentials…

Inline policy on my IAM role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListS3Bucket",
      "Action": "s3:ListBucket",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::myfirstbucket"
    },
    {
      "Sid": "AllowGetFromS3Bucket",
      "Action": "s3:GetObject",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::myfirstbucket/*"
    }
  ]
}

Note about assigning IAM roles to EC2 instances

Up until recently if you’d launched an EC2 instance without an IAM role specified at first boot, you’d have no option but to take an AMI of the instance and then launch a new one using that AMI with an IAM role. That’s now all changed as you can associate an IAM instance profile (and in turn an IAM role) to an existing EC2 instance by using the AWS CLI.

Letting the magic happen

Now that you’ve got an EC2 instance starting with an IAM role then providing your code is using the aws-sdk or you’re working with the AWS CLI, there’s actually very little you have to do. Both of these implementations in all recent versions natively support obtaining access to AWS services using the IAM role associated to the EC2 instance.

Firstly you should delete any reference to your hardcoded IAM user credentials, so move ~/.aws/credentials out of the way and do a unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY at your bash prompt. You should now be able to use the AWS CLI with no further effort to utilise the permissions granted to your IAM role:

$ aws s3 cp s3://myfirstbucket/test/image.jpg .
download: s3://myfirstbucket/test/image.jpg to ./image.jpg

And that’s it! You can now be free of storing any AWS API credentials on your instance or in your code that runs on AWS.

How does this work in practice?

If you’re into all that sort of stuff then the answer is both the aws-sdk and the AWS CLI use the EC2 Metadata service to retrieve temporary, time limited AWS API credentials to leverage your IAM role. You can see this for yourself using curl:

$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<name-of-your-IAM-role>
{
  "Code" : "Success",
  "LastUpdated" : "2017-02-22T08:50:02Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ZZZZZZZZZZZZZZZZZZZZ",
  "SecretAccessKey" : "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTt",
  "Token" : "<long token here>",
  "Expiration" : "2017-02-22T15:11:25Z"
}
comments powered by Disqus