Managing IAM users with Terraform

An essential part of using AWS is controlling access to the resources. We've seen with all the previous recipes how often we need to use the AWS Access Keys, and it's surely not a good idea to use a single key for all your activities. Imagine what would happen if a single one of your services was hacked—the intruder would get the main AWS key and would be able to do everything on your behalf.

A good secure setup would be dedicated keys with a dedicated scope of access rights for every person in your team and every service in your infrastructure.

Thankfully, Identity and Access Management (IAM) is there just for that. We'll see how to use it with Terraform.

Getting ready

To step through this recipe, you will need the following:

  • A working Terraform installation
  • An AWS provider configured in Terraform (refer to the previous recipes)
  • An Internet connection

How to do it…

Let's start with a simple case: two members of a team (Mary and Joe) need to access resources on AWS. They currently all share the same main key, which is a disaster if a leakage happens. So let's ask them what exactly they need to access in the AWS space:

Mary

S3 in read and write

Joe

EC2 in read only

As expected, neither user really needs full access!

Amazon helps by offering prebuilt security policies for IAM. If those aren't enough, you can tailor the ones you need:

How to do it…

Note

You can find all AWS Managed IAM Policies at https://console.aws.amazon.com/iam/home#policies.

An IAM user for S3 access

Let's create a first IAM user for Mary in a new iam.tf file using the aws_iam_user resource:

resource "aws_iam_user" "mary" {
  name = "mary"
  path = "/team/"
}

The path is purely optional and informative, I'm simply suggesting structured paths. So we'll have /apps/ as well later.

We can now create an AWS Access Key for our user Mary, using the aws_iam_access_key resource with reference to our user:

resource "aws_iam_access_key" "mary" {
  user = "${aws_iam_user.mary.name}"
}

And finally, as we know, we want to attach to this user the AmazonS3FullAccess managed policy, let's use the dedicated resource:

resource "aws_iam_user_policy_attachment" "mary_s3full" {
  user = "${aws_iam_user.mary.name}"
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}

Let's write an output so we know both parts of the key in outputs.tf:

output "mary" {
  value = "ACCESS_KEY: ${aws_iam_access_key.mary.id}, SECRET: ${aws_iam_access_key.mary.secret}"
}

Also, terraform apply this to create the mary user:

[...]
Outputs:
mary = ACCESS_KEY: AKIAJPQB7HBK2KLAARRQ, SECRET: wB+Trao2R8qTJ36IEE64GNIGTqeWrpMwid69Etna

Testing the restrictions

Now, terraform apply this, and confirm using an S3 browser that you can access S3! Here's an example of creating a simple S3 bucket with s3cmd:

$ s3cmd --access_key=<mary_access_key> --secret_key=<mary_secret_key> mb s3://iacbook-iam-bucket
Bucket 's3://iacbook-iam-bucket/' created

Is this account really limited to S3, as it pretends to be? Let's try to list EC2 hosts with Mary's account using the aws command line (provided you configured the aws tool accordingly):

$ aws --profile iacbook-mary ec2 describe-hosts
An error occurred (UnauthorizedOperation) when calling the DescribeHosts operation: You are not authorized to perform this operation.

So it all looks good and secure! Mary can do her job on S3 safely.

An IAM user for EC2 in read-only

Is there a similar managed policy for Joe, with a read-only scope on EC2? Fortunately, there is! It's creatively named AmazonEC2ReadOnlyAccess.

Let's create our second user, with this IAM policy in the iam.tf file:

resource "aws_iam_user" "joe" {
  name = "joe"
  path = "/team/"
}

resource "aws_iam_access_key" "joe" {
  user = "${aws_iam_user.joe.name}"
}

resource "aws_iam_user_policy_attachment" "joe_ec2ro" {
  user = "${aws_iam_user.joe.name}"
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
}

Don't forget the useful output that comes with it:

output "joe" {
  value = "ACCESS_KEY: ${aws_iam_access_key.joe.id}, SECRET: ${aws_iam_access_key.joe.secret}"
}

Next, terraform apply this once again, and can the Joe user see what's on S3? No, he can't:

$ s3cmd --access_key=<joe_access_key> --secret_key=<joe_secret_key> ls
ERROR: S3 error: 403 (AccessDenied): Access Denied

But can the Joe user simply list the EC2 VMs as he needs to, with the same command that was forbidden to Mary? Yes, he can:

$ aws --profile iacbook-joe ec2 describe-hosts
{
  "Hosts": []
}

We're on track to securely manage our infrastructure access using code!

An application user IAM – CloudWatch Logs

We've used the CloudWatch Logs service in a previous recipe. If you remember, you had to enter once again your keys in the Docker Engine configuration. If you had 100 servers, your master keys would be on each of them. This is rather unnecessary, if you consider that the scope of this configuration in Docker is just to send logs. Fortunately, there's a managed IAM policy for that named CloudWatchLogsFullAccess.

So let's create another user, exactly as before for Mary and Joe, except this one will be for our Docker Engines and not for a real user in iam.tf. I suggest using a different path, just to separate real users and application users. However, that's totally optional and opinionated:

resource "aws_iam_user" "logs" {
  name = "logs"
  path = "/apps/"
}

resource "aws_iam_access_key" "logs" {
  user = "${aws_iam_user.logs.name}"
}

resource "aws_iam_user_policy_attachment" "logs_cloudwatch_full" {
  user = "${aws_iam_user.logs.name}"
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}

The relevant output in outputs.tf is as follows:

output "logs" {
  value = "ACCESS_KEY: ${aws_iam_access_key.logs.id}, SECRET: ${aws_iam_access_key.logs.secret}"
}

Now, terraform apply this and try the Enabling CloudWatch Logs for Docker with Terraform recipe again with those credentials instead of the master keys: it will still work on the CloudWatch scope, but if something goes wrong, it will never put the rest of your infrastructure in danger. The worst that can happen in this area is the total waste of the logs.

[...]
Outputs:

joe = ACCESS_KEY: AKIAJQPSXBKSD3DY47BQ, SECRET: VQgtQ7D8I+mxRX28/x5qbFk6cdyxZajhhSsh7Rha
logs = ACCESS_KEY: AKIAISIUXTG5RIJZAEYA, SECRET: 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
mary = ACCESS_KEY: AKIAJPQB7HBK2KLAARRQ, SECRET: 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
An application user IAM – CloudWatch Logs

There's more…

If you'd prefer to see how this would work using Ansible, it's a bit different. IAM support is not equivalent, as there's no IAM Managed Policies support. However, you can simply create users like this:

---
- name: create mary user
  iam:
    iam_type: user
    name: mary
    state: present
    access_key_state: create
    path: /team/

As there's currently no IAM Managed Policy support, a workaround is to use the JSON from the IAM Policy we want, such as AmazonS3FullAccess for our user Mary. It's easy to find in the AWS Console in the Policies section (https://console.aws.amazon.com/iam/home#policies). Paste the following JSON content in AmazonS3FullAccess.json at the root of the Ansible folder:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

Use this local policy in the iam_policy module:

- name: Assign a AmazonS3FullAccess policy to mary
  iam_policy:
    iam_type: user
    iam_name: mary
    policy_name: AmazonS3FullAccess
    state: present
    policy_document: AmazonS3FullAccess.json
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.183.89