Documentation
¶
Overview ¶
Package auth holds the two thin auth surfaces of the admin server:
- Agent: shared bearer token, and/or a client certificate when the agent listener runs mutual TLS (Config.AgentTLS with ClientAuth = RequireAndVerifyClientCert, set by --agent-client-ca). The TLS handshake itself rejects clients without a valid certificate; this package only attributes the resulting identity. Validated on every Connect-RPC call from an agent.
- UI: trusted-proxy header pass-through (X-Auth-User / X-Auth-Email) with optional bearer fallback. Per decision 14, the canonical deployment runs oauth2-proxy or equivalent in front of the UI listener; the server does NOT implement OIDC itself.
Index ¶
- Constants
- Variables
- func AgentMiddleware(token string, logger *slog.Logger) func(http.Handler) http.Handler
- func UIMiddleware(cfg UIConfig) func(http.Handler) http.Handler
- func ValidateTrustedCIDRs(cidrs []string) error
- func WithIdentity(ctx context.Context, id Identity) context.Context
- type Identity
- type UIConfig
Constants ¶
const ProxySecretHeader = "X-Auth-Proxy-Secret"
ProxySecretHeader is the header the trusted proxy uses to present UIConfig.ProxySecret. Fixed (not configurable) to keep the contract between proxy and server unambiguous.
Variables ¶
var ErrTrustedProxyMisconfigured = errors.New("admin server: malformed trusted_cidrs entry")
ErrTrustedProxyMisconfigured is returned by ValidateTrustedCIDRs (and wrapped by parseCIDRs) when a trusted-proxy CIDR entry is malformed.
Functions ¶
func AgentMiddleware ¶
AgentMiddleware returns an http middleware that enforces shared-token auth on the agent listener. When token is empty no credential is checked here: the listener is presumed bound to a private network, or the TLS listener already required and verified a client certificate (mutual TLS), in which case the peer certificate's Common Name is attached to the request context as Identity{Subject: "agent:<CN>"}.
logger receives a rate-limited WARN (one per minute per remote IP, with a count of the 401s suppressed in between) every time a request is rejected, so an operator can see in the server log that agents with a bad token are calling (OR5-2). Pass nil for slog.Default.
func UIMiddleware ¶
UIMiddleware authenticates UI requests. It returns a generic 401 on failure rather than leaking which credential mode was attempted.
func ValidateTrustedCIDRs ¶
ValidateTrustedCIDRs reports whether every entry parses as a CIDR. The server calls it at boot so a typo in --ui-trusted-cidrs refuses to start instead of being dropped silently (which used to leave the operator believing a proxy network was trusted when it was not).
Types ¶
type Identity ¶
type Identity struct {
Subject string // "agent:<NodeID>" for agents, the UI user for UI
Email string // empty for agents, optional for UI
Role string // "agent" | "ui-operator"
// ReadOnly marks an operator whose mutations must be refused
// (Data Studio create/update/delete/bulk). Set from the trusted
// proxy's role header or forced globally via UIConfig.ForceReadOnly.
ReadOnly bool
}
Identity is the minimal set of facts the rest of the server may rely on after a request has been authenticated. Used for observability, audit attribution, and the read-only gate on Data Studio mutations.
func IdentityFromContext ¶
IdentityFromContext returns the authenticated identity, or a zero Identity (Subject "") when the context carries none.
type UIConfig ¶
type UIConfig struct {
// BearerToken, when non-empty, lets a request authenticate by sending
// "Authorization: Bearer <BearerToken>". The token is checked AFTER
// trusted-proxy header pass-through.
BearerToken string
// AuthHeader and EmailHeader name the headers the trusted reverse
// proxy uses to forward identity (X-Auth-User / X-Auth-Email by
// default, configurable per Config.UIAuthHeader / UIEmailHeader).
AuthHeader string
EmailHeader string
// TrustedCIDRs is the allowlist of remote addresses whose
// trusted-proxy headers are honoured. Empty = "127.0.0.1/32, ::1/128".
TrustedCIDRs []string
// ProxySecret, when non-empty, is a shared secret the trusted proxy
// must echo in the ProxySecretHeader ("X-Auth-Proxy-Secret") for its
// forwarded identity to be honoured. CIDR membership alone is then no
// longer sufficient — it defends against any co-located process that
// can source packets from a trusted CIDR but does not know the secret.
// Empty keeps the CIDR-only behaviour.
ProxySecret string
// RoleHeader names the trusted-proxy header carrying the operator's
// role (default "X-Auth-Role"). Honoured only on the trusted-proxy
// path, together with AuthHeader. Value "viewer" / "readonly" /
// "read-only" (case-insensitive) marks the identity read-only; any
// other value — including absent — keeps the operator read-write.
RoleHeader string
// ForceReadOnly marks EVERY authenticated UI identity read-only,
// regardless of role header or credential mode. See
// Config.UIReadOnly.
ForceReadOnly bool
// InsecureOpen, when true, authenticates a credential-less request
// whose remote address is loopback as the fixed operator
// "insecure-open". Local-development convenience ONLY (the embedded
// SPA cannot present a bearer, so without a header-setting reverse
// proxy a browser can never load the UI). The server refuses to
// start with this set on a non-loopback UI listener; see
// Config.UIInsecureOpen.
InsecureOpen bool
}
UIConfig groups the UI-listener auth knobs.