Documentation
¶
Overview ¶
Package s3gateway is the funcd S3-protocol frontend over the blob.Bucket substrate (ADR-0080), with in-platform identity via funcd-managed per-function SigV4 keypairs resolved by an in-process versitygw auth.IAMService (ADR-0085).
AuthN: every in-platform function gets a DETERMINISTIC keypair derived from a node-local master secret over its (namespace, function) Ref — funcd injects it into the sandbox env; the function never chooses its secret and cannot derive a peer's. AuthZ: every backend op is a PEP on the cedar PDP (s3::read / s3::write), exactly the spec.blob binding-as-grant model of slice 2.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecodeIdentityAccess ¶
DecodeIdentityAccess maps an Identity access key back to its (namespace, name). ok=false for a non-Identity or malformed key. Used by the store-backed ExternalKeys to resolve the Identity + secret.
func IdentityAccessKey ¶
IdentityAccessKey returns the stable SigV4 access key id for a user-assigned Identity (ADR-0135): "FUNCID" + base32-noPad-upper(ns + "\x00" + name). Deterministic in (ns, name); the SECRET is NOT derived (it is generated + stored in the Identity's owned Secret, so it is rotatable). Because the prefix is not "FUNCD", the Function decodeAccess skips it and the gateway resolves it via ExternalKeys.
func LoadOrCreateMaster ¶
LoadOrCreateMaster loads the node S3 master secret from file (ADR-0085), or, when the path is empty, from / generates+persists 0600 at <dataDir>/s3gateway/master.key. The secret is NEVER logged. A supplied masterSecretFile that does not exist is an error.
Types ¶
type Deps ¶
type Deps struct {
// BucketFor resolves (namespace, Bucket name) → the substrate bucket view. ok=false
// when no such Bucket exists for that namespace (⇒ NoSuchBucket).
BucketFor func(ns v1.NamespaceName, bucket string) (blob.Bucket, bool)
// Buckets lists a namespace's Bucket resources; HeadBucket and ListBuckets answer
// from it, keeping only the Buckets the caller is bound to.
Buckets func(ctx context.Context, ns v1.NamespaceName) ([]v1.Bucket, error)
// PDP is the cedar authorizer; every backend op is a PEP on it (s3::read/s3::write).
PDP authz.Authorizer
// Master is the node master secret keypairs are derived from (ADR-0085). Required.
Master []byte
// External is the optional external-keypair store (the untrusted SigV4 edge); nil ⇒
// in-platform only.
External ExternalKeys
// Listen is the node-private bind address (e.g. 127.0.0.1:9000).
Listen string
// MaxUploadBytes caps a single buffered (multipart/put) object; 0 ⇒ defaultMaxUpload.
MaxUploadBytes int64
// Logger is the server logger; nil ⇒ slog.Default(). The master secret is NEVER logged.
Logger *slog.Logger
}
Deps configures the s3gateway server (ADR-0080 + ADR-0085).
type ExternalKeys ¶
ExternalKeys is the funcd store of issued external scoped keypairs — the one untrusted edge (ADR-0085). It resolves an access key to its secret + scoped namespace for the S3Identity principal. Lifecycle UX (issue/rotate/revoke) is a follow-on; this read-only seam is all the gateway needs.
type Keypair ¶
type Keypair struct{ AccessKey, SecretKey string }
Keypair is a function's deterministic S3 credentials (ADR-0085). Pure value; no I/O.
func DeriveKeypair ¶
DeriveKeypair returns the stable per-(namespace, function) keypair (ADR-0085):
- AccessKey = "FUNCD" + base32-noPad-upper(ns + "\x00" + fn) — decodable to the Ref via decodeAccess; not secret (knowing it grants nothing).
- SecretKey = base64( HMAC-SHA256(master, "s3:"+ns+"/"+fn) ) — only the holder of master can compute it, so a function cannot forge a peer's secret.
Pure and deterministic: the same (master, ns, fn) always yields the same keypair, across daemon restarts (deterministic-across-restart). No storage, no rotation.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the funcd S3 gateway: a versitygw s3api.New server over the funcd backend + in-process IAM, bound to a node-private TCP listener (ADR-0085). It is built via s3api.New (NOT embedgw.RunVersityGW) so the custom IAM is injectable.
func New ¶
New builds the gateway server (ADR-0085). It validates Deps, constructs the in-process IAM + backend, and wires them through s3api.New with a random per-process root account (used by nothing external) so versitygw's required-root invariant is satisfied without granting anyone root. It does NOT bind a listener until Run is called.
func (*Server) Ready ¶
func (s *Server) Ready() <-chan struct{}
Ready returns a channel closed once the listener is bound and accepting (ADR-0085): callers (and tests) wait on it instead of a fixed sleep.