This program generates credentials for use by the AWS CLI, backed by
STS assumeRoleWithWebIdentity
and an OIDC provider.
-
Use
aws <command>
. -
AWS uses
oidc2aws
as acredential_process
to request credentials for a profile (+ Role ARN). -
oidc2aws
checks for unexpired cached credentials for a Role ARN. If found, they are passed back toaws
. -
If no unexpired credentials are cached,
oidc2aws
starts a web-server and, open the user's browser pointed to a configured OAuth app. -
User authenticates with Google and authorises the OAuth app to access the Google account details.
-
Google passes an authorisation code back to the web-server started in step 4 by redirection the user's browser.
-
The web-server handles the redirection, swaps the authorisation code for access and ID tokens, and terminates the web-server.
-
oidc2aws
then uses the ID token with the AWS STS API to assume the given role. -
Credentials generated in step 8 are passed back to
aws
for use with the originally-requested AWS CLI/API call.
Create an OAuth 2.0 Client in Google Cloud console, and:
-
Set Authorised redirect URIs to
http://localhost:9999/code
-
Mark application as Internal on OAuth consent screen.
-
Make note of client ID and secret.
Create a file in $HOME/.oidc2aws/oidcconfig
, with contents of:
Provider = "https://accounts.google.com"
ClientID = "<client ID>"
ClientSecret = "<client secret>"
HostedDomain = "<G Suite email address domain>"
Create an Identity provider in AWS IAM;
-
Provider Type: OpenID Connect
-
Provider URL:
https://accounts.google.com
-
Audience: Client ID of OAuth 2.0 app created above.
Create new role in AWS IAM to be used with oidc2aws
:
-
Select type of trusted entity: Web identity.
-
Identity provider: use provider created earlier (or choose Google).
-
Audience: Select Audience added when creating provider (or use Client ID directly)
-
Do not attach any policies yet. When you first create the role, anyone who can log in to the OAuth application can assume the role.
To restrict access to the role:
-
Go to the details for the role you just created, select the Trust relationships tab, and click Edit trust relationship.
-
Add a
StringEquals
(orForAllValues:StringEquals
) field toConditions
. The value should be an object that looks like:"StringEquals": { "accounts.google.com:sub": "<Google ID>" }
Determining someone's Google ID can be a bit tricky, and I'm not going to write instructions on how to find this out yet :E
Once you've restricted access to the role, you can attach policies to the role as usual.
Make a note of the ARN for the role you created in the previous step.
edit $HOME/.aws/config
, and add a profile section:
[profile my-profile]
credential_process = oidc2aws <role arn:aws:iam::123456789012:role/my-role>
Then you should be able to use the AWS CLI:
$ aws --profile=my-profile sts get-caller-identity
{
"UserId": "AROAXXXXXXXXXXXXXXXX:me@example.com",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/my-role/me@example.com"
}
oidc2aws
supports 2 output formats:
-
JSON format suitable for use by the AWS SDK profile's
credential_process
setting:$ oidc2aws arn:aws:iam::123456789012:role/my-role { "Version": 1, "AccessKeyId": "ASIA...", "Expiration": "2019-03-29...", "SecretAccessKey": "...", "SessionToken": "..." }
-
Env format suitable for setting environment variables in the shell, via
-env
flag.Note that the command for setting the environment variables is for the default shell of the current user.
$ oidc2aws arn:aws:iam::123456789012:role/my-role export AWS_ACCESS_KEY_ID=ASIA... export AWS_SECRET_ACCESS_KEY=... export AWS_SESSION_TOKEN=...
you can set these varables directly using
$()
:$ $(oidc2aws arn:aws:iam::123456789012:role/my-role) $ env | grep AWS AWS_ACCESS_KEY_ID=ASIA... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=...
If you are using
fish
shell, you can do this instead:$ oidc2aws -env arn:aws:iam::123456789012:role/my-role | source
If you are not using the default shell of current user, you can set the shell type explicitly by
-shell
flag:$ oidc2aws -env -shell csh arn:aws:iam::123456789012:role/my-role setenv AWS_ACCESS_KEY_ID ASIA... setenv AWS_SECRET_ACCESS_KEY ... setenv AWS_SESSION_TOKEN ...
You can use oidc2aws
to automatically log in to the AWS console
using -login
:
$ oidc2aws -login arn:aws:iam::123456789012:role/my-role
this will open a web browser to https://console.aws.amazon.com/
with
session on arn:aws:iam::123456789012:role/my-role
AWS allows roles to assume other roles, for example when you have a
staff role that can assume more specific roles, or when using multiple
AWS accounts and cross-account roles. oidc2aws
supports this using
this syntax:
$ oidc2aws <role 1> <role 2> <...> <role N>
This extends the behaviour described at the top of the document:
-
After fetching the oidc credentials,
oidc2aws
passes the first role arn (role 1
) in the list in the call tosts.assumeRoleWithWebIdentity
. -
Then, for each role
R
in the chain,oidc2aws
uses the credentials returned fromsts.AssumeRole(R - 1)
to callsts.AssumeRole(R)
So with:
$ oidc2aws <role 1> <role 2> <role 3>
-
Credentials
C1
forrole 1
will be acquired viasts.assumeRoleWithWebIdentity(<role 1>)
using the OIDC ID token. -
Credentials
C2
forrole 2
will be acquired viastsAssumeRole(<role 2>)
using credentialsC1
. -
Finally, Credentials
C3
will be acquired viastsAssumeRole(<role 3>)
using credentialsC2
.
oidc2aws
also provides a -sourcerole
option for role chaining. A command of
$ oidc2aws -sourcerole arn:aws:iam::123456789012:role/source-role arn:aws:iam::999999999999:role/target-role
is equivalent to the same command without -sourcerole
:
$ oidc2aws arn:aws:iam::123456789012:role/source-role arn:aws:iam::999999999999:role/target-role
oidc2aws
supports an -alias
flag. Add aliases to your oidcconfig
file:
Simple case, assuming a single role:
[alias.<alias-name>]
arn = "arn:aws:iam::<account id>:role/<role name>"
Role chaining:
[alias.<alias-name>]
roleChain = ["arn:aws:iam::<account id>:role/<source role name>", "arn:aws:iam::<account id>:role/<target role name>"]
Either arn
or roleChain
is required.
Then, when you invoke oidc2aws
it will look up the alias in the
config, instead of having to use bare ARNs:
oidc2aws -alias <alias-name>
This was added because oidc2aws
has become useful to use directly
from the cli, not just with credential_process
, and using ARNs
directly gets tedious (especially trying to remember AWS account
ids!).
I considered using AWS profiles, but this would mean parsing
~/.aws/config
and looking for profiles that used
credential_process
, and then parsing the oidc2aws
flags out of the
value.
Similar to -sourcerole
, aliases have a legacy configuration option
sourceRole
:
[alias.<alias-name>]
arn = "arn:aws:iam::<account id>:role/<target role name>"
sourceRole = "arn:aws:iam::<account id>:role/<source role name>"
which is equivalent to:
[alias.<alias-name>]
roleChain = ["arn:aws:iam::<account id>:role/<source role name>", "arn:aws:iam::<account id>:role/<target role name>"]
-
The username used when assuming the role is under control of the client (meaning that
oidc2aws
arbitrarily sets it to the email address of the user), and is not a reliable indicator of the user's identity in AWS (meaning it would be trivial to spoof it to be someone else's email address).You can determine the true G Suite account used by looking at the API event data in CloudTrail in
userIdentity.sessionContext.webIdFederationData.attributes.accounts.google.com:sub
. -
AWS credentials are cached in plain-text at rest in
$HOME/.oidc2aws/<role name>
. Our original requirement was to eliminate use of permanent credentials, so this is acceptable to us as the on-disk credentials expire in < 12 hours.
-
Cache the G Suite ID token similarly to how the AWS credentials are cached.
-
Encrypt or otherwise harden the storage of AWS credentials at rest.
-
Find a better way to manage G Suite user to AWS role mapping. Google account IDs are hard to discover/audit, and if you don't add a condition on the Trusted entity, by default anyone who can use the OAuth app can assume the AWS role.
-
Review the system design to identify flaws in the security model.
-
Get a refresh token from Google when fetching an access token, and store it in the system keychain. Then use that to fetch new access/id tokens instead of re-authenticating via the web.
To eliminate the use of permanent AWS credentials, we looked at migrating to federated authn/z. SAML integration between AWS and G Suite is quite good, but it provides nothing in the way of support for using the AWS CLI.