About the Entra ID OpenID Connect
Integrate Entra ID OpenID Connect (OIDC) with SPIRE to provide workloads with automatic, short-lived cryptographic identities. This configuration allows you to securely authenticate services without maintaining static secrets.
Configuring the external certificate for the managed OIDC discovery provider route
Configure the managed OIDC discovery provider route to use an externally managed TLS certificate. By referencing a TLS secret, you can secure the OIDC endpoint with your own certificate credentials.
-
You have installed Zero Trust Workload Identity Manager 0.2.0 or later.
-
You have deployed the SPIRE Server, SPIRE Agent, SPIFFEE CSI Driver, and the SPIRE OIDC Discovery Provider operands in the cluster.
-
You have installed the cert-manager Operator for Red Hat OpenShift. For more information, Installing the cert-manager Operator for Red Hat OpenShift.
-
You have created a
ClusterIssuerorIssuerconfigured with a publicly trusted CA service. For example, an Automated Certificate Management Environment (ACME) typeIssuerwith the "Let’s Encrypt ACME" service. For more information, see Configuring an ACME issuer
-
Create a
Roleto provide the router service account permissions to read the referenced secret by running the following command:$ oc create role secret-reader \ --verb=get,list,watch \ --resource=secrets \ --resource-name=$TLS_SECRET_NAME \ -n zero-trust-workload-identity-manager -
Create a
RoleBindingresource to bind the router service account with the newly created Role resource by running the following command:$ oc create rolebinding secret-reader-binding \ --role=secret-reader \ --serviceaccount=openshift-ingress:router \ -n zero-trust-workload-identity-manager -
Configure the
SpireOIDCDIscoveryProviderCustom Resource (CR) object to reference the Secret generated in the earlier step by running the following command:$ oc patch SpireOIDCDiscoveryProvider cluster --type=merge -p=' spec: externalSecretRef: ${TLS_SECRET_NAME} '
-
In the
SpireOIDCDiscoveryProviderCR, check if theManageRouteReadycondition is set toTrueby running the following command:$ oc wait --for=jsonpath='{.status.conditions[?(@.type=="ManagedRouteReady")].status}'=True SpireOIDCDiscoveryProvider/cluster --timeout=120s -
Verify that the OIDC endpoint can be accessed securely through HTTPS by running the following command:
$ curl https://$JWT_ISSUER_ENDPOINT/.well-known/openid-configuration { "issuer": "https://$JWT_ISSUER_ENDPOINT", "jwks_uri": "https://$JWT_ISSUER_ENDPOINT/keys", "authorization_endpoint": "", "response_types_supported": [ "id_token" ], "subject_types_supported": [], "id_token_signing_alg_values_supported": [ "RS256", "ES256", "ES384" ] }%
Disabling a managed route
If you want to fully control the behavior of exposing the OIDC Discovery Provider service, you can disable the managed route based on your requirements.
-
To manually configure the OIDC Discovery Provider, set
managedRoutetofalseby running the following command:$ oc patch SpireOIDCDiscoveryProvider cluster --type=merge -p=' spec: managedRoute: "false"
Using Entra ID with Microsoft Azure
Configure your Microsoft Azure environment to enable Entra ID integration with Azure. By defining variables and creating a resource group, you establish the infrastructure needed to securely manage workload identities.
-
You have configured the SPIRE OIDC Discovery Provider Route to serve the TLS certificates from a publicly trusted CA.
-
Log in to Azure by running the following command:
$ az login -
Configure variables for your Azure subscription and tenant by running the following commands:
$ export SUBSCRIPTION_ID=$(az account list --query "[?isDefault].id" -o tsv)$ export TENANT_ID=$(az account list --query "[?isDefault].tenantId" -o tsv)$ export LOCATION=centraluswhere:
SUBSCRIPTION_ID-
Specifies your unique subscription identifier.
TENANT_ID-
Specifies the ID for your Azure Active Directory instance.
LOCATION-
The Azure region where your resource is created.
-
Define resource variable names by running the following commands:
$ export NAME=ztwim$ export RESOURCE_GROUP="${NAME}-rg"$ export STORAGE_ACCOUNT="${NAME}storage"$ export STORAGE_CONTAINER="${NAME}storagecontainer"$ export USER_ASSIGNED_IDENTITY_NAME="${NAME}-identity"where:
NAME-
Specifies A base name for all resources.
RESOURCE_GROUP-
Specifies the name of the resource group.
STORAGE_ACCOUNT-
Specifies the name for the storage account.
STORAGE_CONTAINER-
Specifies the name for the storage container.
USER_ASSIGNED_IDENTITY_NAME-
Specifies the name for a managed identity.
-
Create the resource group by running the following command:
$ az group create \ --name "${RESOURCE_GROUP}" \ --location "${LOCATION}"
Configuring Azure blob storage
Create a new Microsoft Azure storage account and container to provide a dedicated location for your content. Configuring this storage ensures that the Zero Trust Workload Identity Manager can successfully store and retrieve blobs for your environment.
-
Create a new storage account that is used to store content by running the following command:
$ az storage account create \ --name ${STORAGE_ACCOUNT} \ --resource-group ${RESOURCE_GROUP} \ --location ${LOCATION} \ --encryption-services blob -
Obtain the storage ID for the newly created storage account by running the following command:
$ export STORAGE_ACCOUNT_ID=$(az storage account show -n ${STORAGE_ACCOUNT} -g ${RESOURCE_GROUP} --query id --out tsv) -
Create a storage container inside the newly created storage account to provide a location to support the storage of blobs by running the following command:
$ az storage container create \ --account-name ${STORAGE_ACCOUNT} \ --name ${STORAGE_CONTAINER} \ --auth-mode login
Configuring an Azure user managed identity
Create a user-assigned managed identity in Azure to manage access control for your resources. You must also obtain the Client ID to associate roles with the service principal.
-
Create a new User Managed Identity and then obtain the Client ID of the related Service Principal associated with the User Managed Identity by running the following command:
$ az identity create \ --name ${USER_ASSIGNED_IDENTITY_NAME} \ --resource-group ${RESOURCE_GROUP}$ export IDENTITY_CLIENT_ID=$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv) -
Retrieve the
CLIENT_IDof an Azure user-assigned managed identity and save it as an environment variable by running the following command:$ export IDENTITY_CLIENT_ID=$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv) -
Associate a role with the Service Principal associated with the User Managed Identity by running the following command:
$ az role assignment create \ --role "Storage Blob Data Contributor" \ --assignee "${IDENTITY_CLIENT_ID}" \ --scope ${STORAGE_ACCOUNT_ID}
Creating the demonstration application
Create the demonstration application to verify that the entire system functions correctly. This process validates the configuration of your application secrets and namespaces.
-
Set the application name and namespace by running the following commands:
$ export APP_NAME=workload-app$ export APP_NAMESPACE=demo -
Create the namespace by running the following command:
$ oc create namespace $APP_NAMESPACE -
Create the application Secret by running the following command:
$ oc apply -f - << EOF apiVersion: v1 kind: Secret metadata: name: $APP_NAME namespace: $APP_NAMESPACE stringData: AAD_AUTHORITY: https://login.microsoftonline.com/ AZURE_AUDIENCE: "api://AzureADTokenExchange" AZURE_TENANT_ID: "${TENANT_ID}" AZURE_CLIENT_ID: "${IDENTITY_CLIENT_ID}" BLOB_STORE_ACCOUNT: "${STORAGE_ACCOUNT}" BLOB_STORE_CONTAINER: "${STORAGE_CONTAINER}" EOF
Deploying the workload application
Deploy the workload application to your cluster to validate the Zero Trust Workload Identity Manager environment. This application confirms that the SPIFFE Workload API is functioning and can successfully retrieve JWT tokens.
-
The demonstration application has been created and deployed.
-
To deploy the application, copy the entire command block provided and paste it directly into your terminal. Press Enter.
$ oc apply -f - << EOF apiVersion: v1 kind: ServiceAccount metadata: name: $APP_NAME namespace: $APP_NAMESPACE --- kind: Deployment apiVersion: apps/v1 metadata: name: $APP_NAME namespace: $APP_NAMESPACE spec: selector: matchLabels: app: $APP_NAME template: metadata: labels: app: $APP_NAME deployment: $APP_NAME spec: serviceAccountName: $APP_NAME containers: - name: $APP_NAME image: "registry.redhat.io/ubi9/python-311:latest" command: - /bin/bash - "-c" - | #!/bin/bash pip install spiffe azure-cli cat << EOF > /opt/app-root/src/get-spiffe-token.py #!/opt/app-root/bin/python from spiffe import JwtSource import argparse parser = argparse.ArgumentParser(description='Retrieve SPIFFE Token.') parser.add_argument("-a", "--audience", help="The audience to include in the token", required=True) args = parser.parse_args() with JwtSource() as source: jwt_svid = source.fetch_svid(audience={args.audience}) print(jwt_svid.token) EOF chmod +x /opt/app-root/src/get-spiffe-token.py while true; do sleep 10; done envFrom: - secretRef: name: $APP_NAME env: - name: SPIFFE_ENDPOINT_SOCKET value: unix:///run/spire/sockets/spire-agent.sock securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: false runAsNonRoot: true seccompProfile: type: RuntimeDefault ports: - containerPort: 8080 protocol: TCP volumeMounts: - name: spiffe-workload-api mountPath: /run/spire/sockets readOnly: true volumes: - name: spiffe-workload-api csi: driver: csi.spiffe.io readOnly: true EOF
-
Ensure that the
workload-apppod is running successfully by running the following command:$ oc get pods -n $APP_NAMESPACEExample outputNAME READY STATUS RESTARTS AGE workload-app-5f8b9d685b-abcde 1/1 Running 0 60s -
Retrieve the SPIFFE JWT Token (SVID-JWT):
-
Get the pod name dynamically by running the following command:
$ POD_NAME=$(oc get pods -n $APP_NAMESPACE -l app=$APP_NAME -o jsonpath='{.items[0].metadata.name}') -
Run the script inside the pod by running the following command:
$ oc exec -it $POD_NAME -n $APP_NAMESPACE -- \ /opt/app-root/src/get-spiffe-token.py -a "api://AzureADTokenExchange"
-
Configuring Azure with the SPIFFE identity federation
Configure Microsoft Azure with SPIFFE identity federation to enable password-free, automated authentication for the demonstration application. This federates the User Managed Identity with the SPIFFE identity associated with your workload application.
-
Federate the identities between the User Managed Identity and the SPIFFE identity associated with the workload application by running the following command:
$ az identity federated-credential create \ --name ${NAME} \ --identity-name ${USER_ASSIGNED_IDENTITY_NAME} \ --resource-group ${RESOURCE_GROUP} \ --issuer https://$JWT_ISSUER_ENDPOINT \ --subject spiffe://$APP_DOMAIN/ns/$APP_NAMESPACE/sa/$APP_NAME \ --audience api://AzureADTokenExchange
Verifying that the application workload can access the content in the Azure Blob Storage
Verify that your application workload can connect to the Azure Blob Storage. By uploading a test file, you validate the authentication token and ensure that the workload has the correct permissions.
-
An Azure Blob Storage has been created.
-
Retrieve a JWT token from the SPIFFE Workload API by running the following command:
$ oc rsh -n $APP_NAMESPACE deployment/$APP_NAME -
Create and export an environment variable named
TOKENby running the following command:$ export TOKEN=$(/opt/app-root/src/get-spiffe-token.py --audience=$AZURE_AUDIENCE) -
Log in to Azure CLI included within the pod by running the following command:
$ az login --service-principal \ -t ${AZURE_TENANT_ID} \ -u ${AZURE_CLIENT_ID} \ --federated-token ${TOKEN} -
Create a new file with the application workload pod and upload the file to the Blob Storage by running the following command:
$ echo “Hello from OpenShift” > openshift-spire-federated-identities.txt -
Upload a file to the Azure Blog Storage by running the following command:
$ az storage blob upload \ --account-name ${BLOB_STORE_ACCOUNT} \ --container-name ${BLOB_STORE_CONTAINER} \ --name openshift-spire-federated-identities.txt \ --file openshift-spire-federated-identities.txt \ --auth-mode login
-
Confirm the file uploaded successfully by listing the files contained by running the following command:
$ az storage blob list \ --account-name ${BLOB_STORE_ACCOUNT} \ --container-name ${BLOB_STORE_CONTAINER} \ --auth-mode login \ -o table