kms

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package kms wires the proxy's encryption configuration into a running crypto.Vault.

It reads config.Encryption, opens the configured keys as KEKs, assembles a crypto.KEKRegistry, and constructs the crypto.Vault that the rest of the proxy uses to seal and open payloads.

Keys are opened by a KeyFactory, which extends crypto.KeyFactory in two ways. It adds the "extension://" scheme, resolving such a key to an operator-run extension server over a connection supplied by the api package; several keys may name the same server, so each is identified by its whole URI. Every other scheme is a cloud KMS key that crypto opens through gocloud.dev/secrets (awskms, azurekeyvault, gcpkms, or a local testing key). It also meters what it opens, so each key's wraps and unwraps are recorded against its provider.

The package exposes a single Module for Uber fx. When encryption is disabled the module provides a nil *crypto.Vault and starts no background work; when enabled it also runs a goroutine that periodically refreshes the vault so DEKs rotate ahead of expiry.

The package also reports encryption telemetry to Prometheus under the "encryption" subsystem. A Reporter implements crypto.Observer to record DEK cache behavior, the AES-step duration and result of each envelope operation as dek_ops_duration_secs and dek_ops_total, and DEK rotations by reason; the metered keys above record their KMS wraps and unwraps against a provider as kek_ops_total and kek_ops_duration_secs. This package owns the DEK and KEK operations themselves; internal/proxy owns the end-to-end envelope operation, timing its own Seal and Open calls and reporting them as vault_ops_total and vault_ops_duration_secs, labeled by namespace.

Index

Constants

View Source
const ExtensionScheme = "extension"

ExtensionScheme addresses a key served by a configured extension server, as "extension://<server>/<key>". The server names an entry in the config's extensionServers list; the key is a proxy-side identifier that distinguishes several keys hosted by one server. It is never sent to the server, which selects keys by namespace when wrapping and reads the key back out of the self-describing ciphertext when unwrapping.

Variables

View Source
var Module = fx.Options(
	fx.Provide(

		func(p KMSParams) *Reporter {
			return NewReporter(p.Factory.ForSubsystem("encryption"))
		},
		func(p KMSParams, reporter *Reporter) *KeyFactory {
			return NewKeyFactory(p.Extensions, reporter)
		},
		func(p KMSParams, kf *KeyFactory, reporter *Reporter) (*crypto.Vault, error) {
			if p.Config.Encryption.Default == nil {
				return nil, nil
			}

			r, err := createKEKRegistry(
				p.Context,
				p.Lifecycle,
				p.Config,
				p.Logger,
				kf,
			)
			if err != nil {
				return nil, err
			}

			v, err := createVault(p.Config, r, reporter)
			if err != nil {
				_ = r.Close()
				return nil, err
			}

			return v, nil
		},
	),
	fx.Invoke(func(p KMSParams, v *crypto.Vault) {
		if !p.Config.Encryption.Enabled {
			return
		}

		ctx, cancel := context.WithCancel(p.Context)

		p.Lifecycle.Append(fx.Hook{
			OnStart: func(context.Context) error {
				go runRotation(ctx, v, rotationInterval, p.Logger)
				return nil
			},
			OnStop: func(context.Context) error {
				cancel()
				return nil
			},
		})
	}),
)

Module provides a *crypto.Vault whenever encryption keys are configured (a Default key policy is present) and, only while encryption is Enabled, runs background key rotation for the lifetime of the fx application. Building the vault from key presence rather than the Enabled flag lets encryption be turned off for new traffic while the vault stays available to open payloads sealed earlier: the proxy interceptor gates sealing on Enabled but always decrypts. With no key policy the vault is nil and no rotation runs.

Functions

func ProviderForScheme added in v0.3.0

func ProviderForScheme(scheme string) string

ProviderForScheme maps a KMS URI scheme to a stable, low-cardinality provider label. Unknown schemes are used unchanged so a new backend still produces a usable (if unrecognized) label rather than an empty one.

Types

type KMSParams

type KMSParams struct {
	fx.In

	Context   context.Context
	Config    *config.Config
	Lifecycle fx.Lifecycle
	Logger    logger.Logger
	Factory   *metrics.Factory

	// Extensions supplies the connections behind "extension://" key URIs.
	Extensions api.Connections
}

KMSParams are the fx dependencies used to construct and run the vault.

type KeyFactory added in v0.3.0

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

KeyFactory opens the proxy's configured keys, adding two things to crypto.KeyFactory: the "extension://" scheme, which resolves to an operator-run extension server rather than a cloud KMS, and metering, so every wrap and unwrap a key performs is recorded against its provider.

func NewKeyFactory added in v0.3.0

func NewKeyFactory(conns api.Connections, r kekRecorder) *KeyFactory

NewKeyFactory returns a KeyFactory serving the schemes crypto handles by default plus ExtensionScheme, which resolves against conns. Keys it opens record their operations to r.

conns is captured, not copied, and is looked up when a key is opened rather than here, so an extension URI naming a server absent from conns fails at that point and not at construction.

func (*KeyFactory) Create added in v0.3.0

func (f *KeyFactory) Create(ctx context.Context, uri string) (crypto.KEK, error)

Create opens the key addressed by uri and wraps it so its KMS calls are metered. The provider label is derived from the URI's scheme, which keeps the metric's cardinality tied to the number of backends rather than to the number of configured keys.

type Reporter

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

Reporter records encryption telemetry to Prometheus: KEK wrap/unwrap calls, the AES-step duration of DEK operations, and DEK cache behavior. It implements crypto.Observer so a Vault can notify it of those events, and exposes KEKOp for the KEK decorator. The end-to-end envelope operation is owned by internal/proxy, which times its own Seal and Open calls and labels them by namespace. Handles for the low-cardinality label combinations are pre-resolved so the emit path is a lock-free map read; an unexpected combination falls back to WithLabelValues. A Reporter is safe for concurrent use.

func NewReporter

func NewReporter(f *metrics.Factory) *Reporter

NewReporter builds the Prometheus-backed encryption Reporter, pre-resolving the meaningful KEK label combinations so every series starts at zero. f must already be scoped to the "encryption" subsystem by the caller.

func (*Reporter) KEKOp

func (r *Reporter) KEKOp(provider, operation, result string, seconds float64)

KEKOp records a single KEK operation and its duration.

func (*Reporter) Observe added in v0.3.0

func (r *Reporter) Observe(e crypto.Event)

Observe records e against the metric its type owns. There is no default case: an event type this switch does not recognize means this binary's pkg/crypto is newer than this Reporter. Dropping it is deliberate, not an oversight. Metric emission must never alter behaviour, so panicking or returning an error on an unrecognized event is not available here; a missing metric is a missing signal, not corrupted data.

Jump to

Keyboard shortcuts

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