Skip to content

fix(workload-identity): allow-list issuer enums, label SPIFFE preview - #777

Open
lakhansamani wants to merge 1 commit into
mainfrom
security/workload-identity-enum-validation
Open

fix(workload-identity): allow-list issuer enums, label SPIFFE preview#777
lakhansamani wants to merge 1 commit into
mainfrom
security/workload-identity-enum-validation

Conversation

@lakhansamani

Copy link
Copy Markdown
Contributor

Follow-up to the review that produced #774#776, from the question "is SPIFFE
shipped as preview?".

It says preview in exactly one place — a Go comment

internal/constants/grant_types.go:31 reads "PREVIEW: the underlying draft
expired 2026-01-02 and is not WG-adopted. This URN is not IANA-registered and may
change. Ship as experimental only."

Nothing user-facing repeated that. README listed SPIFFE JWT-SVID with a ✅
alongside GA features — while already using a "beta" caveat for the Vue and
Svelte SDKs, so the convention existed and simply wasn't applied. Now labelled.

The part that was more than a docs gap

key_source_type and issuer_type were validated for non-emptiness only.
spiffe_bundle_endpoint is a declared constant with no implementation —
fetchJWKSBytes has no case for it and falls through to unsupported key_source_type — so an operator could create a trusted issuer, get a 200 and a
row that looked configured, and find out it was dead only when the first workload
tried to authenticate. A typo (static_jwks_urls) failed the same way at the
same unhelpful moment.

Both fields are now allow-listed, spiffe_bundle_endpoint refused by name
with "not implemented yet" rather than a generic invalid — those call for
different actions.

Nothing breaks

  • The dashboard offered spiffe_bundle_endpoint in its dropdown
    (UpdateTrustedIssuerModal.tsx:23-25). Left alone, this PR would have made the
    UI present an option guaranteed to error. Removed there too; the two lists are
    now the same list.
  • Existing rows are unaffected. Validation is write-time only,
    UpdateTrustedIssuerRequest cannot set either field, and the read path is
    untouched. A row already holding spiffe_bundle_endpoint was already broken
    and stays exactly as broken — no upgrade cliff.
  • Every value the Kubernetes path uses (oidc_discovery, static_jwks_url,
    kubernetes_sa) is allow-listed, and the existing gRPC/GraphQL trusted-issuer
    tests pass unchanged.

Dead code removed

AuthMethodX509MTLS ("x509_mtls", Phase 6): nothing read it, AddTrustedIssuer
hardcoded jwt_assertion, and no request field could set it — unreachable in
every direction. Removed rather than left as a claim the code does not honour.
AddTrustedIssuer now uses constants.AuthMethodJWTAssertion instead of a bare
literal, which is what made that constant look unused too.

KeySourceSPIFFEBundleEndpoint is deliberately kept — the new validator
rejects it by name, so the constant now has a job.

Verification

Both allow-list tests confirmed failing with the validator reverted.

go build ./...   OK
go vet ./...     OK
make test        exit 0 — 43 packages, 0 FAIL
make lint        exit 0  (Go + Prettier on both web apps)
make smoke       exit 0

Separately — a bigger finding, not fixed here

While checking this wouldn't break Kubernetes deployments I found that
in-cluster TokenReview cannot work at all, and the code says so at
client_assertion.go:492: SafeHTTPClient refuses private IPs, so
https://kubernetes.default.svc is unreachable — while performTokenReview
reads Authorizer's in-cluster SA token (/var/run/secrets/...) to authenticate
to it. The two assumptions contradict, and README lists Kubernetes TokenReview
as shipped with no caveat. Writing that up separately with a kind-based test.

key_source_type and issuer_type were checked for non-emptiness only, so
any string was stored verbatim. The value that mattered was
"spiffe_bundle_endpoint": a declared constant with no implementation —
fetchJWKSBytes has no case for it and returns "unsupported
key_source_type" — so an operator got a 200 and a row that looked
configured, and learned it was dead only when the first workload tried to
authenticate. A plain typo failed identically, at the same unhelpful
moment.

The dashboard offered spiffe_bundle_endpoint in its dropdown, so this
would otherwise have left the UI presenting an option guaranteed to
error. Removed there too — the two lists are now the same list.

Existing rows are unaffected: validation is write-time only,
UpdateTrustedIssuerRequest cannot set either field, and the read path is
unchanged. A row already holding spiffe_bundle_endpoint was already
broken and stays exactly as broken.

Also:
- README labels SPIFFE JWT-SVID preview, which grant_types.go has said
  since it landed ("Ship as experimental only") and nothing user-facing
  repeated. The README already uses that convention for the Vue and
  Svelte SDKs.
- AuthMethodX509MTLS removed: nothing read it, AddTrustedIssuer hardcoded
  jwt_assertion, and no request field could set it — unreachable in every
  direction. Reintroduce with the implementation, not ahead of it.
- AddTrustedIssuer now writes constants.AuthMethodJWTAssertion instead of
  a bare "jwt_assertion" literal, which is what made that constant look
  unused in the first place.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant