Self-Hosted Deployment Identity Tokens
A self-hosted Massdriver server can sign deployment identity tokens for your deployments. This page shows how to turn them on in the Helm chart and how cloud trust setup differs from Massdriver Cloud. For the token format, levels, and bundle configuration, see Deployment Identity Tokens.
Identity tokens are off until you give the chart a signing key. Without a signing key, deployments get no token files and the discovery paths return 404.
Identity tokens are the recommended approach. Provisioner workload identity is still supported, but it only reaches the cloud that runs the cluster and gives every deployment the same cloud identity. See Identity Tokens vs. Provisioner Workload Identity.
Prerequisites​
Your cloud provider fetches the discovery document and the key set from the issuer URL. The issuer must be reachable over HTTPS from the public internet, with a certificate from a public certificate authority.
By default, the issuer is your Massdriver API URL, for example https://api.massdriver.example.com. If the API is not reachable from the internet, host the discovery documents elsewhere.
Step 1: Generate a Signing Key​
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out massdriver-oidc.pem
Store this key securely. Anyone with the key can sign tokens that your cloud accounts trust.
Step 2: Configure the Helm Chart​
Set the signing key in the provisioner.identityTokens section of your values-custom.yaml. Use one of the following options.
Option A: Key in the Values File​
The chart creates a Secret with the key:
provisioner:
identityTokens:
signingKey: |-
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
Option B: Existing Secret​
Create the Secret yourself, for example with kubectl or an external secrets operator. The key must be stored under signingKey:
kubectl create secret generic massdriver-identity-tokens \
-n massdriver \
--from-file=signingKey=massdriver-oidc.pem
Reference the Secret in values-custom.yaml:
provisioner:
identityTokens:
existingSecret: massdriver-identity-tokens
If existingSecret is set, the chart ignores signingKey and previousPublicKeys.
Optional Settings​
| Value | Default | Description |
|---|---|---|
provisioner.identityTokens.ttlMinutes | 120 | Token lifetime in minutes. Set it longer than your longest deployment. |
provisioner.identityTokens.issuer | The Massdriver API URL | The iss claim and the base URL of the discovery documents. Set it only to host the discovery documents elsewhere. |
All trust rules contain the issuer. If you change the issuer after you create trust rules, you must update all of them.
Step 3: Apply and Verify​
helm upgrade massdriver massdriver/massdriver \
-n massdriver \
-f values-custom.yaml
If the signing key is invalid, the Massdriver server does not start and logs the cause.
Check that the discovery document is published and that issuer has the expected value:
curl https://api.massdriver.example.com/.well-known/openid-configuration
Step 4: Set Up Cloud Trust​
Follow the cloud steps in Deployment Identity Tokens, but replace https://api.massdriver.cloud with your issuer. Replace ORG_ID with your organization ID. The bundle and resource type configuration does not change.
AWS​
aws iam create-open-id-connect-provider \
--url https://api.massdriver.example.com \
--client-id-list massdriver:ORG_ID
In the trust policy, use the issuer host without https:// in the federated principal and the condition keys:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::222222222222:oidc-provider/api.massdriver.example.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"api.massdriver.example.com:aud": "massdriver:ORG_ID",
"api.massdriver.example.com:sub": "org:ORG_ID:project:ecomm:env:prod"
}
}
}
]
}
GCP​
gcloud iam workload-identity-pools providers create-oidc massdriver \
--location=global \
--workload-identity-pool=massdriver \
--issuer-uri=https://api.massdriver.example.com \
--allowed-audiences=massdriver:ORG_ID \
--attribute-mapping="google.subject=assertion.sub,attribute.project=assertion.project,attribute.environment=assertion.environment" \
--attribute-condition="assertion.organization == 'ORG_ID'"
Azure​
az identity federated-credential create \
--name ecomm-prod \
--identity-name massdriver-ecomm-prod \
--resource-group massdriver-identities \
--issuer https://api.massdriver.example.com \
--subject org:ORG_ID:project:ecomm:env:prod \
--audiences massdriver:ORG_ID
Private Networks​
If the Massdriver API is not reachable from the internet, your cloud provider cannot fetch the discovery documents from it. Host the two documents on a public static site, such as an S3 bucket, and set the issuer to that site's URL.
The discovery document's jwks_uri is the issuer followed by /.well-known/jwks.json, so upload both files under .well-known/ at the root of the site.
Create the bucket and allow public reads of
.well-known/*:aws s3api create-bucket --bucket acme-massdriver-oidc --region us-east-1
aws s3api put-public-access-block --bucket acme-massdriver-oidc \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=false,RestrictPublicBuckets=false
aws s3api put-bucket-policy --bucket acme-massdriver-oidc --policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::acme-massdriver-oidc/.well-known/*"
}]
}'Set the issuer to the bucket URL, without a trailing slash, and apply the change with
helm upgrade:provisioner:
identityTokens:
existingSecret: massdriver-identity-tokens
issuer: https://acme-massdriver-oidc.s3.us-east-1.amazonaws.comFrom a host on the private network, download the documents from the Massdriver API. The server builds them from the new issuer:
curl -o openid-configuration https://api.massdriver.example.com/.well-known/openid-configuration
curl -o jwks.json https://api.massdriver.example.com/.well-known/jwks.jsonUpload the documents:
aws s3 cp openid-configuration s3://acme-massdriver-oidc/.well-known/openid-configuration --content-type application/json
aws s3 cp jwks.json s3://acme-massdriver-oidc/.well-known/jwks.json --content-type application/jsonCheck that the documents are public:
curl https://acme-massdriver-oidc.s3.us-east-1.amazonaws.com/.well-known/openid-configuration
Use the bucket URL as the issuer in your cloud trust setup. For AWS, the condition keys are acme-massdriver-oidc.s3.us-east-1.amazonaws.com:aud and acme-massdriver-oidc.s3.us-east-1.amazonaws.com:sub.
The hosted jwks.json does not update itself. Upload it again every time you rotate or change the signing key, before deployments use the new key.
Rotating the Signing Key​
Extract the public key from the current signing key:
openssl pkey -in massdriver-oidc.pem -pubout -out massdriver-oidc-previous.pubGenerate a new signing key.
Set the new signing key, and set the old public key as a previous public key:
Option A: set
provisioner.identityTokens.signingKeyto the new key andprovisioner.identityTokens.previousPublicKeysto the old public key.Option B: update the Secret. Store the new key under
signingKeyand the old public key underpreviousPublicKeys:kubectl create secret generic massdriver-identity-tokens \
-n massdriver \
--from-file=signingKey=massdriver-oidc-new.pem \
--from-file=previousPublicKeys=massdriver-oidc-previous.pub \
--dry-run=client -o yaml | kubectl apply -f -
Apply the change. For Option A, run
helm upgrade. For Option B, restart the server withkubectl rollout restart deployment/massdriver-massdriver -n massdriver. Tokens signed with the old key stay valid until they expire.If you host the discovery documents elsewhere, download and upload
jwks.jsonagain.After the token lifetime has passed, remove the previous public key and apply the change again.
To publish more than one previous key, put the PEM blocks one after the other in previousPublicKeys.
Troubleshooting​
| Symptom | Likely cause |
|---|---|
/massdriver/oidc/ is empty or missing in the provisioner pod | Neither signingKey nor existingSecret is set. |
| The Massdriver server does not start after the upgrade | The signing key or a previous public key is not valid PEM. The server log names the setting. |
The discovery paths return 404 | Identity tokens are not turned on. |
AWS InvalidIdentityToken: Couldn't retrieve verification key from your identity provider | The cloud cannot reach the issuer. Make the API public or host the discovery documents elsewhere. |
| Token verification fails after a key rotation | The hosted jwks.json is out of date. Upload it again. |
| The token is expired during a long deployment | Increase provisioner.identityTokens.ttlMinutes. |