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 authentication with OpenID Connect #93

Open
stoggi opened this issue May 21, 2019 · 4 comments
Open

Support authentication with OpenID Connect #93

stoggi opened this issue May 21, 2019 · 4 comments

Comments

@stoggi
Copy link

stoggi commented May 21, 2019

Hello,

I'm interested in authenticating users to the BLESS lambda using OpenID Connect identity tokens.

This would be an alternative to using KMS auth to prove the identity of the caller. Users could use AWS AssumeRoleWithWebIdentity API to get temporary credentials to invoke the lambda, and then pass their identity token in the payload to get a certificate signed for a username that matches a claim in the identity token.

I'm keen to implement this feature. Is there any interest and support for new features like this?

@russell-lewis
Copy link
Contributor

Can you elaborate a bit more on the system you're thinking about? That might help to see if anyone else has interest.

At least in my experience, anywhere we have OIDC clients set up, it is from code running in containers or instances doing code flow. That same application would then be able to just sign certificates directly using the SSO session directly, instead of stitching SSO AuthN->AWS AssumeRole->Invoke Lambda->Re-AuthN user.

You can always use the bless.ssh module directly if you need a python library that can issues certs. See https://github.com/Netflix/bless/blob/master/bless/aws_lambda/bless_lambda_user.py#L162-L186

@stoggi
Copy link
Author

stoggi commented May 22, 2019

I want to let engineers sign their public SSH keys from their own machines in a similar setup as Lyft with https://github.com/lyft/python-blessclient. But engineers also have federated access to AWS from an identity provider and so there are no users in IAM to use with KMS Auth.

One way to federate AWS access is to use a command line application running on an engineers laptop that uses OpenID Connect Authorization Code flow with PKCE. The users browser authenticates to their identity provider with MFA, and securely redirects the identity token (and refresh token) to the command line application. With the identity token they can then call AssumeRoleWithWebIdentity to get temporary AWS access keys. For subsequent use, the command line application would use the refresh token stored in the keychain to retrieve a new identity token, until the refresh token expires or is revoked.

This method of assuming a role in AWS is neat because AWS can verify the validity of the identity token, and there is no need to host a publicly facing API gateway. It is also a good way to transfer an identity token from the users browser into their keychain using a well known OAuth2.0 flow with PKCE.

The piece that is missing is getting the BLESS lambda to also validate the identity token was signed by the correct identity provider and has the correct username claim for the requested user. Eventually I would like to be to support adding other principals in the certificate based on claims in the identity token, for example to add a sudo principal and use https://github.com/uber/pam-ussh for sudo access on the host.

I did play around with using KMS Auth with the assumed role, since you can include the subject identifier in an IAM policy as a condition to encrypt the KMS key:

"Condition": {
    "StringEquals": {
        "kms:EncryptionContext:from": "${accounts.google.com:sub}"
    }
}

However, this limits my host usernames to the subject identifiers from the identity provider. It would be nice to verify a different claim in the identity token, or support other features like sudo principals.

@russell-lewis
Copy link
Contributor

russell-lewis commented May 22, 2019

Running a PKCE enabled client on dev machines makes perfect sense in that case, as you'll have the Access token, ID Token, and Refresh Token directly. It sounds reasonable to use other claims to control which principals can be present in the cert.

Every time I take a closer look at OIDC ID tokens, I find conflicting information on how they should be used vs. how they are often used. For example, many ID Providers don't allow ID Tokens to be refreshed. So I'm not sure if your environment could refresh the ID token without a reauth flow.

Our stance internally has generally been to only use Access Tokens for the case you are describing (where a client app is calling a remote resource). Then have the remote resource (e.g. BLESS) validate the access token either directly (in the case they are also JWTs) or from calling /userinfo with the access token. Assuming a valid access token from a valid issuer with valid scopes, the claims or /userinfo response could could control the cert contents.

However, it looks like API_AssumeRoleWithWebIdentity takes ID Tokens (they only accept 2 ID Providers for Access Tokens). Assuming a valid ID token from a valid issuer with the expect audience (your PKCE app's client id), it seems reasonable that the claims could control the cert contents. Assuming of course your ID Token has all the claims you need.

Given the inconsistencies between ID Providers and contents of Access Tokens and ID Tokens, as well as the bias towards making as few external calls from the Lambda as possible, I'd suggest building general JWT validation logic. That way you could configure a schema for additional JWT payload structure validation, and that schema could support Access tokens, ID tokens, or custom tokens.

Lastly, take a look at https://github.com/Netflix/bless/blob/master/bless/cache/bless_lambda_cache.py and you could extend that to cache the JWT signing key.

stoggi added a commit to stoggi/bless that referenced this issue Aug 7, 2019
* Created new configuration section for JWT Auth
 - Configure a JWK to verify a JWT signature
 - Configure requried signature algorithms
 - Configure required audience and issuer claims
 - Configure name of username claim
* Added code block in lambda_handler_user to validate JWT if configured
 - Require remote_usernames == bastion_user
 - Require valid JWT signature, expiry, and signature algorithm
 - Require username_claim in JWT
 - Require username_claim == bastion_user
* Added unit tests for config and JWT validation
@stoggi
Copy link
Author

stoggi commented Aug 7, 2019

@russell-lewis thank you for your detailed answer above. I had a go at JWT validation for BLESS #100

I opted to allow the user to hardcode their JSON Web Key (JWK) instead of fetching it at runtime. For OpenID Connect you could find this key in your .well-known/openid-connect endpoint. But I think this strikes a good balance between following the OpenIDConnect standard, and verifying generic JWTs. Maybe a future PR could let you specify the URL and cache it.

For example, with Google you can fetch the jwks_uri in: https://accounts.google.com/.well-known/openid-configuration

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

No branches or pull requests

2 participants