resourceproxy

package
v1.60.0 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package resourceproxy is the machinery of cmd/resource-proxy: a sidecar that puts a sluis resource server's front door on a stock MCP server (or any HTTP service) that knows nothing about sluis.

Two sides, independent:

  • Inbound faces the gateway. It verifies the caller's access token (identity/resource), serves the resource's RFC 9728 metadata, writes one audit line per request, and reverse-proxies to the stock server with the caller's Authorization header removed.
  • Outbound is optional and faces the stock server, on loopback only. The stock server points its backend URL at it; it adds a bearer token this workload's OWN identity earned by an RFC 8693 exchange. The caller's token is never forwarded anywhere (the MCP spec forbids passing it through), so the stock server holds no credential at all.

Index

Constants

View Source
const (
	DefaultMaxRequestBytes = 4 << 20
	DefaultBodyTimeout     = 30 * time.Second
	DefaultUpstreamTimeout = 2 * time.Minute
	DefaultRefreshBefore   = time.Minute
)

Default limits.

Variables

This section is empty.

Functions

func IsLoopbackListen

func IsLoopbackListen(addr string) bool

IsLoopbackListen reports whether a listen address binds loopback only. A bare port (":8429") binds every interface and is not loopback.

func Run

func Run(ctx context.Context, cfg Config, log *slog.Logger) error

Run starts the listeners and blocks until ctx is done, then drains them. A listener that fails to start, or dies, ends the process: a sidecar with half its sides gone should be restarted, not trusted.

Types

type Config

type Config struct {
	// Listen is the inbound listener.
	Listen string
	// Upstream is the stock server. Its path, if any, is where the
	// resource's own path maps to: upstream http://127.0.0.1:8081/mcp for
	// resource https://h/metrics sends /metrics to /mcp and /metrics/x to
	// /mcp/x.
	Upstream string
	// IssuerURL, ResourceURL and Scope configure identity/resource.
	IssuerURL   string
	ResourceURL string
	Scope       string

	// MaxRequestBytes caps a request body; more is refused with 413.
	MaxRequestBytes int64
	// BodyReadTimeout bounds reading one request body.
	BodyReadTimeout time.Duration
	// UpstreamTimeout bounds the wait for the stock server's response
	// headers. A streamed response is not bounded by it.
	UpstreamTimeout time.Duration

	// Outbound side. Empty OutboundListen turns it off.
	OutboundListen        string
	OutboundTarget        string
	OutboundTokenEndpoint string
	OutboundClientID      string
	OutboundAudience      string
	OutboundSATokenFile   string
	// OutboundCAFile is a PEM bundle appended to the system roots for the
	// outbound forwarder's connection to OutboundTarget, and only that
	// one: the issuer and the token exchange keep the system roots alone.
	OutboundCAFile string
	// OutboundAllowNonLoopback lets the outbound listener bind anything
	// but loopback. Anyone who can reach it borrows this workload's
	// identity, so it is refused unless asked for by name.
	OutboundAllowNonLoopback bool
	// RefreshBefore is how long before expiry a token is exchanged again.
	RefreshBefore time.Duration
}

Config is everything the proxy is told. cmd/resource-proxy fills it from flags, each defaulting to an environment variable.

func (Config) OutboundEnabled

func (c Config) OutboundEnabled() bool

OutboundEnabled reports whether the outbound side runs.

func (*Config) Validate

func (c *Config) Validate() error

Validate refuses a configuration that cannot work, or that works unsafely, before anything listens.

type Inbound

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

Inbound is the public side.

func NewInbound

func NewInbound(cfg Config, log *slog.Logger, extraReady func() error) (*Inbound, error)

NewInbound builds the inbound side from a validated Config.

func (*Inbound) Handler

func (in *Inbound) Handler() http.Handler

Handler is the whole public listener.

func (*Inbound) Resource

func (in *Inbound) Resource() *resource.Resource

Resource is the verifier and metadata the inbound side is built on.

type Outbound

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

Outbound is the loopback forwarder: whatever reaches it is sent to the target with this workload's own bearer token.

func NewOutbound

func NewOutbound(cfg Config, log *slog.Logger, source *TokenSource) (*Outbound, error)

NewOutbound builds the forwarder from a validated Config.

func (*Outbound) Handler

func (o *Outbound) Handler() http.Handler

Handler is the loopback listener.

type TokenSource

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

TokenSource is this workload's own identity, as a bearer token: its projected ServiceAccount token traded at the issuer (RFC 8693) for one audienced at the backend. The kubelet rotates the ServiceAccount token under the pod, so it is read afresh for every exchange, never cached.

func NewTokenSource

func NewTokenSource(cfg Config, log *slog.Logger, client *http.Client) (*TokenSource, error)

NewTokenSource builds the source from a validated Config. client is the transport to the issuer; nil uses tokens' default.

func (*TokenSource) Obtained

func (s *TokenSource) Obtained() bool

Obtained reports whether a token has ever been obtained: what readiness means for the outbound side.

func (*TokenSource) Token

func (s *TokenSource) Token(ctx context.Context) (string, error)

Token returns a bearer token with at least the refresh margin left, exchanging when it has none. One exchange at a time: a burst of calls that all find the token stale waits for one answer rather than making one each.

A failed exchange while the previous token is still valid serves the previous token and tries again next call: a blip at the issuer is not an outage of the backend.

func (*TokenSource) Warm

func (s *TokenSource) Warm(ctx context.Context)

Warm tries until the first token is obtained, so readiness does not wait for the first request, and stops on ctx.

Jump to

Keyboard shortcuts

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