Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support AWS SSO #449

Closed
benbridts opened this issue Nov 9, 2019 · 6 comments · Fixed by #549
Closed

Support AWS SSO #449

benbridts opened this issue Nov 9, 2019 · 6 comments · Fixed by #549

Comments

@benbridts
Copy link

The AWS CLI v2 (beta) now has support for using AWS SSO. It would be great if aws-vault could support this too. The blog post is here. I will provide a more in-depth description of how this works in this issue.

Expected behavior

If you use SSO with aws2, your config file willl look like this:

[profile AWSAdministratorAccess-123456789012]
sso_start_url = https://d-90xxxxxxxx.awsapps.com/start
sso_region = us-east-1
sso_account_id = 123456789023
sso_role_name = AWSAdministratorAccess
region = eu-west-1
output = json

The AWS CLI has a wizard to generate this, but I think we can assume that the user either will follow this wizard, or will get the config from someone else.

The flow is as follows:

  1. The aws-vault will read this config and redirect the user to a browser if he does not have a cached and valid access token (see below for how to generate the redirect urls)
  2. The user logs in to SSO and allows aws-vault to login (there is no redirect back to aws-vault)
  3. aws-vault queries sso-oidc to get an access token after the approval (this can be done in a loop)
  4. the access token can be exchanged for access key / secret key

Testing this out

I tried to do the same steps as the aws2 cli, but step by step. This is how that goes, and what I found:

1. Register a client

This will generate a client_id and a client_secret for aws-vault to use. AWS expects that this will be cached and long-lived.
The request is unauthenticated and the region most be the sso_region.

aws sso-oidc register-client --client-name aws-vault-test --client-type public --region $sso_region --no-sign-request
{
    "clientId": "REDACTED",
    "clientSecret": "REDACTED",
    "clientIdIssuedAt": 1573304968,
    "clientSecretExpiresAt": 1581080968
}

Some notes:

  • clientSecretExpiresAt is 90 days in the future
  • clientSecret is a JWT token with payload that looks like {"serialized": "...a-serialized-json-dictionary..."}. If you deserialize the dictionary, it looks like this:
{
  "expired": false,
  "clientId": {
      "value": "REDACTED"
  },
  "clientName": "aws-vault-test",
  "clientType": "PUBLIC",
  "templateArn": null,
  "templateContext":null,
  "expirationTimestamp": 1581080968.984000000,
  "createdTimestamp":1573304968.984000000,"
  updatedTimestamp":1573304968.984000000,
  "createdBy": null,
  "updatedBy": null
}

Step 2: Redirect the user to a verification URI

(note: If you use the CLI, dissable the resolving of URLS with aws configure set cli_follow_urlparam false)

The user has to enter a verifiaction code on a verification uri. We can also combine these two steps by adding the verification code in a query parameter. AWS will return us both options if we call start-device-authorization

aws sso-oidc start-device-authorization --client-id $client_id --client-secret $client_secret --start-url $sso_start_url --region $sso_region --no-sign-request
{
    "deviceCode": "REDACTED",
    "userCode": "HZBR-VSPV",
    "verificationUri": "https://device.sso.us-east-1.amazonaws.com/",
    "verificationUriComplete": "https://device.sso.us-east-1.amazonaws.com/?user_code=HZBR-VSPV",
    "expiresIn": 600,
    "interval": 1
}

The user has to be redirected to verificationUriComplete and aws-vault needs to safe deviceCodein memory.

Step 3: User signs in:

login
approve
approved

I assume the "AWS CLI" text at some point will be changed to use the ClientName from the first api call.

Step 4: Getting Credentials

Once the User has pushed approve, the deviceCode can be exchanged for an access token. The CLI retries this until it does, so no user interaction is needed (after expiresIn seconds the loop can break, as it will never succeed later).

aws sso-oidc create-token --client-id $client_id --client-secret $client_secret --grant-type urn:ietf:params:oauth:grant-type:device_code --device-code $device_code --region us-east-1 --no-sign-request
{
    "accessToken": "REDACTED",
    "tokenType": "Bearer",
    "expiresIn": 28800
}

I found the grant type in botocore. There is also a --scope parameter and botocore has a default scope, but it seems not used. If it fails without, use sso-portal:*.

The accessToken is again a JWT. The payload has been encrypted by KMS, so it's not direct usable.

echo $payload | base64 --decode | strings
H"'~7"
DataPlaneSession
Peregrine
aws-kms
Karn:aws:kms:us-east-1:276787839696:key/4603c12d-5f0b-4eb4-b7c9-ccb048912c24
�;&;'
0=QOC
]Hi`

The accessToken can be cached and reused to get credentials for all roles with the same sso_start_url and sso_region configuration.

aws sso get-role-credentials --role-name $sso_role_name --account-id $sso_account_id --access-token $access_token --region $sso_region --no-sign-request
{
    "roleCredentials": {
        "accessKeyId": "REDACTED",
        "secretAccessKey": "REDACTED",
        "sessionToken": "REDACTED",
        "expiration": 1573311707000
    }
}

References:

@FernandoMiguel
Copy link
Collaborator

isn't this what https://github.com/Versent/saml2aws already does?

@benbridts
Copy link
Author

isn't this what https://github.com/Versent/saml2aws already does?

Not really:

  • it does not have AWS SSO as a supported IdP (you do not need an external directory to use SSO)
  • It asks for username and password on the command line, the sso-oidc keeps all credentials in the browser
  • I'm sure there are more differences because sso-oidc does not use saml

@wigsaparelli
Copy link

This write-up was incredibly helpful thank you. Have you ever managed to obtain a refresh token from create-token (or underlying createToken API) so the user doesn't have to be prompted to sign-in every 8 hours (this is how long the access token seems to last). It doesn't ever seem to return a refresh token... Or an idToken for that matter (the other documented output that is never returned).

@eorea
Copy link

eorea commented Feb 12, 2020

FYI, AWS CLI v2 has become GA on Feb 10th, as posted here

@benbridts
Copy link
Author

@wigsaparelli I haven't tried that. IIRC it's not a requirement from OAuith / OIDC to support refresh tokens (but I might be completely wrong on that)

@abeluck
Copy link

abeluck commented Mar 26, 2020

FWIW, we've switch from aws-vault to aws2-wrap which supports AWS SSO and covers many of aws-vault's use cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants