Categorygithub.com/xenitab/aad-oidc-identity
module
0.0.0-20220104070907-1a421e1e5ab9
Repository: https://github.com/xenitab/aad-oidc-identity.git
Documentation: pkg.go.dev

# README

aad-oidc-identity

PLEASE NOTE: This is a pre-alpha proof of concept. The name is just a placeholder for something meaningful in the future and borrowed from aad-pod-identity.

This proof of concept aims to showcase the new federated identity credentials in Azure AD working together with Service Account Token Volume Projection and Service Account Issuer Discovery in Kubernetes to create a secure way for applications to get Azure AD tokens, kind of like IRSA.

POC TODO

  • Initial project setup
  • Service Account Issuer Discovery with go-oidc-middleware
  • External endpoint for OIDC Metadata (/.well-known/openid-configuration)
  • External endpoint for JWKs (/jwks)
  • Request Azure AD access tokens using federated identity credentials with JWT created with JWK
  • Internal endpoint for token switching from Kubernetes JWT to Azure AD JWT (/token/azure)
  • Create a small overview diagram
  • Document the flow to showcase the functionality
  • Client ID discovery through annotation
  • Tenant ID discovery through annotation
  • Scope discovery through annotation
  • Store JWK in Kubernetes secret
  • Add AWS support
  • Add Google support
  • Document AWS support
  • Document Google support

AWS NOTES

Get your thumbprint by following this guide: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html

ISSUER=aks-oidc.domain.com
THUMBPRINT=$(echo | openssl s_client -connect ${ISSUER}:443 2>&- | openssl x509 -fingerprint -noout | sed 's/://g' | awk -F= '{print tolower($2)}')
aws iam create-open-id-connect-provider --url https://${ISSUER} --thumbprint-list ${THUMBPRINT} --client-id-list api://AWSTokenExchange

Request token using:

TOKEN=$(cat /var/run/secrets/tokens/oidc-token)
curl -v -H "Authorization: Bearer ${TOKEN}" -k http://aad-oidc-identity/token/aws

GOOGLE NOTES

When setting up the trust from the external identity to the Service Account, go into IAM > Service Accounts > [service account] > Permissions.

From there, click "Grant Access" and set the "New Principal to:

principal://iam.googleapis.com/projects/GOOGLE_PROJECT_ID/locations/global/workloadIdentityPools/GOOGLE_POOL_ID/subject/system:serviceaccount:K8S_NAMESPACE:K8S_SERVICE_ACCOUNT_NAME

Grant it Workload Identity User role as well as the roles required to do what it needs.

Request token using:

TOKEN=$(cat /var/run/secrets/tokens/oidc-token)
curl -v -H "Authorization: Bearer ${TOKEN}" -k http://aad-oidc-identity/token/google

Overview

High level diagram

overview

Flow

  1. Create a private key and store it as a Kubernetes secret (will be used to sign tokens sent to Azure AD)

    step-cli crypto keypair tmp/tls.crt tmp/tls.key --insecure --no-password --kty=RSA --size=2048
    kubectl create secret generic aad-oidc-identity-jwks --from-file=tls.key=tmp/tls.key --from-file=tls.crt=tmp/tls.crt
    
  2. Azure AD app is created and federated identity credentials is configured

    AZ_APP_OBJECT_ID=$(az ad app show --id 00000000-0000-0000-0000-000000000000 --output tsv --query objectId)
    az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/${AZ_APP_OBJECT_ID}/federatedIdentityCredentials' --body '{"name":"AKSCluster","issuer":"https://aks-oidc.domain.com","subject":"system:serviceaccount:team1:team1","description":"AKS Cluster authentication with aad-oidc-identity","audiences":["api://AzureADTokenExchange"]}'
    
  3. Service account is created with an annotation for the client id

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        aad-oidc-identity.xenit.io/client-id: 00000000-0000-0000-0000-000000000000
        aad-oidc-identity.xenit.io/role-arn: arn:aws:iam::123456789:role/test
      namespace: team1
      name: team1
    
  4. A pod is created using the service account and Service Account Token Volume Projection

    apiVersion: v1
    kind: Pod
    metadata:
      name: app
      namespace: team1
    spec:
      serviceAccountName: team1
      containers:
        - image: nginx:alpine
          name: app
          volumeMounts:
            - mountPath: /var/run/secrets/tokens
              name: oidc-token
      volumes:
        - name: oidc-token
          projected:
            sources:
              - serviceAccountToken:
                  path: oidc-token
                  expirationSeconds: 7200
                  audience: aad-oidc-identity
    
  5. The pod (app) requests a token from aad-oidc-identity

    TOKEN=$(cat /var/run/secrets/tokens/oidc-token)
    curl -H "Authorization: Bearer ${TOKEN}" -k http://aad-oidc-identity/token/azure
    

    Note: future iterations may include what scopes are requested here.

  6. aad-oidc-identity receives the request and validates the token using Service Account Issuer Discovery

  7. NOT IMPLEMENTED YET: Get Client ID (and Tenant ID if available - maybe even scopes if not included in the request) through the Kubernetes API from the Service Account

  8. aad-oidc-identity creates a JWT (with the sub system:serviceaccount:team1:team1) and signs it with its own JWK

  9. aad-oidc-identity sends the new JWT using the Client Credentials Grant Flow to Azure AD

  10. Azure AD goes out to the OIDC Discovery Endpoint (metadata) based on the configured issuer, https://aks-oidc.domain.com/.well-known/openid-configuration, and grabs the jwks_uri from the JSON response

  11. Azure AD goes out to the jwks_uri, https://aks-oidc.domain.com/jwks, and downloads the public key(s)

  12. Azure AD validates the token based on the downloaded public key(s) and if valid issues an Azure AD access token

  13. aad-oidc-identity receives the Azure AD access token and responds with it to the pod (app)

  14. The pod now has an Azure AD access token that it can use for whatever tasks needed

Development

Testing

kubectl apply -f test/client-deployment.yaml
kubectl exec -it client /bin/sh
TOKEN=$(cat /var/run/secrets/tokens/oidc-token)
curl -v -H "Authorization: Bearer ${TOKEN}" -k http://aad-oidc-identity/token/azure

Add custom federated identity

az rest --method POST --uri 'https://graph.microsoft.com/beta/applications/${APP_OBJECT_ID}/federatedIdentityCredentials' --body '{"name":"Testing","issuer":"${EXTERNAL_ISSUER}","subject":"system:serviceaccount:default:default","description":"Testing","audiences":["api://AzureADTokenExchange"]}'

# Packages

No description provided by the author