proxy

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: May 27, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Forward

func Forward(ctx context.Context, client, upstream net.Conn, peeked []byte, logger zerolog.Logger)

Forward pipes data bidirectionally between client and upstream, replaying peeked ClientHello bytes to upstream first.

func LimitWriter

func LimitWriter(w io.Writer, n int64) io.Writer

LimitWriter returns a writer that writes at most n bytes to w.

func LoadOrCreateCA

func LoadOrCreateCA(dir string) (*x509.Certificate, *ecdsa.PrivateKey, *tls.Certificate, error)

LoadOrCreateCA loads an existing CA certificate and key from dir, or generates a new self-signed CA if none exists. Returns the parsed certificate, private key, and a tls.Certificate ready for signing leaf certs.

func LogModeString

func LogModeString(m LogMode) string

LogModeString returns the string representation of a LogMode. An unknown value is reported as "off" rather than a more permissive mode so a corrupted value can never silently escalate logging.

func PeekClientHello

func PeekClientHello(conn net.Conn) (peeked []byte, serverName string, err error)

PeekClientHello reads the TLS ClientHello from conn without consuming it. Returns the peeked bytes (to replay to upstream) and the extracted SNI hostname.

func RemoveStats

func RemoveStats(path string)

RemoveStats removes the proxy stats file (best-effort).

func SetLogMode

func SetLogMode(mode LogMode)

SetLogMode sets the global traffic log mode.

func StatsPath

func StatsPath() string

StatsPath returns the default path for the proxy stats file (~/.human/proxy-stats.json).

func WriteStats

func WriteStats(path string, s Stats) error

WriteStats atomically writes stats to path (write tmp + rename).

Types

type Config

type Config struct {
	Mode      Mode     `mapstructure:"mode"`
	Domains   []string `mapstructure:"domains"`
	Intercept []string `mapstructure:"intercept"` // domains to MITM for traffic logging
}

Config holds the proxy section of .humanconfig.yaml.

func LoadConfig

func LoadConfig(dir string) (*Config, error)

LoadConfig reads the proxy configuration from .humanconfig.yaml in dir. Returns (nil, nil) when the proxy section is absent.

type Decider

type Decider interface {
	Allowed(hostname string) bool
}

Decider decides whether a given hostname is allowed to pass through the proxy.

type InteractiveDecider

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

InteractiveDecider wraps a base Decider and prompts the user for hostnames that the base does not allow. Decisions are cached for the session.

func NewInteractiveDecider

func NewInteractiveDecider(base Decider, prompt PromptFunc) *InteractiveDecider

NewInteractiveDecider creates an InteractiveDecider that falls through to prompt for hostnames not allowed by base.

func (*InteractiveDecider) Allowed

func (d *InteractiveDecider) Allowed(hostname string) bool

Allowed returns true if the hostname is permitted. Hostnames allowed by the base decider pass through immediately. Unknown hostnames trigger a prompt; the result is cached for subsequent calls.

type Interceptor

type Interceptor interface {
	// ShouldIntercept returns true if this domain should be MITM'd.
	ShouldIntercept(hostname string) bool
	// Intercept handles a MITM'd connection. The peeked bytes contain the
	// already-read ClientHello that must be replayed into the TLS handshake.
	Intercept(ctx context.Context, conn net.Conn, hostname string, peeked []byte) error
}

Interceptor can intercept and inspect decrypted traffic for specific domains.

type LeafCache

type LeafCache struct {
	CACert *x509.Certificate
	CAKey  *ecdsa.PrivateKey
	// contains filtered or unexported fields
}

LeafCache generates and caches per-domain TLS certificates signed by a CA. Cache size is bounded; concurrent requests for the same hostname collapse onto a single generation call.

func (*LeafCache) Get

func (lc *LeafCache) Get(hostname string) (*tls.Certificate, error)

Get returns a cached leaf certificate for hostname, or generates a new one.

type LogMode

type LogMode int32

LogMode controls the verbosity of traffic logging.

const (
	LogModeOff  LogMode = 0 // no logging (default, zero value)
	LogModeMeta LogMode = 1 // log method, path, status, body_size only
	LogModeFull LogMode = 2 // log everything including body
)

func GetLogMode

func GetLogMode() LogMode

GetLogMode returns the current global traffic log mode.

func ParseLogMode

func ParseLogMode(s string) (LogMode, error)

ParseLogMode parses a string into a LogMode. Unknown input returns LogModeOff along with the parse error, so a typo in config cannot accidentally widen what gets captured.

type LoggingInterceptor

type LoggingInterceptor struct {
	Domains   []string // exact domain matches to intercept
	LeafCache *LeafCache
	Logger    zerolog.Logger
	LogDir    string // directory for traffic log files

	// Dialer connects to upstream servers. Injected for testing.
	// If nil, tls.Dial is used.
	Dialer func(ctx context.Context, network, address string) (net.Conn, error)
	// contains filtered or unexported fields
}

LoggingInterceptor performs MITM interception for configured domains, logging HTTP request/response bodies to JSON-lines files.

func (*LoggingInterceptor) Intercept

func (li *LoggingInterceptor) Intercept(ctx context.Context, conn net.Conn, hostname string, peeked []byte) error

Intercept performs a MITM TLS handshake with the client, dials the real upstream, and proxies HTTP traffic while logging request/response bodies.

func (*LoggingInterceptor) ShouldIntercept

func (li *LoggingInterceptor) ShouldIntercept(hostname string) bool

ShouldIntercept returns true if hostname matches a configured intercept domain.

type Mode

type Mode string

Mode determines whether the domain list is an allowlist or blocklist.

const (
	ModeAllow Mode = "allowlist"
	ModeBlock Mode = "blocklist"
)

type NetworkEventEmitter

type NetworkEventEmitter interface {
	// Emit records a single network decision or failure.
	// Source is one of "proxy", "oauth", "fail".
	// Status is a short human readable decision: "forward", "intercept",
	// "block", "no-sni", "parse-fail", "dial-fail", "callback".
	// Host may be empty for failures that happen before SNI extraction.
	Emit(source, status, host string)
}

NetworkEventEmitter is the minimal interface the SNI proxy uses to surface ambient network activity (forwards, intercepts, blocks, failures) to any external observer. The daemon's NetworkEventStore satisfies this interface. The proxy package does not depend on the daemon package so the dependency direction stays one-way.

type Policy

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

Policy decides whether a given hostname is allowed to pass through the proxy.

func BlockAllPolicy

func BlockAllPolicy() *Policy

BlockAllPolicy returns a policy that blocks every hostname.

func NewPolicy

func NewPolicy(mode Mode, domains []string) (*Policy, error)

NewPolicy creates a policy from a mode and domain list.

func (*Policy) Allowed

func (p *Policy) Allowed(hostname string) bool

Allowed reports whether hostname is permitted by this policy.

type PromptFunc

type PromptFunc func(hostname string) (bool, error)

PromptFunc asks the user whether a hostname should be allowed. It returns true to allow, false to deny.

func NewTerminalPrompt

func NewTerminalPrompt(in io.Reader, out io.Writer) PromptFunc

NewTerminalPrompt returns a PromptFunc that asks the user via the terminal. It serialises I/O with its own mutex so that concurrent prompts don't interleave on the terminal.

type Server

type Server struct {
	Addr        string
	Policy      Decider
	Interceptor Interceptor // optional: MITM interceptor for specific domains
	Logger      zerolog.Logger
	// Emitter records ambient network decisions for the TUI activity
	// panel. Optional — nil means no events are recorded so tests and
	// standalone use pay nothing for the feature.
	Emitter NetworkEventEmitter
	// Dialer connects to upstream servers. Injected for testing.
	Dialer func(ctx context.Context, network, address string) (net.Conn, error)
	// contains filtered or unexported fields
}

Server is a transparent HTTPS proxy that reads the SNI from TLS ClientHello to block/allow domains without decrypting traffic. Domains listed in the Interceptor are MITM'd for traffic inspection/logging.

func (*Server) ActiveConns

func (s *Server) ActiveConns() int64

ActiveConns returns the number of currently active forwarded connections.

func (*Server) ListenAndServe

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

ListenAndServe starts the TCP listener and blocks until ctx is cancelled.

type Stats

type Stats struct {
	ActiveConns int64 `json:"active_conns"`
}

Stats holds proxy runtime metrics written by the daemon and read by the TUI.

func ReadStats

func ReadStats(path string) Stats

ReadStats reads proxy stats from path. Returns zero stats on any error.

type TrafficLog

type TrafficLog struct {
	Timestamp time.Time `json:"ts"`
	Direction string    `json:"dir"` // "request" or "response"
	Host      string    `json:"host"`
	Method    string    `json:"method,omitempty"` // request only
	Path      string    `json:"path,omitempty"`   // request only
	Status    int       `json:"status,omitempty"` // response only
	Body      string    `json:"body"`
	BodySize  int64     `json:"body_size"`
}

TrafficLog is a single JSON-lines entry written to the traffic log.

Jump to

Keyboard shortcuts

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