kevinhakanson.com

CLI SAML Authentication and AWS STS assume-role

March 02, 2017 #aws #iam #cli

When I run a CLI based SAML log in (using an internal tool), I can select from the roles available to my account (e.g., PowerUser or ReadOnly).  I think of these as user roles.  However, IAM roles can also be created and assigned to EC2 instances or Lambda functions.  I was trying to understand how local code could run under one of those specific IAM roles.  I decided to try out assume-role — AWS CLI 1.11.56 Command Reference:

$ aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/kjh-s3-encryption-test1-role \
--role-session-name cli-kjh-s3-encryption-test1-role

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::123456789012:assumed-role/PowerUser/kevin.hakanson@example.com is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/kjh-s3-encryption-test1-role

As you can see, that didn’t work.  PowerUser has the authority to sts:AssumeRole, but I had to add myself to that role policy so my specific Principal could assume kjh-s3-encryption-test1-role.  The default was to only allow the EC2 service to do this.  You can see the full policy below.

$ aws iam get-role --role kjh-s3-encryption-test1-role

{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "ec2.amazonaws.com"
                    }
                },
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:sts::123456789012:assumed-role/PowerUser/kevin.hakanson@example.com"
                    }
                }
            ]
        },
        "RoleId": "AROAXXXXXXXXXXXXXXXXX",
        "CreateDate": "2016-08-13T16:31:08Z",
        "RoleName": "kjh-s3-encryption-test1-role",
        "Path": "/",
        "Arn": "arn:aws:iam::123456789012:role/kjh-s3-encryption-test1-role"
    }
}

After that change, I was able to perform an AssumeRole.

$ aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/kjh-s3-encryption-test1-role \
--role-session-name cli-kjh-s3-encryption-test1-role

{
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:cli-kjh-s3-encryption-test1-role",
        "Arn": "arn:aws:sts::123456789012:assumed-role/kjh-s3-encryption-test1-role/cli-kjh-s3-encryption-test1-role"
    },
    "Credentials": {
        "SecretAccessKey": "<secret>",
        "SessionToken": "<token>",
        "Expiration": "2017-03-01T22:41:37Z",
        "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX"
    }
}

However, this wasn’t as useful as I thought, since I would also need to create a profile and copy those credentials into my  ~/.aws/credentials file.  Luckily, I discovered Assuming a Role - AWS Command Line Interface and just added a new profile section to my ~/.aws/config that referenced that role ARN and my saml profile.

[profile kjh-s3-encryption-test1-role]
role_arn = arn:aws:iam::123456789012:role/kjh-s3-encryption-test1-role
source_profile = saml

After that, I was able to automatically assume that role with the CLI by specifying my new profile.

$ aws s3api --profile kjh-s3-encryption-test1-role list-objects \
--bucket kjh-encryption-test1 \
--prefix cli-s3api-kms/

{
    "Contents": [
        {
            "LastModified": "2016-12-06T21:02:16.000Z",
            "ETag": "\"f3a07b31bc1cee5e70b6f0bbd9c5bf09\"",
            "StorageClass": "STANDARD",
            "Key": "cli-s3api-kms/QBF.txt.enc",
            "Size": 45
        }
    ]
}

Interestingly, when I run that same command with my saml profile, I got a different result:

$ aws s3api --profile saml list-objects \
--bucket kjh-encryption-test1 \
--prefix cli-s3api-kms/

{
    "Contents": [
        {
            "LastModified": "2016-12-06T21:02:16.000Z",
            "ETag": "\"f3a07b31bc1cee5e70b6f0bbd9c5bf09\"",
            "StorageClass": "STANDARD",
            "Key": "cli-s3api-kms/QBF.txt.enc",
            "Owner": {
                "DisplayName": "redacted",
                "ID": "redacted"
            },
            "Size": 45
        }
    ]
}

Seeing the difference was the Owner, I added s3:GetObjectAcl to my policy using the AWS Console UI.  After that, I got the same result using either profile.  I also learned that if you want to see the policy document from the CLI, get-policy doesn’t give you that information…

$ aws iam get-policy --policy-arn arn:aws:iam::123456789012:policy/kjh-s3-encryption-test1

{
    "Policy": {
        "PolicyName": "kjh-s3-encryption-test1",
        "CreateDate": "2016-08-12T22:55:52Z",
        "AttachmentCount": 1,
        "IsAttachable": true,
        "PolicyId": "ANPAXXXXXXXXXXXXXXXXX",
        "DefaultVersionId": "v5",
        "Path": "/",
        "Arn": "arn:aws:iam::123456789012:policy/kjh-s3-encryption-test1",
        "UpdateDate": "2017-03-01T22:15:36Z"
    }
}

... so you need to use `get-policy-version` with the version id to get that policy document.

```console
$ aws iam get-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/kjh-s3-encryption-test1 \
--version-id v5

{
    "PolicyVersion": {
        "CreateDate": "2017-03-01T22:15:36Z",
        "VersionId": "v5",
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": [
                        "s3:DeleteObject",
                        "s3:GetObject",
                        "s3:PutObject",
                        "s3:GetObjectAcl"
                    ],
                    "Resource": [
                        "arn:aws:s3:::kjh-encryption-test1/*"
                    ],
                    "Effect": "Allow"
                },
                {
                    "Action": [
                        "s3:ListBucket"
                    ],
                    "Resource": [
                        "arn:aws:s3:::kjh-encryption-test1"
                    ],
                    "Effect": "Allow"
                }
            ]
        },
        "IsDefaultVersion": true
    }
}

Hopefully, this will allow me to better understand and change IAM policies in order to apply the Principle of least privilege and only grant the specific actions required.