How workload tokens are issued
Where managed identities live, how a pod proves which workload it is, and what the token endpoint refuses.
Where identities live
The Managed Identity service keeps no database. Each identity is a confidential client in the
platform's identity provider, named mi- followed by the resource's ID, with service-account
tokens enabled and nothing else. The client's description records the resource's type, name,
resource group and boundary, and that record is where the service reads an identity's placement
from. The identity's principal ID is the client's service-account user.
The resource's ID is its Kubernetes UID for apps and function namespaces, and an ID derived from the boundary, resource group and name for container instances.
The token endpoint
Pods fetch tokens from the service inside the cluster:
POST http://module-managedidentity.stackship-system:8080/api/identities/<resource-uid>/tokenThe operator puts this address into workload pods as STACKSHIP_IDENTITY_TOKEN_ENDPOINT, taken
from its setting ManagedIdentity__TokenEndpoint. The installer sets that to the platform's
namespace. Without it the operator falls back to
http://module-managedidentity.stackship-system.svc.cluster.local:8080/api/identities, which is
right only for a platform in stackship-system. Setting ManagedIdentity__Enabled to false
stops the variables for apps and functions; container instances get them regardless, whenever a
component needs the identity.
The endpoint is not published through the platform API, and it refuses callers whose address is not a private or cluster-internal one.
How a pod proves who it is
A pod has no platform credential when it asks — that is the point of the endpoint — so the resource UID in the path is only an address, not a secret. The proof is a Kubernetes service-account token that the operator has the kubelet project into the pod:
- at
/var/run/secrets/stackship.se/identity/token, named bySTACKSHIP_IDENTITY_POD_TOKEN_PATH; - minted for the audience
stackship-managed-identity:followed by that resource's UID, and valid for ten minutes; - mounted into every container that is told which identity to use.
The pod sends it in the header X-Stackship-Pod-Token. The service has the platform's kernel
review it with the Kubernetes API server of the cluster the workload runs on, for the audience of
the identity being asked for — so a pod's token only ever passes for its own resource's identity —
and then requires the pod's namespace to be the namespace of the identity's resource group.
Important
The audience binding keeps one workload's pod from using its proof for another workload. It does not keep out people who hold rights in the resource group's namespace:
- Anyone who can run commands in the workload's pods — Kubernetes
pods/exec, which Owner, Contributor, Apps Operator, Blueprints Operator, Platform Owner and Platform Contributor carry when assigned on the resource group or above — can read the proof file and fetch the identity's tokens.- Anyone who can create pods in the namespace — Owner, Contributor, Platform Owner and Platform Contributor, with direct cluster access — can have a token minted for any
stackship-managed-identity:<uid>audience, and so obtain any identity in that resource group.Treat those rights as equal to holding the roles of every identity in the resource group.
Only then does the service obtain an access token from the identity provider and return it. The identity's client secret never leaves the service on this path.
What is refused
Every refusal is the same 404: no such identity, no proof, a proof that does not pass review, a
pod in another namespace, a caller from outside the cluster. The service's log says which. When
the kernel cannot be reached, the endpoint refuses rather than hands out identities, so workloads
with secrets do not start until it is back.
Creating and deleting identities
The platform creates an identity when an app, function namespace or container instance is created, and deletes it when the resource is deleted; the operator also creates a missing one for an app or function namespace it reconciles, and one for each S3 storage account. Creating is idempotent: an identity that already exists for the resource is reused. The identity is given no role.