auth

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package auth — outgoing-side helpers for principal propagation across service→service gRPC calls.

Server-side extraction lives in `corelib/grpcsrv/principal_extract.go` (UnaryPrincipalExtract / StreamPrincipalExtract) and is unchanged. This package complements it with the outgoing-MD side: client adapters wrap outgoing ctx with PropagateOutgoing so the peer's incoming-MD interceptor reconstructs the caller's Principal instead of falling back to SystemPrincipal() = user:bootstrap.

Why this exists

vpc/compute per-RPC authz interceptor calls `kaname.InternalIAMService.Check` from `clients/check_client.go` adapter. Without this wrap, outgoing gRPC carries no `x-kacho-principal-*` MD → iam-server-side `grpcsrv.UnaryPrincipalExtract` falls back to `operations.SystemPrincipal() = user:bootstrap` → every iam handler that later calls `operations.PrincipalFromContext` (audit, scope-filter, policy-overlay context.user.*) sees the wrong identity. Lifting the helper here means любой peer-client (loadbalancer, dns) получает корректную propagation простым импортом, без per-repo копий.

Security boundary

This helper is for **cluster-internal** service→service calls only. Both edges of api-gateway (the REST middleware and the native-gRPC interceptor) STRIP the ENTIRE client-supplied `x-kacho-` namespace — in the bare form and in the grpc-gateway `Grpc-Metadata-x-kacho-…` bridged form — so a tenant can inject neither a principal, nor an admin flag, nor a project scope. The strip is a namespace sweep, not a list of banned names: it previously enumerated `principal`/`token` only, and `x-kacho-admin` / `x-kacho-project-id` (read by compute/vpc) slipped through the bridge into a live privilege escalation.

Backends must NOT treat that strip as licence to trust the headers: the gateway is not the only way to reach a listener. Authz-bearing metadata is honoured only when it is both (a) forwarded by a trust-gated peer and (b) received on the listener where it is meaningful — see kacho-compute / kacho-vpc `internal/handler/tenant_interceptor.go`.

Index

Constants

View Source
const (
	MDKeyPrincipalType    = grpcsrv.MDKeyPrincipalType
	MDKeyPrincipalID      = grpcsrv.MDKeyPrincipalID
	MDKeyPrincipalDisplay = grpcsrv.MDKeyPrincipalDisplay
)

MD-key re-exports for ergonomic imports — callers shouldn't have to import `corelib/grpcsrv` just to know the wire-format header names. The canonical definitions remain in `corelib/grpcsrv/principal_extract.go` (server-side extract package owns the wire-format contract).

Variables

This section is empty.

Functions

func PropagateOutgoing

func PropagateOutgoing(ctx context.Context) context.Context

PropagateOutgoing forwards the caller's Principal onto outgoing gRPC metadata (`x-kacho-principal-type` / `-id` / `-display-name`) so the peer's `grpcsrv.UnaryPrincipalExtract` reconstructs the same Principal on the other side.

Semantics:

  • nil ctx → context.Background() (defensive — does not panic).
  • ctx without an explicit WithPrincipal → operations.PrincipalFromContext falls back to SystemPrincipal() (Type="system", ID="bootstrap"); that is a NON-empty principal and headers ARE forwarded — worker peer-calls stay attributable as system rather than going on the wire as identity-less.
  • ctx whose Principal has both Type=="" and ID=="" (an explicit empty WithPrincipal — should not happen in practice) → ctx is returned unchanged, no MD added.

Per gRPC metadata.AppendToOutgoingContext semantics, a second wrap appends rather than overwrites; the peer reads the first value via md.Get(key)[0]. Practically each outgoing RPC has exactly one wrap (in the outermost adapter method), so this never matters.

func SystemPrincipalFor

func SystemPrincipalFor(service, role string) operations.Principal

SystemPrincipalFor returns a typed system principal for worker / reconciler contexts that have no user identity (cron jobs, expirer workers, fga-outbox drainer, bootstrap workers).

Use INSTEAD of `operations.SystemPrincipal()` for any cross-service call originating from such a worker — `bootstrap` is reserved for the kaname bootstrap-admin seed and should not appear on the wire as the caller of normal operational traffic.

  • service: "vpc" | "compute" | "iam" | "api-gateway" | ...
  • role: "reconciler" | "expirer" | "drainer" | "bootstrap" | ...

Empty service or role → falls back to operations.SystemPrincipal() so an accidental empty-string caller does not produce a garbage `system:-` id.

Produces:

Principal{Type: "user", ID: "system.<service>-<role>", DisplayName: "<service>-<role>"}

"user" type (not "system") because FGA tuples and audit fields key on user-typed subjects; the `system.` prefix in the ID is the discriminator.

The separator is '.' (NOT ':') deliberately: ':' is an FGA-reserved char (type:id boundary) rejected by the receiving-side subject-sanitizer (authz.validSubjectID / FormatSubject). A ':' here would make every worker collapse to "user:unknown" and be denied fail-closed as anonymous. '.' passes the sanitizer, keeping each worker a distinct, attributable FGA subject.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL