Reach the admin API without your identity provider
Every admin caller over the network presents a JWT. A person presents
one by way of pistra login, a
workload by way of the token its platform already projects. So the
admin API is only as reachable as the issuer behind it. When that
issuer is the thing that broke, from an expired signing key, a
directory outage, or a misapplied policy that locked out the group you
were in, the admin API is a locked door. You cannot fix the issuer
through it.
The local socket is the other door. It answers to the filesystem instead of to an issuer, so it does not depend on anything that can go down somewhere else.
issuers: - name: corp url: https://login.example.com
admin: listen: 0.0.0.0:8485 # the network door, needs an issuer local_socket: /run/pistra/admin.sock policy_file: /etc/pistra/admin.cedar issuers: - issuer: corp audience: pistra-adminThat configuration turns both doors on at once. The socket is not a mode the gateway is in instead of serving the network. It is a second listener on the same API, and configuring an issuer does not take it away.
Turn it on before you need it. Turning it on is a configuration change, and a configuration change is what somebody locked out of the admin API cannot make.
Reaching it
Section titled “Reaching it”Use the following example:
pistra admin -socket /run/pistra/admin.sock /admin/v1/configOr set PISTRA_ADMIN_SOCKET and drop the flag. On Kubernetes the
chart turns the socket on by default and the door is kubectl exec:
kubectl exec pistra-0 -n pistra-system -- pistra admin /admin/v1/suspensions
kubectl exec pistra-0 -n pistra-system -- pistra admin \ PUT /admin/v1/suspensions/provider:openai \ -d '{"reason":"INC-4471 leaking prompt text","duration":"4h"}'Use pistra admin rather than curl, because the image is distroless
and has neither. It is a transport: a method, a path, a body, and
whatever the API answered. Every endpoint in the OpenAPI document is
reachable through it, and it exits non-zero on 400 and above.
That kubectl exec is worth noticing rather than working around.
Reaching this door means holding a Kubernetes credential. Kubernetes
RBAC has already gated that credential and its audit log has already
recorded it. It is an identity system that is still up when yours is
the thing that broke. Delegating to it avoids inventing a second
credential of our own to lose.
Who the caller is
Section titled “Who the caller is”The caller is whoever opened the socket, named by the peer credentials the kernel recorded:
actor.kind=local actor.name=unix:501 actor.subject=31904unix:501 is a uid the operating system enforces, and the subject is
the pid that asked, so the record names more than “somebody local”.
That is the difference between this and a loopback TCP listener, where
every caller is the same anonymous principal and the trail cannot tell
two of them apart.
The socket is created 0600 and owned by the gateway’s own user, so
“who may administer this gateway locally” is a question the filesystem
answers. Put it in a directory only that user can enter, because the
mode on the socket is the access control. The chart uses an emptyDir
at /run/pistra.
What it may do
Section titled “What it may do”Policy decides, the same as for anyone else. Local callers hold
Role::"local":
@id("local-break-glass")permit(principal in Role::"local", action, resource);That is the grant to narrow first, because it is the one with no directory behind it. A useful shape gives on-call what an incident needs and nothing more:
@id("local-can-look")permit(principal in Role::"local", action in Action::"read", resource);
@id("local-can-take-a-provider-out")permit(principal in Role::"local", action == Action::"suspend", resource)when { resource.id like "provider:*" };Now exec’ing into the pod lets you see the deployment and take a provider out of service, and does not let you mint yourself a key. Pair it with Suspend a key, provider or rule. The suspension expires on its own, so the incident does not leave a degraded state behind it.
You can also name one uid, though it is usually the wrong instinct in a container where everything runs as the same user:
@id("just-me")permit(principal, action, resource) when { principal.name == "unix:501" };Before there is a policy at all
Section titled “Before there is a policy at all”A gateway with no issuer and no policy_file lets local callers through
unconditionally. That is the state a fresh deployment is in, and it is
how the first key gets minted and the first policy installed. There is
nothing else to authenticate with yet, and no policy could be written
about an identity provider that has not been configured.
Once an issuer is configured a policy is required, and it then decides for local callers too. Without the grant above the socket is open and permits nothing, so the grant is not optional in production.
What it does not cover
Section titled “What it does not cover”This door does not cover a cluster that has lost quorum. No admin write succeeds without raft, suspensions included, so getting in through this door leaves you holding an API that can only read. That has its own way out, offline and on a stopped node. See Recover a cluster that has lost quorum.
The gateway announces the socket at WARN on every start:
level=WARN msg="local admin socket listening: whoever can open this fileadministers this gateway" path=/run/pistra/admin.sock policy=truepolicy=false means no policy is loaded and a local caller can do
anything on the admin plane. That is correct for a deployment that has
not been set up yet and is a finding anywhere else.
This door is a fallback for Connect your identity provider. Authorization says why both exist.