Managing access to multiple AWS Accounts with OpenID

Many organisations look towards a multiple account strategy with Amazon Web Services (AWS) to provide administrative isolation between workloads, limited visibility and discoverability of workloads, isolation to minimize blast radius, management of AWS limits and cost categorisation. However, this comes at a large complexity cost, specifically around Identity Access Management (IAM).

Starting off with a single AWS account, and using a handful of IAM users and groups for access management, is usually the norm. As an organisation grows they start to see a need for separate staging, production, and developer tooling accounts. Managing access to these can quickly become a mess. Do you create a unique IAM user in each account and provide your employees with the unique sign-on URL? Do you create a single IAM user for each employee and use AssumeRole to generate credentials and to enable jumping between accounts? How do employees use the AWS Application Programming Interface (API) or the Command Line Interface (CLI); are long-lived access keys generated? How is an employee’s access revoked should they leave the organisation?

User per account approach

All users in a single account using STS AssumeRole to access other accounts

Design

Reuse employees existing identities

In most organisations, an employee will already have an identity, normally used for accessing e-mail. These identities are normally stored in Active Directory (AD), Google Suite (GSuite) or Office 365. In an ideal world, these identities could be reused and would grant access to AWS. This means employees would only need to remember one set of credentials and their access could be revoked from a single place.

Expose an OpenID compatible interface for authentication

OpenID provides applications with a way to verify a users identity using JSON Web Tokens (JWT). Additionally, it provides profile information about the end user such as first name, last name, email address, group membership, etc. This profile information can be used to store the AWS accounts and AWS roles the user has access to.

By placing an OpenID compatible interface on top an organisation’s identity store users can easily generate JWTs which can be later used by services to authenticate them.

Trading JWTs for AWS API Credentials

In order to trade JWTs for AWS API Credentials, a service can be created that runs on AWS with a role that has access to AssumeRole.This service would be responsible for validating a users JWT, ensuring the JWT contains the requested role and executing STS AssumeRole to generate the AWS API Credentials.

Additionally, the service would also generate an Amazon Federated Sign-On URL which would enable users to access the AWS Web Console using their JWT.

Example Implementation

Provided below is an example implementation of the above design. One user with username “demo” and password “demo” exists. Please do not use this demo in a production environment without https.

To follow along, clone or download the code at https://github.com/imduffy15/aws-credentials-issuer.

OpenID Provider

Keycloak provides an IAM along with OpenID Connect (OIDC) and Security Assertion Markup Language (SAML) interfaces. Additionally, it supports federation to Active Directory (AD) and Lightweight Directory Access Protocol (LDAP) servers. Keycloak enables organisations to centrally manage employees access to many different services.

The provided code contains a docker-compose file that on executing docker-compose up will bring up a keycloak server with its administrative interface accessible at http://localhost:8080/auth/admin/ using username “admin” and password “password”.

On the “clients” screen, a client named “aws-credentials-issuer” is present, users will use this client to generate their JWT tokens. This client is pre-configured to work with the Authorization Code Grant for command line interfaces to generate tokens and the Implicit Grant for a frontend application.

Under the “aws-credentials-issuer” additional roles can be added, these roles must exist on AWS and they must have a trust relationship to the account that will be running the “aws-credentials-issuer”.

Additionally, these roles must be placed into the users JWT tokens, this is pre-configured under “mappers”.

Finally, the role must be assigned to a user. This can be done by navigating to users -> demo -> role mappings and moving the wanted role from “available roles” to “assigned roles” for the client “aws-credentials-issuer”

AWS Credentials Issuer Service

Backend

The provided code supplies a lambda function which will take care of validating the users JWT token and exchanging it using AssumeRole for AWS Credentials.

This code can be deployed to an AWS account by using the serverless framework and the supplied definition. The definition will create the following:

With AWSCLI configured with credentials for the account that the service will run in execute sls deploy, this will deploy the lambda functions and return URLs for executing them.

Frontend

The provided code supplies a frontend which will provide users with a graphical experience for accessing the AWS Web Console or generating AWS API Credentials.

The frontend can be deployed to an S3 bucket using the serverless framework. Before deploying it some variables must be modified. In the serverless definition (serverless.yml), replace “ianduffy-aws-credentials-issuer” with a desired S3 bucket name and modify ui/.env to contain your Keycloak and Backend URL as highlighted above. The deployment can be executed with sls client deploy.

On completion, a URL in the format of http://<bucket-name>.s3-website.<region>.amazonaws.com will be returned. This needs to be supplied to keycloak a redirect URI for the “aws-credentials-issuer” client.

Usage

Browser

By navigating to the URL of the S3 bucket a user can get access to the AWS Web Console or get API credentials which they can use to manually configure an application.

Command line

To interact with the “aws-credentials-issuer” the user must have a valid JWT. This can be done by executing the Authorization Code Grant against keycloak.

token-cli can be used to execute the Authorization Code Grant and generate a JWT token, this can be downloaded from the projects releases page; alternatively, on OSX it can be installed with homebrew brew install imduffy15/tap/token-cli.

Once token-cli is installed it must be configured to run against keycloak, this can be done as follows:

Finally, a token can be generated with token-cli token get aws-credentials-issuer -p 9000. On first run the users browser will be opened and they will be required to login, on subsequent runs the token will be cached or refreshed automatically.

This token can be used against the “aws-credentials-issuer” to get AWS API credentials:

curl https://<API-GATEWAY>/dev/api/credentials?role=<ROLE-ARN> \
-H "Authorization: bearer $(token-cli token get aws-credentials-issuer)"

Alternatively, a AWS Amazon Federated Sign-On URL can also be generated:

curl https://<API-GATEWAY>/dev/api/login?role=<ROLE-ARN> \
-H "Authorization: bearer $(token-cli token get aws-credentials-issuer)"