proxy

package
v0.2.37 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

Documentation

Overview

Package proxy reverse-proxies authenticated requests to kcp.

Index

Constants

View Source
const DefaultMaxLimiterEntries = 10000

DefaultMaxLimiterEntries bounds how many distinct client addresses a per-address rate limiter tracks at once. Past it the least recently seen address is dropped; an address that comes back simply starts a fresh bucket. The bound exists so a flood of distinct peers cannot grow the hub's memory without limit — it is not a security boundary in itself, since every key is a real connection peer once ClientIP has done its job.

Variables

View Source
var ErrIdentifyNoBearer = errors.New("no Authorization: Bearer token")

ErrIdentifyNoBearer is returned by IdentifyUser when the request carries no Authorization: Bearer header. Callers (e.g. the tenant middleware) translate this into a 401.

View Source
var ErrUserRecordUnavailable = errors.New("caller authenticated but user record unavailable")

ErrUserRecordUnavailable wraps failures that happen AFTER the caller's credential has been accepted — the static token matched, or the OIDC token verified — but the backing User record could not be read or created.

It exists so callers can tell "this caller is not authenticated" from "this caller is authenticated and the hub is having trouble". Both surface as an error from IdentifyUser, and conflating them forces a choice between serving unauthenticated callers and 500-ing authenticated ones during a hiccup. Endpoints that only need to know the caller is legitimate (not who they are) can proceed on this; anything that acts as the user must still fail.

Functions

func ClientIP

func ClientIP(r *http.Request, trusted []netip.Prefix) string

ClientIP derives the address the hub keys its pre-authentication rate limits on. The rule is the one every reverse proxy follows when it forwards: the proxy APPENDS the peer it saw to X-Forwarded-For, so the only hop the hub can vouch for is the right-most one that a trusted proxy wrote. Every hop to the left of that is whatever the client chose to send.

  • The connection peer (RemoteAddr) is always the starting point.
  • When trusted is empty, X-Forwarded-For and X-Real-IP are ignored entirely and the peer address is the answer. A hub deployed behind a proxy without --trusted-proxy-cidrs therefore sees every client as the proxy's own address and throttles them as one; that is the safe way to fail (too much throttling, never too little).
  • When the peer is inside a trusted prefix, X-Forwarded-For is walked from the right, skipping hops that are themselves trusted proxies, and the first untrusted hop is the client. X-Real-IP is consulted only when that walk yields nothing.
  • A hop that is not a valid IP ends the walk and the peer address is used, so a malformed header can never mint a fresh bucket.

The result is a canonical IP string (IPv4-mapped IPv6 unmapped, zone dropped); when RemoteAddr itself is not an IP it is returned as-is, which only happens for non-TCP listeners.

func ParseTrustedProxyCIDRs

func ParseTrustedProxyCIDRs(values []string) ([]netip.Prefix, error)

ParseTrustedProxyCIDRs parses the --trusted-proxy-cidrs values. Each entry may be a CIDR ("10.0.0.0/8", "fd00::/8") or a bare address, which is taken as a single-host prefix. Entries may themselves be comma-separated; blanks are ignored.

Types

type BoundedKeys

type BoundedKeys[V any] struct {
	// contains filtered or unexported fields
}

BoundedKeys is a fixed-capacity, least-recently-used map from a client key to a limiter state, with idle eviction. It is not safe for concurrent use; the limiter that owns it holds the lock.

Two things keep it bounded: Put evicts the least recently used entry when the map is at capacity, and any Put that lands more than idle after the previous sweep first drops every entry that has not been touched within idle. The sweep walks from the least recent end and stops at the first live entry, so it costs no more than the number of entries it removes.

func NewBoundedKeys

func NewBoundedKeys[V any](maxEntries int, idle time.Duration) *BoundedKeys[V]

NewBoundedKeys returns an empty store holding at most max entries and forgetting entries not touched for idle. Non-positive max falls back to DefaultMaxLimiterEntries; non-positive idle disables idle eviction.

func (*BoundedKeys[V]) Get

func (b *BoundedKeys[V]) Get(key string, now time.Time) (V, bool)

Get returns the value for key, marking it as used at now.

func (*BoundedKeys[V]) Len

func (b *BoundedKeys[V]) Len() int

Len is the number of tracked keys.

func (*BoundedKeys[V]) Put

func (b *BoundedKeys[V]) Put(key string, val V, now time.Time)

Put stores val under key as the most recently used entry, evicting idle entries and then the least recently used one as needed to stay in bounds.

type IPRateLimiter

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

IPRateLimiter is the per-client-address token bucket the hub uses on its pre-authentication endpoints (token-login, the aggregate MCP bearer check). It is the same limiter HandleTokenLoginRateLimited applies, exposed so other packages can share one implementation without importing its internals.

func NewIPRateLimiter

func NewIPRateLimiter(interval time.Duration, burstSize int) *IPRateLimiter

NewIPRateLimiter returns a limiter that admits burstSize requests per client address immediately and refills one token per interval up to burstSize.

func (*IPRateLimiter) Allow

func (l *IPRateLimiter) Allow(clientIP string) bool

Allow reports whether a request from clientIP may proceed, consuming one token when it may.

type KCPProxy

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

KCPProxy is a reverse proxy that authenticates requests via OIDC and forwards them to the user's dedicated kcp tenant workspace.

func NewKCPProxy

func NewKCPProxy(kcpConfig *rest.Config, verifier *oidc.IDTokenVerifier, railgridClient *railgridclient.Client, bootstrapper *kcp.Bootstrapper, staticAuthTokens []string, hubExternalURL string, devMode bool) (*KCPProxy, error)

NewKCPProxy creates a reverse proxy to kcp. It validates bearer tokens as OIDC id_tokens before proxying. verifier may be nil when only static token auth is used.

func (*KCPProxy) AuthorizeCluster added in v0.2.7

func (p *KCPProxy) AuthorizeCluster(ctx context.Context, userName, clusterID string) bool

AuthorizeCluster reports whether userName may reach clusterID — the same membership question ServeHTTP settles for /clusters/{id}, exported so the one answer serves every front door.

The provider backend proxy is the second such door: a data-plane route names its workspace in its path (/{root}/clusters/{id}/…) and the hub has to authorize that cluster before it hands the ID to a provider as X-Railgrid-Cluster. It asks here rather than growing a second membership check, so the set of workspaces a caller can reach with kubectl and the set they can reach through a provider stay the same set.

It adds no policy of its own: the decision, the caching and the fail-closed behaviour are all clusterAuthorizer.authorize below, unchanged.

func (*KCPProxy) BrowserIdentity

func (p *KCPProxy) BrowserIdentity(r *http.Request) (browsersession.Identity, error)

BrowserIdentity validates a portal bearer and resolves it to the stable platform user identity used by the shared browser-session store. The returned value contains no bearer, kubeconfig, or OIDC token.

func (*KCPProxy) HandleTokenLogin

func (p *KCPProxy) HandleTokenLogin(w http.ResponseWriter, r *http.Request)

HandleTokenLogin handles static token login requests. POST /auth/token-login with Authorization: Bearer <token> Returns a LoginResponse with kubeconfig pointing to the user's workspace. Note: This handler should be wrapped with rate limiting using HandleTokenLoginRateLimited when registering routes to prevent brute force attacks.

func (*KCPProxy) HandleTokenLoginRateLimited

func (p *KCPProxy) HandleTokenLoginRateLimited(w http.ResponseWriter, r *http.Request)

HandleTokenLoginRateLimited wraps HandleTokenLogin with rate limiting. This should be used when registering the route to protect against brute force attacks. In devMode the limiter is bypassed: dev clusters share a single client IP (localhost) across many test/CLI invocations, which trivially exhausts the per-IP bucket and is not a realistic threat model for dev.

func (*KCPProxy) IdentifyUser

func (p *KCPProxy) IdentifyUser(r *http.Request) (string, error)

IdentifyUser extracts the caller's User CR name from r's bearer token, using the same auth schemes as ServeHTTP (static token, OIDC). Used by hub REST endpoints behind the tenant middleware so they can identify the caller without duplicating the proxy's auth dispatch.

Returns ErrIdentifyNoBearer for missing/unparseable Authorization headers, and other errors for verification failures. kcp ServiceAccount tokens are intentionally not accepted here — REST endpoints are addressed by humans (or by their portal session) and not by edge-side bots.

func (*KCPProxy) ScrubStaticTokenUsers

func (p *KCPProxy) ScrubStaticTokenUsers(ctx context.Context) error

ScrubStaticTokenUsers removes the token text from the Users (and their personal Organizations) of every configured static token; see scrubStaticTokenUser. Login does the same, but a token nobody uses after an upgrade would otherwise stay readable in member lists indefinitely. It only updates Users that exist; it never creates one.

func (*KCPProxy) ServeHTTP

func (p *KCPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP validates the bearer token and proxies the request to kcp. Two token types are supported:

  • OIDC id_tokens (from Dex): resolved to a tenant workspace via User CRD lookup, forwarded with admin credentials.
  • kcp ServiceAccount tokens: the clusterName claim identifies the workspace, forwarded with the original SA token so kcp handles authn/authz natively.

func (*KCPProxy) SetBrowserSessionStore

func (p *KCPProxy) SetBrowserSessionStore(store *browsersession.Store)

SetBrowserSessionStore wires the hub-wide opaque browser-session store used by static-token portal login. The proxy never places the static bearer in that store or in the cookie; it records only the resolved User identity.

func (*KCPProxy) SetTrustedProxies

func (p *KCPProxy) SetTrustedProxies(prefixes []netip.Prefix)

SetTrustedProxies tells the token-login rate limiter which connection peers are reverse proxies whose X-Forwarded-For may be believed (see ClientIP). Without it every request is keyed on the connection peer, so a hub behind a proxy throttles all of its clients as one address.

Jump to

Keyboard shortcuts

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