egress

package
v0.9.0 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: 25 Imported by: 0

Documentation

Overview

Package egress is a forward proxy that lets a plugin reach only the hosts it declared, and never an internal address.

Index

Constants

View Source
const (
	EnvConnectTimeout = "APP_PLUGIN_EGRESS_CONNECT_TIMEOUT"
	EnvIdleTimeout    = "APP_PLUGIN_EGRESS_IDLE_TIMEOUT"
	EnvMaxTunnels     = "APP_PLUGIN_EGRESS_MAX_TUNNELS"
	EnvBytesPerMinute = "APP_PLUGIN_EGRESS_BYTES_PER_MINUTE"
	EnvAllowPrivate   = "APP_PLUGIN_EGRESS_ALLOW_PRIVATE"
)

Environment variable names read by ConfigFromEnv.

View Source
const (
	EnvPolicySocket = "APP_PLUGIN_EGRESS_POLICY_SOCKET"
	EnvPolicyCache  = "APP_PLUGIN_EGRESS_POLICY_CACHE"
	EnvListen       = "APP_PLUGIN_EGRESS_LISTEN"
)

Environment variable names for the sidecar policy channel.

View Source
const (
	ReasonUnknownCaller = "unknown_caller"
	ReasonHostNotListed = "host_not_allowed"
	ReasonBlockedAddr   = "blocked_address"
	ReasonResolveFailed = "resolve_failed"
	ReasonDialFailed    = "dial_failed"
	ReasonTunnelCap     = "too_many_tunnels"
	ReasonRateLimit     = "rate_limited"
	ReasonBadRequest    = "bad_request"
)

Denial reasons passed to the deny callback.

Variables

This section is empty.

Functions

func EncodePolicy

func EncodePolicy(p Policy) ([]byte, error)

EncodePolicy validates and serializes a policy.

func HexHash

func HexHash(h SecretHash) string

HexHash is the wire form of a SecretHash.

func ListenPolicySocket

func ListenPolicySocket(path string) (net.Listener, error)

ListenPolicySocket creates the socket directory (0700) and the socket (0600).

func ProxyURL

func ProxyURL(plugin, secret, hostport string) string

ProxyURL builds the proxy URL a plugin gets in its proxy environment.

func PushPolicy

func PushPolicy(ctx context.Context, socketPath string, p Policy) error

PushPolicy sends a policy to a sidecar over its Unix socket.

Types

type Allowlist

type Allowlist interface {
	Allowed(plugin, host string, port int) bool
}

Allowlist says whether a plugin may reach host:port.

type Config

type Config struct {
	ConnectTimeout time.Duration
	IdleTimeout    time.Duration
	// MaxTunnels caps simultaneous connections per plugin.
	MaxTunnels int
	// BytesPerMinute caps traffic per plugin in both directions combined.
	BytesPerMinute int64
	// AllowPrivate lets targets that resolve to internal addresses through.
	// The caller decides it, usually from EnvAllowPrivate.
	AllowPrivate bool
}

Config holds the proxy limits. Zero values mean "use the default".

func ConfigFromEnv

func ConfigFromEnv(lookup func(string) (string, bool)) Config

ConfigFromEnv builds a Config from APP_PLUGIN_EGRESS_* variables. Unparseable values are ignored so a typo keeps the safe default.

type Counters

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

Counters tallies denials per plugin and reason. It holds no hosts or payload, so its size is bounded by plugins times reasons.

func NewCounters

func NewCounters() *Counters

NewCounters returns an empty tally.

func (*Counters) Handler

func (c *Counters) Handler() http.Handler

Handler serves the tally as JSON on the policy socket only.

func (*Counters) Record

func (c *Counters) Record(plugin, reason string)

Record adds one denial. An unknown caller is filed under an empty plugin name.

func (*Counters) Snapshot

func (c *Counters) Snapshot() []DenialCount

Snapshot returns the tally sorted by plugin then reason.

type CredentialStore

type CredentialStore interface {
	Verify(plugin, secret string) bool
}

CredentialStore verifies a plugin's proxy credential.

type DenialCount

type DenialCount struct {
	Plugin string `json:"plugin"`
	Reason string `json:"reason"`
	Count  int64  `json:"count"`
	// LastDenied is the UTC time of the latest refusal (RFC 3339).
	LastDenied string `json:"last_denied,omitempty"`
}

DenialCount is how often one plugin was refused for one reason.

func FetchDenials

func FetchDenials(ctx context.Context, socketPath string, timeout time.Duration) ([]DenialCount, error)

FetchDenials reads the denial tally from a sidecar's policy socket. It is read-only.

type DenyFunc

type DenyFunc func(plugin, host string, port int, reason string)

DenyFunc records a refused request. It never receives payload.

type HashedCredentials

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

HashedCredentials is a CredentialStore over plugin to secret hash.

func NewHashedCredentials

func NewHashedCredentials(hashes map[string]SecretHash) *HashedCredentials

NewHashedCredentials copies the map so later edits cannot change it.

func (*HashedCredentials) Verify

func (h *HashedCredentials) Verify(plugin, secret string) bool

Verify compares in constant time and does the same work for an unknown plugin, so timing does not reveal which names exist.

type Options

type Options struct {
	Credentials CredentialStore
	Allowlist   Allowlist
	// Resolver defaults to net.DefaultResolver.
	Resolver Resolver
	OnDeny   DenyFunc
	Config   Config
}

Options wires the proxy. Credentials and Allowlist are required.

type PluginPolicy

type PluginPolicy struct {
	Name       string   `json:"name"`
	SecretHash string   `json:"secret_hash"`
	Hosts      []string `json:"hosts"`
}

PluginPolicy is one plugin's credential hash (hex SHA-256) and allowed hosts. Hosts are "host" (port 443) or "host:port", as StaticAllowlist expects.

type Policy

type Policy struct {
	Version int64          `json:"version"`
	Plugins []PluginPolicy `json:"plugins"`
}

Policy is the full egress policy the control plane pushes to the sidecar.

func DecodePolicy

func DecodePolicy(r io.Reader) (Policy, error)

DecodePolicy parses strictly (unknown fields and trailing data are errors) and validates.

func (Policy) Validate

func (p Policy) Validate() error

Validate rejects any policy the sidecar must not apply.

type PolicyServer

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

PolicyServer applies pushed policies to a store and caches the last good one.

func NewPolicyServer

func NewPolicyServer(store *PolicyStore, cachePath string) *PolicyServer

NewPolicyServer returns a server. An empty cachePath disables the cache.

func (*PolicyServer) Handler

func (s *PolicyServer) Handler() http.Handler

Handler serves PUT /policy and GET /healthz.

func (*PolicyServer) LoadCache

func (s *PolicyServer) LoadCache() error

LoadCache installs the cached policy when the store has none yet. A missing cache is not an error; a corrupt one is, and leaves the store denying all.

func (*PolicyServer) WithCounters

func (s *PolicyServer) WithCounters(c *Counters) *PolicyServer

WithCounters serves the denial tally at GET /denials.

type PolicyStore

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

PolicyStore holds the active policy and swaps it atomically. With no policy it denies everything. It implements CredentialStore and Allowlist.

func NewPolicyStore

func NewPolicyStore() *PolicyStore

NewPolicyStore returns a store with no policy (deny all).

func (*PolicyStore) Allowed

func (s *PolicyStore) Allowed(plugin, host string, port int) bool

Allowed implements Allowlist.

func (*PolicyStore) Ready

func (s *PolicyStore) Ready() bool

Ready reports whether a policy has been installed.

func (*PolicyStore) Set

func (s *PolicyStore) Set(p Policy) error

Set validates and installs a policy in one atomic swap.

func (*PolicyStore) Verify

func (s *PolicyStore) Verify(plugin, secret string) bool

Verify implements CredentialStore.

func (*PolicyStore) Version

func (s *PolicyStore) Version() int64

Version returns the installed policy version, or -1 with none.

type Proxy

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

Proxy is an http.Handler serving CONNECT and absolute-form requests.

func New

func New(o Options) *Proxy

New returns a Proxy. It panics on missing required options, which is a programming error rather than a runtime condition.

func (*Proxy) ServeHTTP

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

ServeHTTP implements http.Handler.

type Resolver

type Resolver interface {
	LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error)
}

Resolver looks up the addresses of a host.

type SecretHash

type SecretHash [sha256.Size]byte

SecretHash is the SHA-256 of a plugin credential. Only hashes are stored.

func HashSecret

func HashSecret(secret string) SecretHash

HashSecret returns the stored form of a secret.

func NewCredential

func NewCredential() (secret string, hash SecretHash, err error)

NewCredential returns a random secret and the hash to store for it.

type StaticAllowlist

type StaticAllowlist map[string][]string

StaticAllowlist is a simple Allowlist. Entries are "host" (port 443 only) or "host:port" (that port only); hosts match exactly, ignoring case.

func (StaticAllowlist) Allowed

func (s StaticAllowlist) Allowed(plugin, host string, port int) bool

Allowed implements Allowlist.

Jump to

Keyboard shortcuts

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