api

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 api reaches the extension servers an operator runs: gRPC services implementing one of the contracts published under api/, currently api.kms.v1.EncryptionService and api.auth.v1.AuthService.

Extension servers exist so an operator can plug in a backend the proxy has no built-in support for, such as an on-prem HSM or an internal key service for encryption, or a policy engine or session service for authentication.

Module dials every server named in the configuration and publishes the results as Connections, keyed by server name. Callers build their own clients over those connections rather than receiving finished ones, because the two do not correspond one-to-one: KMS is the client for the encryption service, and one is built per key, several of which may live on one server. Auth is the client for the authentication service, and there is at most one of those, since the proxy admits a caller on a single verdict.

Dialing happens when the connections are first demanded rather than on the first call over them, so a bad address, certificate, or credential is caught during construction. The connections outlive this package and are closed with the application, not by any client built over them.

Index

Constants

This section is empty.

Variables

View Source
var Module = fx.Options(
	fx.Provide(func(p APIParams) (Connections, error) {

		if err := p.Config.ExtensionServers.Validate(); err != nil {
			return nil, fmt.Errorf("invalid extension server configuration: %w", err)
		}

		out := make(Connections, len(p.Config.ExtensionServers))
		conns := make([]*connect.Conn, 0, len(p.Config.ExtensionServers))

		for i := range p.Config.ExtensionServers {
			es := &p.Config.ExtensionServers[i]

			conn, err := extensionConn(p.Pool, es)
			if err != nil {
				return nil, err
			}

			out[es.Name] = conn
			conns = append(conns, conn)
		}

		p.Lifecycle.Append(fx.StartHook(func(ctx context.Context) error {
			if err := connect.WaitReady(ctx, conns...); err != nil {
				return fmt.Errorf("extension server connection not ready: %w", err)
			}

			return nil
		}))

		return out, nil
	}),
)

Module provides the pooled connection for every configured extension server, built when the provider runs rather than on first use, so a bad dial target surfaces at construction instead of on the first encryption call, and opened on start so an unreachable server (or one whose certificate this proxy will not accept) fails startup. Per-call credentials are still only exercised by a real request.

Functions

This section is empty.

Types

type APIParams

type APIParams struct {
	fx.In

	Config    *config.Config
	Lifecycle fx.Lifecycle
	Pool      *connect.Pool
}

APIParams collects the fx-provided dependencies needed to reach the configured extension servers. Pool is shared with the proxy's upstream connections; connect.Module owns it and closes every pooled connection on shutdown, which is why KMS.Close is a no-op.

type Auth added in v0.4.0

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

Auth authenticates an inbound stream by delegating the decision to an extension server implementing api.auth.v1.AuthService. It is the escape hatch for identity systems the built-in authenticators do not cover: the proxy asks, the operator's server decides.

The headers name the metadata carrying the caller's credentials. They are declared rather than discovered because a verdict reports only admit-or-deny and says nothing about which headers mattered, and the proxy needs to know two things: which values to lift into the request, and which to report as Auth.SecureHeaders so they are stripped from the stream before it reaches an upstream, where a caller credential would collide with the proxy's own.

func NewAuth added in v0.4.0

func NewAuth(cc grpc.ClientConnInterface, secureHeaders []string) *Auth

NewAuth returns an Auth that consults the AuthService reachable over cc and reports secureHeaders as the headers to strip from an admitted stream. As with NewKMS, cc is not owned here: it is shared with anything else configured on the same extension server and closed with the application.

func (*Auth) Authenticate added in v0.4.0

func (a *Auth) Authenticate(ctx context.Context, md metadata.MD) error

Authenticate asks the extension server whether the caller may proceed. Any response admits the stream and any error denies it, so a server that is down or cannot reach its own backend fails the request closed rather than opening the gateway to everyone for as long as it is unhealthy.

A denial reaches the caller as an api.Reject: the provider's status code is kept, since it tells a worker whether to fix its credential or retry, but its message is demoted to the server-side detail. A provider writes that message for whoever operates it, not for the caller it just turned away.

The declared credential headers are lifted into the request and withheld from the forwarded metadata, so each credential reaches the server in exactly one place. That separation is what lets the proxy hold a credential of its own to this server: metadata carries the proxy's, the request carries the caller's, and neither has to be told apart from the other on a shared header. It also puts the caller's credential out of reach of the interceptor outbound.DialOptions installs, which deletes the proxy's credential header from forwarded metadata and cannot tell that on this one call that header is the subject of the request rather than incidental cargo.

The caller's remaining metadata is forwarded so the server can weigh context such as the method being invoked. gRPC drops reserved keys (":authority", "user-agent", "content-type", "grpc-*") when writing the request, so a caller cannot reach the extension server's transport this way.

func (*Auth) SecureHeaders added in v0.4.0

func (a *Auth) SecureHeaders() []string

SecureHeaders returns the credential headers the proxy must strip before forwarding an admitted stream upstream. The result is a copy: the strip list is a security control, so a caller inspecting it cannot quietly shorten it.

type Connections

type Connections map[string]grpc.ClientConnInterface

Connections maps an extension server name to a connection to that server. It carries no lifecycle: closing a connection is the owner's responsibility, not the caller's.

Callers get connections rather than finished clients because the two do not correspond one-to-one: several keys may live on one extension server, so a caller builds one KMS per key over the shared connection.

type KMS

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

KMS wraps and unwraps data encryption keys on an extension server implementing api.kms.v1.EncryptionService. Only key material crosses the wire; payload plaintext never reaches the server.

The id names the key this client addresses. It is recorded in every DEK the key wraps and is what selects the key again when unwrapping, so it must stay stable for as long as any sealed payload references it.

func NewKMS

func NewKMS(id string, cc grpc.ClientConnInterface) *KMS

NewKMS returns a KMS addressing the key named by id over cc. Several keys may live on one extension server and share a connection, so cc is not owned here.

func (*KMS) Close

func (k *KMS) Close() error

Close is a no-op. The gRPC connection passed to NewKMS is owned by the caller that dialed it, which remains responsible for closing it; a KEKRegistry closing this KEK must not tear down a connection it does not own.

func (*KMS) Decrypt

func (k *KMS) Decrypt(ctx context.Context, ct []byte) ([]byte, error)

Decrypt decrypts a DEK previously produced by Encrypt.

func (*KMS) Encrypt

func (k *KMS) Encrypt(ctx context.Context, ns string, pt []byte) ([]byte, error)

Encrypt wraps a DEK via the remote provider, returning the ciphertext. The namespace is forwarded so the provider can select a per-namespace key.

func (*KMS) ID

func (k *KMS) ID() string

ID returns a unique ID for this KEK, e.g. a KMS ARN.

Jump to

Keyboard shortcuts

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