Rotating the Actor JWT Signing Key
The rotation steps, how long to wait between them, and replacing a compromised key.
ate-api-server signs actor JWTs with the active key in the actor-id-jwt-pool
Secret, and ate-idp-server publishes every key in the pool. A rotation adds a
key, waits until relying parties have it, makes it the signing key, and
removes the old key once nothing it signed can still be presented.
actor-jwts.md describes the pool.
The commands below use the default Secret. Set ISSUER to your
actorJWT.issuer:
ISSUER=https://idp.example.com
POOL="--name actor-id-jwt-pool --secret-namespace ate-system"
How long each step takes
- Reaching the pods. A change to the Secret reaches the ate-api-server and ate-idp-server pods when the kubelet next syncs the mounted Secret, and each process rereads the pool within a minute after that. Allow a few minutes.
- Reaching relying parties. Each relying party caches the key set for its own period. Unless every relying party documents a shorter one, wait a day.
- Tokens in flight. A token is valid for up to an hour. The egress gateway reuses a token until a third of its lifetime is left, so a token signed just before the new key took over can still be presented, and has to verify, for up to an hour after that.
Routine rotation
Note the active key:
kubectl ate admin list-jwt-keys $POOLAdd the new key. It is published but signs nothing yet. Pass
--alg RS256to change the algorithm; see Changing the algorithm.kubectl ate admin add-jwt-key $POOLWait until the key set lists the new key ID:
curl -s "$ISSUER/openid/v1/jwks" | jq -r '.keys[].kid'Then wait out the longest key set cache among your relying parties.
Make the new key the signing key:
kubectl ate admin activate-jwt-key $POOL --key-id <new>Wait at least an hour, plus a few minutes for the change to reach every ate-api-server pod.
Remove the old key:
kubectl ate admin remove-jwt-key $POOL --key-id <old>
Changing the algorithm
Follow the same steps and pass --alg to add-jwt-key, for example to move
to RS256 for a relying party that doesn’t accept ES256. Every relying party
has to accept the new algorithm before step 4. During the overlap the
discovery document’s id_token_signing_alg_values_supported lists both.
The actorJWT.algorithm setting only affects a pool the installer creates, so
rotating is the way to change an existing install.
Replacing a compromised key
Skip the waits:
Add a new key and activate it right away. Relying parties that haven’t fetched the new key reject tokens it signs until they refetch the key set.
Once the activation has reached every ate-api-server pod, remove the compromised key.
Restart the egress gateway so it drops the tokens it cached:
kubectl rollout restart deployment/atenet-egress -n ate-system
A relying party keeps accepting the compromised key until it refetches the key set, so make each one refetch if it offers a way to.