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

Added federated OIDC JWT support. #680

Merged
merged 6 commits into from
May 4, 2022
Merged

Conversation

JustinJudd
Copy link
Contributor

In response to issue #671, I took a pass at adding in capability of using OIDC JWTs. Completed the CLA.

Thank you for your contribution to Go-AutoRest! We will triage and review it as soon as we can.

As part of submitting, please make sure you can make the following assertions:

  • I've tested my changes, adding unit tests if applicable.
  • N/A - No new files I've added Apache 2.0 Headers to the top of any new source files.

@JustinJudd
Copy link
Contributor Author

Friendly ping if a reviewer or maintainer can take a look at this.

@jhendrixMSFT jhendrixMSFT requested a review from chlowell April 15, 2022 19:01
Copy link
Member

@jhendrixMSFT jhendrixMSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need clarification in the usage of the Jwt field.

@JustinJudd
Copy link
Contributor Author

Yeah, I don't see any strong reason that the JWT would need to be exported in this instance. I updated the PR to make it internal.

@karlschriek
Copy link

Anything still blocking this? I opened up two downstream issues where I would love to implement workload identities as soon as this is merged.

@jhendrixMSFT
Copy link
Member

There's an open question about how to handle the JWT expiring. @karlschriek how is your app handling this?

@karlschriek
Copy link

karlschriek commented May 2, 2022

We use this for "Azure Workload Identities" on Kubernetes. Essentially we annotate a Pod's ServiceAccount, such as:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: external-dns
  labels:
    azure.workload.identity/use: 'true'
  annotations:
    azure.workload.identity/client-id: bbbbbbbb-xxx-yyyyy-mmmm-rrrrrrrrrr
    azure.workload.identity/service-account-token-expiration: '86400' # expiry after one day
    azure.workload.identity/tenant-id: yyyyyyy-xxx-zzzz-xxxx-rrrrrrrrrrrrr

Starting a Pod with the above service account results in the following ENV vars being mounted.

AZURE_AUTHORITY_HOST: https://login.microsoftonline.com/
AZURE_CLIENT_ID: bbbbbbbb-xxx-yyyyy-mmmm-rrrrrrrrrr
AZURE_FEDERATED_TOKEN_FILE: /var/run/secrets/azure/tokens/azure-identity-token
AZURE_TENANT_ID: yyyyyyy-xxx-zzzz-xxxx-rrrrrrrrrrrrr

In addition, a JWT token is mounted at the file location /var/run/secrets/azure/tokens/azure-identity-token.

My understanding is that after 86400 seconds the token at that location will be refreshed, i.e. the contents of the file is replaced (I cannot 100% confirm this, but that is what I assume happens - can check it a bit later). I noticed that the adal module has token refresh callbacks, though it isn't clear to me how they work. Am I to take from the comments above that these cannot be used to automatically refresh the token? I our case "refreshing" the token in-memory would simply mean reading it from the same location on disk again.

@karlschriek
Copy link

Just for more info, in the apps (e.g. in external-dns and cert-manager, which both currently make use of adal) we might then have something like this:

awiClientId := os.Getenv("AZURE_CLIENT_ID")
awiTenantId := os.Getenv("AZURE_TENANT_ID")
jwtBytes, err := ioutil.ReadFile(os.Getenv("AZURE_FEDERATED_TOKEN_FILE"))
jwt := string(jwtBytes)
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, awiTenantId)
spt, err := adal.NewServicePrincipalTokenFromFederatedToken(*oauthConfig, awiClientId, jwt, env.ResourceManagerEndpoint)

@karlschriek
Copy link

karlschriek commented May 2, 2022

By far the most convenient on client side would be to be able to pass for example adal.NewServicePrincipalTokenFromFederatedToken(*oauthConfig, awiClientId, jwtPath, expirySeconds, env.ResourceManagerEndpoint) and have adal just look up the token from jwtPath again after expirySeconds has passed. Is there any feasible way to imlement this?

@JustinJudd
Copy link
Contributor Author

Since the JWT has it's own expiry, there isn't a way to get refresh tokens using the JWT. Rather we are wrapping around this call to get a new JWT and then a new Azure access token when needed. Getting the JWT will be highly dependent on the Identity Provider in use.

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 this pull request may close these issues.

4 participants