The Vault Cognito secrets engine allows you to request credentials from AWS Cognito.
AWS cognito is used as an authentication engine based around JSON Web Tokens (JWT).
At Wealth Wizards we have adopted Cognito for our application authentication and have been moving towards JWT everywhere. Which allows us to authenticate and authorised both human users and automation users based on their JWT tokens issued from Cognito.
As our user groups/permissions have evolved we have the challenge to test multiple different types of users, previously we have had statically created users in our test Cognito user pools and shared the credentials in the Vault K/V secrets backend. These static/stored credentials become well known and prevent us seeing who is using them and stops us from revoking access from individuals. Introducing this secrets backend allows our tests to request the access that they need when they need it, with the credentials revoked once the tests have finished.
We also have automated processes that need access for a short period of times to production systems, rather than introduce static credentials or alternative authentication mechanisms, we can use Vault to issue JWT tokens for these processes.
AWS Cognito provides JWT based authentication and authorisation and can authenticate users from an internal user database as well as third parties, e.g. via SAML. Cognito can be used in stand along services, but is easily integrated with many AWS serverless components, e.g. API gateway, AppSync.
For more information about Cognito read: What Is Amazon Cognito?.
A Cognito User Pool is a user directory in Amazon Cognito and provides sign in services which authenticates users and allows them to get tokens for authorisation, see Using Tokens with User Pools for more details.
In order to interact with Cognito, an App Client is required. This is an entity within a user pool that has permission t call unauthenticated API operations, e.g. sign in. To call these API operations you need an app client id and optionally a client secret.
As well as providing tokens for a user, an app client can be used to perform machine to machine authentication, refered to as client credential grant . A good explanation of Cognito and setting this up is given in this article: Server to Server Auth with Amazon Cognito .
To begin plugin installation, either download a release or build from source for your chosen OS and architecture.
Always download the latest stable release from github.
Build for your target OS, the plugin binary will be available in vault/plugins
.
make build
OS=linux make build
OS=darwin make build
- Move the desired plugin binary into your Vault's configured
plugin_directory
:
mv vault-plugin-secrets-cognito-<os>-<arch> <plugin_directory>/cognito
- If you downloaded it from releases then you may need to change the permissions:
chmod u+x cognito
- Register the plugin:
Get the checksum from checksums.txt corresponding to downloaded release binary.
Or get it from the locally built binary, e.g sha256sum <compiled_binary>
.
Insert the checksum into the following command:
vault plugin register \
-sha256=<SHA256 Hex value of the plugin binary> \
secret \
cognito
- Enable the secrets backend:
vault secrets enable -path=cognito cognito
- Install the new version of the plugin as per the instructions above and reregister the plugin with the new checksum, until this is done the plugin will be unavailable:
vault plugin register \
-sha256=<SHA256 Hex value of the plugin binary> \
secret \
cognito
- Reload the plugin:
vault plugin reload -plugin=cognito
There are two types of roles that can be configured and is determined by the credential_type
value in the configured
role:
client_credentials_grant
- This uses an app client secret to generates an JWT access token that can be used as a bearer access tokenuser
- This creates a user in configured the user pool and returns the username, password and JWT tokens
To create a role:
vault write cognito/roles/my-cognito-role credential_type=<client_credentials_grant/user> ...
The other parameters are defined below for each type.
Create a role that determines how to get an access token:
vault write cognito/roles/my-cognito-client-credentials-grant credential_type=client_credentials_grant app_client_id=IIIIII app_client_secret="SSSSSS" cognito_pool_domain="my-user-pool.auth.eu-west-1.amazoncognito.com"
Where
- credential_type:
client_credentials_grant
- app_client_id: The id of the cognito pool app client
- app_client_secret: The secret created for your app client
- cognito_pool_domain: The domain for the user pool: e.g. my-user-pool.auth.eu-west-1.amazoncognito.com
The user role creates a user in the configured Cognito User pool, Vault must have permissions to run the Admin API commands. The user is created, and the forced change password is worked through in order to produce a user that is ready to be used with your application.
Create a role that determines how to get a user and associated tokens:
$ vault write cognito/roles/my-congito-user credential_type=user region=eu-west-1 app_client_id=abcdefghijeck user_pool_id=eu-west-1_abcdefg group=mycognitogroup dummy_email_domain=example.com
Where:
- credential_type:
user
- region: The AWS region that your cognito pool exists, e.g. us-east-1
- app_client_id: The app client id
- user_pool_id: The cognito pool id, e.g. eu-west-1_abcdefg
- group: The Cognito user group to assign this user to
- dummy_email_domain: The user will be created using an email address, set the domain to use, it does not need to be a real domain as emails are not sent.
- ttl: The default time to live for this user, before is revoked
Note that the TTL is how long the user exists, the tokens returned will have their own TTLs based on the app client configuration and may be valid for longer than the user. However, the refresh token will be rejected once the user has been revoked.
Vault requires permissions to manage users in your Cognito User Pool, in order to add and delete users. This is not required for client credentials grant role as this uses the app client secret in the role definition.
This plugin uses the AWS SDK for go and can be configured explicity by writing config or via other options like environment variables, see Configuring the AWS SDK for Go for details.
Vault requires permissions to your cognito user pools, this is an example IAM policy to achive this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cognito-idp:AdminDeleteUser",
"cognito-idp:AdminInitiateAuth",
"cognito-idp:AdminCreateUser",
"cognito-idp:AdminAddUserToGroup",
"cognito-idp:AdminRespondToAuthChallenge"
],
"Resource": "*"
}
]
}
To set AWS credentials on this secrets backend write to config:
vault write cognito/config aws_access_key_id=AAAA aws_secret_access_key=BBBB aws_session_token=CCCC
Where:
- aws_access_key_id: The AWS access key to use
- aws_secret_access_key: The AWS secret access key to use
- aws_session_token: The AWS session token to use
- aws_assume_role_arn: the full ARN of the IAM role to assume
These are all optional, depending on how your Vault instance authenticates against AWS.
aws_assume_role_arn
defines an IAM role that can be assumed by this plugin; this can be either in addition to access
keys or instead. There are a number of scenarios where this might be useful:
- granularity of control - Vault may interact with AWS via another plugin, using a role reduces what this plugin can do
- cross account access - Vault may need to interact with multiple AWS accounts, this can be achieved by enabling this plugin multiple times, each with a different role set up to allow access to another account
- ec2 instance role - Vault may be running on ec2 with an instance role, rather than the instance having full access to everything required, it can assume a role that only allows this plugin to do what it needs to do
vault read cognito/creds/my-cognito-client-credentials-grant
Key Value
--- -----
access_token ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
expires_in 3600
token_type Bearer
Note that the expiration (expires_in
) is determined by the app client configuration and cannot be renewed either via
Vault or cognito as there is no accompanying refresh token. However, it's trivial to retrieve a new one if required.
You can then use the token for Bearer authentication as part of a http requests by setting the Authorization
header.
curl --location --request GET 'https://my-api.example.com' --header 'Authorization: ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
vault read cognito/creds/my-cognito-user
Key Value
--- -----
lease_id cognito/creds/my-cognito-user/abcdefg
lease_duration 300s
lease_renewable true
access_token aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
expires_in 3600
id_token iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
password pppppppppppppppppppppppppppppp
refresh_token rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
token_type Bearer
username vaultc21-0074-0574-988c-bd3ae161ea5d@example.com
Note that the expiration (expires_in
) is determined by the app client configuration. However, the refresh token can be
used to get new access/id tokens from Cognito as long as the user hasn't been revoked by Vault.
All commands can be run using the provided Makefile. However, it may be instructive to look at the
commands to gain a greater understanding of how Vault registers plugins. Using the Makefile will result in running the
Vault server in dev
mode. Do not run Vault in dev
mode in production. The dev
server allows you to configure the
plugin directory as a flag, and automatically registers plugin binaries in that directory. In production, plugin
binaries must be manually registered.
This will build the plugin binary and start the Vault dev server:
make
Now open a new terminal window and run the following commands:
export VAULT_ADDR='http://127.0.0.1:8200'
make enable
Run the tests:
$ make test
CircleCI builds and tests this project. Artefacts are created on each build and made available as part of the build.
To release the project:
- Create a new release in the GitHub project
- Set the Tag version and Release title to the next semantic verion for the next release, e.g.
v.0.0.2
- Enter a brief description of the changes included in this release
- Download the two binaries and the checksums.txt file from the CicleCi build correspinding to the latest commit that we're tagging and upload them to the GitHub release
- Click publish release