5232fcb808
R-023: Zero-trust enforcement operationally wired. ACL enforcement (C-45 staged rollout): - acl.Check wired into all 5 daemon handlers (dispatch/jobs/nodes/tasks) - health endpoints exempt (liveness probes not gated) - ACL log-only mode default (config acl.enforce=false); enforce after bootstrap ACL verified - sshpush auth: ORCA_OIDC_TOKEN validated against JWKS before apply - txn apply: Authorize hook validates OIDC token before running pull - acl.json mode 0600 (was 0644) - flock on acl.json for concurrent grant/revoke - bootstrap ACL: init grants cluster-admin to orca-admins group + SVID Audit actor identity: - currentActor reads OIDC sub from credentials.json (was hardcoded "cli") - threaded through all audit.Record calls via context WebAuthn registration auth: - BeginRegistration/FinishRegistration require authenticated session - fail-closed 401 when no authFunc configured New files: internal/daemon/acl.go, internal/cli/authactor.go, internal/engine/actor.go, internal/identity/authtoken.go, internal/sshpush/auth.go, internal/txn/auth_test.go ---ci--- project: orca phase: 4 milestone: v0.13 status: complete requirements: covered: [153] ---/ci---
61 lines
2.3 KiB
Go
61 lines
2.3 KiB
Go
// Package identity — authtoken.go provides the ORCA_OIDC_TOKEN
|
|
// validation helper used by the SSH-push applier and the txn apply
|
|
// path (P04, v0.13; C-44). Both paths validate the env-var token
|
|
// against the issuer's JWKS before applying any state change.
|
|
//
|
|
// The token is read from $ORCA_OIDC_TOKEN. The issuer + client ID
|
|
// come from the OIDC config (oidc.issuer, oidc.client_id). If the
|
|
// token is missing or invalid, the apply is refused. The verified
|
|
// claims (sub + groups) are returned so the caller can thread them
|
|
// into the audit actor field (T5) and the ACL check (T3/T4).
|
|
package identity
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// EnvOIDCToken is the environment variable holding the OIDC ID token
|
|
// for the SSH-push / txn apply paths (R-021: the IdP issues the
|
|
// token; Orca never issues its own).
|
|
const EnvOIDCToken = "ORCA_OIDC_TOKEN"
|
|
|
|
// VerifyOperatorToken reads $ORCA_OIDC_TOKEN and verifies it against
|
|
// the issuer's JWKS. Returns the verified claims (sub, groups) on
|
|
// success. Returns an error if the token is missing, expired, or
|
|
// fails signature verification.
|
|
//
|
|
// The issuer + clientID come from the OIDC config block. When issuer
|
|
// is empty, the function returns an error — the apply path requires
|
|
// an OIDC issuer to be configured.
|
|
func VerifyOperatorToken(ctx context.Context, issuer, clientID string) (*IDTokenClaims, error) {
|
|
raw := os.Getenv(EnvOIDCToken)
|
|
if raw == "" {
|
|
return nil, fmt.Errorf("identity: %s env var is not set (operator OIDC token required for apply)", EnvOIDCToken)
|
|
}
|
|
if issuer == "" {
|
|
return nil, fmt.Errorf("identity: oidc.issuer is not configured (required to verify %s)", EnvOIDCToken)
|
|
}
|
|
if clientID == "" {
|
|
clientID = "orca-cli"
|
|
}
|
|
claims, err := VerifyIDTokenStatic(ctx, issuer, clientID, raw)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("identity: verify %s: %w", EnvOIDCToken, err)
|
|
}
|
|
return claims, nil
|
|
}
|
|
|
|
// OperatorActor renders the verified operator identity for the audit
|
|
// `actor` field. The convention is "oidc:<sub>" so audit entries can
|
|
// be filtered by human operator. Falls back to "oidc:unknown" when
|
|
// claims are nil (e.g. when the caller could not verify the token but
|
|
// still wants to record an audit entry).
|
|
func OperatorActor(claims *IDTokenClaims) string {
|
|
if claims == nil || claims.Subject == "" {
|
|
return "oidc:unknown"
|
|
}
|
|
return "oidc:" + claims.Subject
|
|
}
|