proxy

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 35 Imported by: 0

Documentation

Overview

Package proxy serves every allowlisted service on a local unix socket, forwarding each request to an upstream Temporal frontend over gRPC. The socket path is derived from the upstream host:port, so local workers connect without TLS while the upstream hop stays secured.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EncryptionInterceptor

func EncryptionInterceptor(enabled bool, v Vault, r *Reporter) (grpc.UnaryClientInterceptor, error)

EncryptionInterceptor returns a unary client interceptor that opens inbound response payloads using v and, when enabled is true, seals outbound request payloads as well. Sealing is gated so encryption can be turned off for new traffic while still opening data sealed earlier: inbound decryption always runs. Each payload is sealed under the DEK for the request's namespace, read from the outgoing gRPC metadata via meta.NamespaceFrom, so the upstream never sees plaintext while local workers still exchange cleartext. On the way back only payloads this interceptor sealed (identified by the encryptionEncoding marker) are opened; anything else passes through untouched. Search attributes are skipped so they stay queryable upstream. r records the duration and result of every seal/open through VaultOp. It returns an error only if the underlying visitor interceptor cannot be constructed.

func ResolverFor added in v0.5.0

func ResolverFor(upstream *config.Upstream, opts []grpc.DialOption, log logger.Logger) (connect.Resolver, error)

ResolverFor builds the connect.Resolver for an upstream. When neither the hostPort nor the TLS server name is templated it returns a static resolver, whose connection is constructed while the graph is built, opened on start, and reused for every request; otherwise it returns a DynamicResolver that renders the target and server name, and rebuilds credentials, per request. opts holds the request-independent dial options (namespace translation and outbound credentials). log, when non-nil, is threaded into the DynamicResolver for per-request debug entries.

func TranslationDialOptions added in v0.5.0

func TranslationDialOptions(t *protoutil.Translator, out, in func(string) string) []grpc.DialOption

TranslationDialOptions returns the dial options that install namespace translation on the outbound connection: t rewrites message bodies and typed error details, out maps local names to remote on the way out, and in maps remote names to local on the way back. Callers fold them into the dial options for the upstream connection.

Types

type DynamicResolver

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

DynamicResolver is a connect.Resolver that renders an upstream's dial target (and optional TLS server name) per request from the local namespace and request metadata. It always reports IsStatic as false, so a connect.Conn built from it resolves lazily on every call. A non-templated hostPort renders to itself, so a DynamicResolver also serves upstreams with a fixed address. Construct one with NewDynamicResolver.

func NewDynamicResolver

func NewDynamicResolver(up *config.Upstream, opts ...ResolverOption) (*DynamicResolver, error)

NewDynamicResolver builds a DynamicResolver for up. It compiles the hostPort and TLS server-name templates (failing if either is malformed) and applies opts. By default the remote namespace equals the local one and no dial options are added; use WithRemoteNamespacer and WithOptionsFactory to change that.

func (*DynamicResolver) IsStatic

func (r *DynamicResolver) IsStatic() bool

IsStatic reports that a DynamicResolver always resolves per request.

func (*DynamicResolver) Resolve

Resolve renders the dial target and server name from ctx and returns the pool cache key, the dial target, and the dial options. The cache key combines the target and rendered server name so that two requests to the same address with different server names get distinct pooled connections. It fails with codes.Internal (naming the upstream and template) when a template fails to render, the rendered address is empty or malformed, or the options factory errors; nothing is dialed in those cases.

type Forwarder added in v0.5.0

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

Forwarder forwards any allowlisted method to a single upstream, typing each request and response from the proto registry rather than being generated per service. The typing is load-bearing: namespace translation and payload encryption are client interceptors on cc that operate on proto messages, so an opaque byte passthrough (as the router uses) would silently skip both. Resolved methods are cached, and a Forwarder is safe for concurrent use.

func NewForwarder added in v0.5.0

func NewForwarder(cc grpc.ClientConnInterface, a services.Allowlist, opts ...ForwarderOption) (*Forwarder, error)

NewForwarder builds a Forwarder that forwards over cc every method belonging to a service a admits. It fails when cc or a is nil. By default methods are typed against the global proto registry; use WithProtoTypes to override it.

func (*Forwarder) Handle added in v0.5.0

func (f *Forwarder) Handle(_ any, ss grpc.ServerStream) error

Handle forwards one stream to the upstream, and suits google.golang.org/grpc.UnknownServiceHandler. A method whose service the services.Allowlist does not admit is rejected with Unimplemented before any upstream work, so the proxy answers as a server that does not implement it rather than revealing that an upstream might. Only methods present in the compiled descriptors can be forwarded; anything else is Unimplemented too.

type ForwarderOption added in v0.5.0

type ForwarderOption func(*Forwarder)

ForwarderOption configures a Forwarder at construction time.

func WithProtoTypes added in v0.5.0

func WithProtoTypes(t protoutil.Types) ForwarderOption

WithProtoTypes sets the registry used to resolve a method's request and response message types. A nil registry leaves the default in place.

type Option

type Option func(*Options)

Option configures a Server via New.

func WithLogger

func WithLogger(log logger.Logger) Option

WithLogger sets the logger used by the proxy.

func WithSocketPath added in v0.5.0

func WithSocketPath(path string) Option

WithSocketPath sets the unix socket path the proxy binds, overriding the one derived from hostPort. A caller that also dials this socket passes the same value to both sides so the two cannot disagree. New rejects a path that exceeds the platform's sun_path limit.

type Options

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

Options configures a Server at construction time.

type Reporter

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

Reporter records envelope-operation telemetry to Prometheus: each seal (encrypt) and open (decrypt) the encryption interceptor performs, timed end to end, including any KEK wrap or unwrap and any DEK cache lookup along the way. The AES-step duration alone is owned by internal/kms. The namespace label is unbounded, so handles are resolved per call via WithLabelValues rather than pre-computed. A Reporter is safe for concurrent use.

func NewReporter

func NewReporter(f *metrics.Factory) *Reporter

NewReporter builds the Prometheus-backed vault-operation Reporter. f must already be scoped to the "encryption" subsystem by the caller.

func (*Reporter) VaultOp added in v0.3.0

func (r *Reporter) VaultOp(operation, result, namespace string, seconds float64)

VaultOp records a single envelope operation and its duration.

type ResolverOption

type ResolverOption func(*DynamicResolver)

ResolverOption configures a DynamicResolver at construction.

func WithOptionsFactory

func WithOptionsFactory(f func(RouteData) ([]grpc.DialOption, error)) ResolverOption

WithOptionsFactory sets the function that produces the dial options for a resolved request. It receives the rendered host and server name via RouteData.

func WithRemoteNamespacer

func WithRemoteNamespacer(f func(string) string) ResolverOption

WithRemoteNamespacer sets the function that maps the local namespace to the remote one, making RemoteNamespace available to the templates.

func WithResolverLogger added in v0.5.0

func WithResolverLogger(l logger.Logger) ResolverOption

WithResolverLogger sets the logger used to emit a per-request debug entry after a successful resolve. Unset: no entry is emitted.

type RouteData

type RouteData struct {
	template.UpstreamContext
	ResolvedServerName string
}

RouteData is passed to the options factory once a request has been resolved. It carries the template context used for rendering plus the resolved TLS server name, so the factory can build dial options (e.g. credentials whose SNI depends on the rendered server name).

type Server

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

Server proxies the Temporal WorkflowService. It re-serves an upstream frontend on a local unix socket, letting local workers connect without TLS while the upstream hop stays secured. The upstream connection(s) it forwards to are owned by the shared connect.Pool, not by this Server.

func New

func New(hostPort string, fw *Forwarder, opts ...Option) (*Server, error)

New constructs a Server that hands every inbound method to fw, which forwards it to the upstream fw was built against. The local listener is a unix socket whose path is derived from hostPort. The connection(s) fw forwards over are owned by the shared pool, not by this Server.

func (*Server) Listen

func (s *Server) Listen(ctx context.Context) (net.Listener, error)

Listen removes any socket left behind by a prior run and binds the proxy's local unix socket, returning the listener. Binding is separate from Start so callers can bind synchronously during startup (the socket is then listening, and the OS backlogs connections) before serving in the background, ensuring no request is routed to an unbound socket.

func (*Server) Start

func (s *Server) Start(ctx context.Context, lis net.Listener) error

Start serves on lis until Stop is called; ctx is not what stops it, and is used only to drive the periodic health check. It blocks, so callers typically run it in its own goroutine after binding the listener with Listen.

func (*Server) Stop

func (s *Server) Stop(ctx context.Context) error

Stop shuts the proxy down, draining in-flight RPCs within the server's shutdown budget and dropping whatever is left.

type Vault

type Vault interface {
	Seal(context.Context, string, []byte) (*crypto.Message, error)
	Open(context.Context, *crypto.Message) ([]byte, error)
}

Vault seals and opens payloads using envelope encryption scoped by namespace. It is the subset of crypto.Vault the interceptor depends on.

Jump to

Keyboard shortcuts

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