gateway

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package gateway is the funcd catalog PEP proxy over the Quack/DuckLake serving path (ADR-0137, FEAT-0008/F102). It brings catalog under the same per-caller, per-query Cedar PEP as blob/kv/S3, for BOTH caller classes, by putting funcd on the query path via a proxy that fronts the engine — the S3 gateway shape (ADR-0085/0088): one proxy door, a per-caller credential resolved to a Cedar principal, a per-op PEP, the real backend credential (the shared engine token) used only after allow.

A Quack token is a BARE BEARER credential (unlike an S3 access key, which is inert without the SigV4 secret), so the internal per-function token must be unforgeable on its own. It is a standard HS256 JWT (go-jose) signed with the node master: DeriveCatalogToken signs it, PrincipalFor verifies the signature (alg pinned to HS256) before trusting its {ns, fn} claims. The external Identity gets a minted, random, rotatable token resolved by a store lookup (the ADR-0088 IdentityAccessKey analog).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeriveCatalogToken

func DeriveCatalogToken(master []byte, ns v1.NamespaceName, fn v1.ObjectName) (string, error)

DeriveCatalogToken mints the per-(namespace, function) catalog token as an HS256 JWT signed with the node master (ADR-0137). A Quack token is a bare bearer credential, so it must be unforgeable on its own: only the master holder can produce the signature, so a function cannot forge a peer's token. Deterministic — the same (master, ns, fn) always yields the same compact JWT (no time claims), so injection re-derives it without stored state.

func NewCatalogProxy

func NewCatalogProxy(keys CatalogKeys, pdp auth.Authorizer, engine EngineTarget) http.Handler

NewCatalogProxy builds the catalog PEP proxy handler fronting one CatalogService engine (ADR-0137). It logs through slog.Default; the Manager builds its proxies over its own logger (ADR-0002 §6).

Types

type CatalogKeys

type CatalogKeys interface {
	// PrincipalFor resolves token to its principal. First: verify it as an HS256 JWT signed with the node
	// master (a forged {ns, fn} fails the signature) ⇒ a Function principal. Else: a store lookup of a
	// minted per-Identity token ⇒ an Identity principal. Else: default-deny (ok=false).
	PrincipalFor(token string) (auth.EntityRef, bool)
}

CatalogKeys resolves a presented catalog token to its Cedar principal (ADR-0137). It mirrors s3gateway.principalFor, with the JWT signature standing in for SigV4's per-request possession proof.

func NewCatalogKeys

func NewCatalogKeys(master []byte, s store.Store) CatalogKeys

NewCatalogKeys builds the production CatalogKeys over the node master and the metastore.

type EngineTarget

type EngineTarget struct {
	Catalog     auth.EntityRef // Type=KindCatalogService, Name=<catalog>; namespace overridden per-principal
	Upstream    string         // the netns engine endpoint (an http URL)
	EngineToken string         // the shared engine token, never exposed to callers
}

EngineTarget is the single CatalogService this proxy endpoint fronts (ADR-0137): its Cedar resource ref (the catalog::query resource — its NAME fixes the target catalog; the namespace is taken from the resolved principal), its netns engine URL, and the shared engine token the caller token is swapped for on allow. One EngineTarget per listener endpoint, so the target catalog is fixed by the endpoint (never parsed from the opaque Quack body).

The ADR Contracts sketch typed Catalog as `v1.EntityRef`; there is no such type — the PDP resource is auth.EntityRef (a CatalogService ref), which is what this carries.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns the per-CatalogService catalog PEP proxies (ADR-0137): it runs ONE node-private http.Server per catalog, each fronting that catalog's engine. The CatalogService reconciler calls Ensure on its Ready branch to (re)bind a node-private listener for the catalog and learn the proxy URL that internal functions are injected with (FUNCD_CATALOG_<ALIAS>_URL), Suspend while it is not Ready, and Remove on teardown. One listener endpoint per catalog fixes the PEP's target catalog by the endpoint (the proxy takes the namespace from the resolved principal — see NewCatalogProxy).

func NewManager

func NewManager(bindHost, publishHost string, keys CatalogKeys, pdp auth.Authorizer, log *slog.Logger) *Manager

NewManager builds the catalog proxy Manager over the shared token resolver + PDP (ADR-0137). The same CatalogKeys/Authorizer back every per-catalog proxy; each proxy differs only in its EngineTarget (the catalog ref + its netns engine URL + shared engine token). bindHost is the interface each proxy binds; publishHost is the host injected into functions (see Manager). Both empty ⇒ 127.0.0.1 (the process-runtime/dev default, shared loopback); under containerd pass bindHost "0.0.0.0" and publishHost the CNI bridge gateway IP so a worker in its own netns can reach the proxy.

func (*Manager) Ensure

func (m *Manager) Ensure(catalog auth.EntityRef, upstream, engineToken string) (string, error)

Ensure guarantees a node-private proxy is running for catalog, fronting upstream with engineToken, and returns the bare "<publishHost>:<port>" host:port the function injects as FUNCD_CATALOG_<ALIAS>_URL (its handler wraps it in quack://; the engine endpoint was bare too). Under containerd publishHost is the CNI bridge gateway IP (netns-reachable), NOT the bind interface; in dev both are 127.0.0.1. If a proxy already runs for the catalog it is reused (idempotent — a re-reconcile does not rebind); if the upstream or engineToken differs, the running proxy is retargeted in place and its URL stays the same. Thread-safe.

func (*Manager) Remove

func (m *Manager) Remove(ns v1.NamespaceName, name v1.ObjectName)

Remove stops and forgets the proxy for a catalog (ADR-0137 teardown path). Idempotent: removing an unknown catalog is a no-op.

func (*Manager) Shutdown

func (m *Manager) Shutdown()

Shutdown stops every running proxy (daemon shutdown). Thread-safe and idempotent.

func (*Manager) Suspend added in v0.2.2

func (m *Manager) Suspend(ns v1.NamespaceName, name v1.ObjectName)

Suspend keeps a catalog's listener, so the URL consumers were injected with stays valid (#59), but answers every request 503 and drops the engine target and token until Ensure retargets it: a not-Ready catalog serves no caller (#372). Suspending an unknown catalog is a no-op.

Jump to

Keyboard shortcuts

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