Documentation
¶
Overview ¶
Package server provides cache management API handlers.
Package server provides the REST API for Bifrost server.
Index ¶
- Constants
- Variables
- func StaticHandler() http.Handler
- type API
- type BackendHealthEvent
- type CacheAPI
- type ClientSummary
- type Config
- type ConfigMeta
- type ConfigSaveRequest
- type ConfigSaveResponse
- type Connection
- type ConnectionEvent
- type ConnectionTracker
- func (t *ConnectionTracker) Add(clientIP, clientPort, host, backend, protocol string) string
- func (t *ConnectionTracker) Count() int
- func (t *ConnectionTracker) Get(id string) (Connection, bool)
- func (t *ConnectionTracker) GetAll() []Connection
- func (t *ConnectionTracker) GetByClient(clientIP string) []Connection
- func (t *ConnectionTracker) GetUniqueClients() []ClientSummary
- func (t *ConnectionTracker) Remove(id string)
- func (t *ConnectionTracker) SetDestination(id, host, backend string) (Connection, bool)
- func (t *ConnectionTracker) UpdateBytes(id string, sent, recv int64)
- type HostCount
- type MeshAPI
- type MeshNetwork
- type PACGenerator
- type RequestLog
- func (r *RequestLog) Add(entry RequestLogEntry)
- func (r *RequestLog) Clear()
- func (r *RequestLog) GetAll() []RequestLogEntry
- func (r *RequestLog) GetRecent(n int) []RequestLogEntry
- func (r *RequestLog) GetSince(sinceID int64) []RequestLogEntry
- func (r *RequestLog) IsEnabled() bool
- func (r *RequestLog) SetEnabled(enabled bool)
- func (r *RequestLog) Stats() RequestLogStats
- type RequestLogEntry
- type RequestLogStats
- type StatsEvent
- type ValidationError
- type WebSocketHub
- func (h *WebSocketHub) Broadcast(eventType string, data interface{})
- func (h *WebSocketHub) Run()
- func (h *WebSocketHub) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (h *WebSocketHub) SetAllowedOrigins(origins []string)
- func (h *WebSocketHub) SkipsOriginCheck() bool
- func (h *WebSocketHub) Stop()
Constants ¶
const ( SectionServer = "server" SectionBackends = "backends" SectionRoutes = "routes" SectionAuth = "auth" SectionRateLimit = "rate_limit" SectionAccessControl = "access_control" SectionAccessLog = "access_log" SectionMetrics = "metrics" SectionLogging = "logging" SectionWebUI = "web_ui" SectionAPI = "api" SectionHealthCheck = "health_check" SectionAutoUpdate = "auto_update" SectionCache = "cache" SectionNetwork = "network" SectionSession = "session" SectionMITM = "mitm" SectionMesh = "mesh" )
Config section names. These are the canonical identifiers shared by the save/validate responses, the /config/meta endpoint and the Web UI, and they match the `yaml`/`json` tags of the corresponding config.ServerConfig fields. Every top-level field of config.ServerConfig must have a constant here — TestConfigSectionsCoverServerConfig enforces that.
const ( EventBackendHealth = "backend.health" EventConnectionNew = "connection.new" EventConnectionClose = "connection.close" EventConfigReload = "config.reload" EventStats = "stats.update" )
Event types for WebSocket broadcasts
const EventConfigSaved = "config.saved"
EventConfigSaved is broadcast when config is saved.
const MaxWebSocketClients = 100
MaxWebSocketClients is the maximum number of concurrent WebSocket connections.
const WebSocketPingInterval = 20 * time.Second
WebSocketPingInterval is how often the server sends a protocol-level ping to prove the peer is alive. Must be well under WebSocketReadTimeout so a healthy but idle connection is refreshed before the read deadline expires.
const WebSocketReadTimeout = 60 * time.Second
WebSocketReadTimeout bounds how long a connection may go without ANY traffic (application message, or a pong in reply to our keepalive ping) before it is considered dead. Keep it comfortably above WebSocketPingInterval.
const WebSocketWriteTimeout = 5 * time.Second
WebSocketWriteTimeout bounds a single broadcast write to one client, so one slow reader cannot stall the hub.
Variables ¶
var ( ErrNetworkExists = errors.New("network already exists") ErrNetworkNotFound = errors.New("network not found") )
Common errors
Functions ¶
func StaticHandler ¶
StaticHandler returns a handler for serving the embedded static files.
Types ¶
type API ¶
type API struct {
// contains filtered or unexported fields
}
API provides the REST API for Bifrost server.
func (*API) ConnectionTracker ¶
func (a *API) ConnectionTracker() *ConnectionTracker
ConnectionTracker returns the connection tracker for tracking active connections.
func (*API) RequestLog ¶
func (a *API) RequestLog() *RequestLog
RequestLog returns the request log for adding entries.
func (*API) Router ¶
Router returns the HTTP router for the API without the WebSocket endpoint, the PAC endpoints or the static Web UI. The server binary uses RouterWithWebSocket; this variant exists for embedders that mount only the REST surface.
func (*API) RouterWithWebSocket ¶
func (a *API) RouterWithWebSocket(hub *WebSocketHub) http.Handler
RouterWithWebSocket returns a router with WebSocket and static file support.
type BackendHealthEvent ¶
BackendHealthEvent represents a backend health change event.
type CacheAPI ¶
type CacheAPI struct {
// contains filtered or unexported fields
}
CacheAPI provides cache management endpoints.
func NewCacheAPI ¶
NewCacheAPI creates a new cache API handler.
func (*CacheAPI) RegisterRoutes ¶
RegisterRoutes registers cache API routes on the given router.
type ClientSummary ¶
type ClientSummary struct {
ClientIP string `json:"client_ip"`
Connections int `json:"connections"`
BytesSent int64 `json:"bytes_sent"`
BytesRecv int64 `json:"bytes_recv"`
FirstSeen time.Time `json:"first_seen"`
}
ClientSummary summarizes connections from a single client.
type Config ¶
type Config struct {
Backends *backend.Manager
HealthManager *health.Manager
CacheManager *cache.Manager
Token string
GetConfig func() interface{} // Returns sanitized config
GetFullConfig func() *config.ServerConfig // Returns full config for editing
ReloadConfig func() error // Triggers config reload
SaveConfig func(*config.ServerConfig) error // Saves config to file
ConfigPath string // Path to config file
ProxyHost string // Proxy host for PAC file
ProxyPort string // HTTP proxy port for PAC file
SOCKS5Port string // SOCKS5 port for PAC file
EnableRequestLog bool // Enable request logging
RequestLogSize int // Max requests to keep
// SessionManager, when non-nil AND Token is set, enables the token-exchange
// login/cookie flow (see API.sessionManager). The caller (server) owns the
// manager's lifecycle and must Close it on shutdown. Ignored when Token is
// empty because the API is unauthenticated in that case and sessions would
// gate nothing.
SessionManager *session.Manager
// Mesh configures the mesh coordinator API. When Mesh.Enabled is false the
// /api/v1/mesh routes are not mounted at all. Mesh.StatePath, when set,
// persists coordinator networks and peers across restarts.
Mesh config.MeshConfig
}
Config holds API configuration.
type ConfigMeta ¶
type ConfigMeta struct {
Section string `json:"section"`
HotReloadable bool `json:"hot_reloadable"`
Description string `json:"description"`
}
ConfigMeta describes which config sections are hot-reloadable.
type ConfigSaveRequest ¶
type ConfigSaveRequest struct {
Config config.ServerConfig `json:"config"`
CreateBackup bool `json:"create_backup"`
}
ConfigSaveRequest represents a config save request.
type ConfigSaveResponse ¶
type ConfigSaveResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
BackupPath string `json:"backup_path,omitempty"`
// RequiresRestart is true when at least one changed section cannot be
// applied by ReloadConfig, i.e. RestartRequiredSections is non-empty.
RequiresRestart bool `json:"requires_restart"`
ChangedSections []string `json:"changed_sections"`
// HotReloadedSections lists the changed sections that were applied to the
// running server without a restart. Populated so the Web UI never has to
// re-derive hot-reloadability client-side (where it can drift).
HotReloadedSections []string `json:"hot_reloaded_sections"`
// RestartRequiredSections lists the changed sections that were written to
// disk but only take effect after a server restart.
RestartRequiredSections []string `json:"restart_required_sections"`
Errors []ValidationError `json:"errors,omitempty"`
}
ConfigSaveResponse represents the response after saving config.
type Connection ¶
type Connection struct {
ID string `json:"id"`
ClientIP string `json:"client_ip"`
ClientPort string `json:"client_port"`
Host string `json:"host"`
Backend string `json:"backend"`
Protocol string `json:"protocol"` // HTTP, SOCKS5, CONNECT
StartTime time.Time `json:"start_time"`
BytesSent int64 `json:"bytes_sent"`
BytesRecv int64 `json:"bytes_recv"`
}
Connection represents an active proxy connection.
type ConnectionEvent ¶
type ConnectionEvent struct {
Protocol string `json:"protocol"`
Host string `json:"host"`
Backend string `json:"backend"`
ClientIP string `json:"client_ip"`
}
ConnectionEvent represents a connection event.
type ConnectionTracker ¶
type ConnectionTracker struct {
// contains filtered or unexported fields
}
ConnectionTracker tracks active proxy connections.
func NewConnectionTracker ¶
func NewConnectionTracker() *ConnectionTracker
NewConnectionTracker creates a new connection tracker.
func (*ConnectionTracker) Add ¶
func (t *ConnectionTracker) Add(clientIP, clientPort, host, backend, protocol string) string
Add adds a new connection and returns its ID.
func (*ConnectionTracker) Count ¶
func (t *ConnectionTracker) Count() int
Count returns the number of active connections.
func (*ConnectionTracker) Get ¶
func (t *ConnectionTracker) Get(id string) (Connection, bool)
Get returns a snapshot of the tracked connection and true when it exists.
func (*ConnectionTracker) GetAll ¶
func (t *ConnectionTracker) GetAll() []Connection
GetAll returns all active connections.
func (*ConnectionTracker) GetByClient ¶
func (t *ConnectionTracker) GetByClient(clientIP string) []Connection
GetByClient returns connections for a specific client IP.
func (*ConnectionTracker) GetUniqueClients ¶
func (t *ConnectionTracker) GetUniqueClients() []ClientSummary
GetUniqueClients returns a list of unique client IPs with connection counts.
func (*ConnectionTracker) Remove ¶
func (t *ConnectionTracker) Remove(id string)
Remove removes a connection by ID.
func (*ConnectionTracker) SetDestination ¶
func (t *ConnectionTracker) SetDestination(id, host, backend string) (Connection, bool)
SetDestination sets the destination host and backend for a tracked connection once they are known (after CONNECT / backend selection). It returns a snapshot of the updated connection and true when the connection is still tracked, or a zero Connection and false otherwise.
func (*ConnectionTracker) UpdateBytes ¶
func (t *ConnectionTracker) UpdateBytes(id string, sent, recv int64)
UpdateBytes updates the byte counters for a connection.
type MeshAPI ¶
type MeshAPI struct {
// contains filtered or unexported fields
}
MeshAPI provides the REST API for mesh network management.
func NewMeshAPI ¶
func NewMeshAPI() *MeshAPI
NewMeshAPI creates an in-memory mesh API handler with no persistence.
func NewMeshAPIWithConfig ¶
func NewMeshAPIWithConfig(cfg config.MeshConfig) (*MeshAPI, error)
NewMeshAPIWithConfig creates a mesh API handler from the server's mesh config. When cfg.StatePath is set, previously persisted networks and peers are restored and every subsequent mutation is written back. A restore failure is returned so the caller can decide whether to surface it; the returned handler is always usable (it simply starts empty).
func (*MeshAPI) CreateNetwork ¶
func (m *MeshAPI) CreateNetwork(id, name, cidr string) (*MeshNetwork, error)
CreateNetwork creates a mesh network programmatically.
func (*MeshAPI) GetNetwork ¶
func (m *MeshAPI) GetNetwork(id string) (*MeshNetwork, bool)
GetNetwork returns a mesh network by ID.
func (*MeshAPI) RegisterRoutes ¶
RegisterRoutes registers mesh API routes on a chi router.
type MeshNetwork ¶
type MeshNetwork struct {
ID string `json:"id"`
Name string `json:"name"`
CIDR string `json:"cidr"`
Created time.Time `json:"created"`
// contains filtered or unexported fields
}
MeshNetwork represents a single mesh network.
type PACGenerator ¶
type PACGenerator struct {
// contains filtered or unexported fields
}
PACGenerator generates PAC (Proxy Auto-Configuration) files.
func NewPACGenerator ¶
func NewPACGenerator(getConfig func() *config.ServerConfig, proxyHost, proxyPort, socks5Port string) *PACGenerator
NewPACGenerator creates a new PAC generator.
func (*PACGenerator) Generate ¶
func (p *PACGenerator) Generate(requestHost string) string
Generate creates a PAC file based on the current routes configuration.
func (*PACGenerator) HandlePAC ¶
func (p *PACGenerator) HandlePAC(w http.ResponseWriter, r *http.Request)
HandlePAC serves the PAC file.
type RequestLog ¶
type RequestLog struct {
// contains filtered or unexported fields
}
RequestLog maintains a ring buffer of recent requests.
func NewRequestLog ¶
func NewRequestLog(maxSize int, enabled bool) *RequestLog
NewRequestLog creates a new request log with the given max size.
func (*RequestLog) Add ¶
func (r *RequestLog) Add(entry RequestLogEntry)
Add adds a new entry to the request log.
func (*RequestLog) Clear ¶
func (r *RequestLog) Clear()
Clear removes all entries and resets the running totals reported by Stats. Entry IDs keep increasing so that GetSince cursors held by clients stay valid across a clear.
func (*RequestLog) GetAll ¶
func (r *RequestLog) GetAll() []RequestLogEntry
GetAll returns all entries.
func (*RequestLog) GetRecent ¶
func (r *RequestLog) GetRecent(n int) []RequestLogEntry
GetRecent returns the most recent n entries.
func (*RequestLog) GetSince ¶
func (r *RequestLog) GetSince(sinceID int64) []RequestLogEntry
GetSince returns entries since the given ID.
func (*RequestLog) IsEnabled ¶
func (r *RequestLog) IsEnabled() bool
IsEnabled returns whether request logging is enabled.
func (*RequestLog) SetEnabled ¶
func (r *RequestLog) SetEnabled(enabled bool)
SetEnabled enables or disables request logging.
func (*RequestLog) Stats ¶
func (r *RequestLog) Stats() RequestLogStats
Stats returns statistics about the request log. See RequestLogStats for the scope of each field.
type RequestLogEntry ¶
type RequestLogEntry struct {
ID int64 `json:"id"`
Timestamp time.Time `json:"timestamp"`
Method string `json:"method"`
Host string `json:"host"`
Path string `json:"path"`
URL string `json:"url"`
UserAgent string `json:"user_agent"`
ClientIP string `json:"client_ip"`
Username string `json:"username,omitempty"`
Backend string `json:"backend"`
StatusCode int `json:"status_code"`
BytesSent int64 `json:"bytes_sent"`
BytesRecv int64 `json:"bytes_recv"`
Duration int64 `json:"duration_ms"`
Error string `json:"error,omitempty"`
Protocol string `json:"protocol"` // HTTP, SOCKS5, CONNECT
}
RequestLogEntry represents a single request log entry.
type RequestLogStats ¶
type RequestLogStats struct {
Enabled bool `json:"enabled"`
Count int `json:"count"`
MaxSize int `json:"max_size"`
TotalRequests int64 `json:"total_requests"`
TotalBytesSent int64 `json:"total_bytes_sent"`
TotalBytesRecv int64 `json:"total_bytes_recv"`
RequestsByMethod map[string]int `json:"requests_by_method"`
RequestsByStatus map[string]int `json:"requests_by_status"`
TopHosts []HostCount `json:"top_hosts"`
}
RequestLogStats is the payload of GET /api/v1/requests/stats.
Two different scopes are mixed here deliberately, because the ring buffer cannot answer both questions from the same data:
- TotalRequests, TotalBytesSent and TotalBytesRecv are running totals accumulated on every Add since the process started or since the last Clear. They survive ring buffer eviction.
- Count, RequestsByMethod, RequestsByStatus and TopHosts describe only the entries still retained in the buffer (at most MaxSize of them), because evicted entries no longer exist.
Every field is always present, and the map/slice fields are never nil, so the Web UI can render them without null checks.
type StatsEvent ¶
type StatsEvent struct {
ActiveConnections int64 `json:"active_connections"`
TotalConnections int64 `json:"total_connections"`
BytesSent int64 `json:"bytes_sent"`
BytesReceived int64 `json:"bytes_received"`
}
StatsEvent represents a stats update event.
type ValidationError ¶
type ValidationError struct {
Section string `json:"section"`
Field string `json:"field,omitempty"`
Message string `json:"message"`
}
ValidationError represents a config validation error.
type WebSocketHub ¶
type WebSocketHub struct {
// contains filtered or unexported fields
}
WebSocketHub manages WebSocket connections.
func NewWebSocketHub ¶
func NewWebSocketHub() *WebSocketHub
NewWebSocketHub creates a new WebSocket hub with default max clients.
func NewWebSocketHubWithMaxClients ¶
func NewWebSocketHubWithMaxClients(maxClients int) *WebSocketHub
NewWebSocketHubWithMaxClients creates a new WebSocket hub with a custom max clients limit. For low-power devices (OpenWrt routers), use 5-10 to reduce memory usage.
func (*WebSocketHub) Broadcast ¶
func (h *WebSocketHub) Broadcast(eventType string, data interface{})
Broadcast sends a message to all connected clients.
func (*WebSocketHub) Run ¶
func (h *WebSocketHub) Run()
Run starts the hub's main loop. Call Stop() to terminate the loop.
func (*WebSocketHub) ServeHTTP ¶
func (h *WebSocketHub) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP upgrades the request and services the connection until it dies.
⚠ This used to use golang.org/x/net/websocket, which that package's own docs describe as having "limited support for pings, pongs and close frames". Any peer that sent a protocol-level ping — notably the Home Assistant Ingress proxy, and every browser-side keepalive — desynchronised the frame stream and the client aborted with "RSV1 set / reserved bits must be 0". The old code also only understood a literal text message "ping", which no standard client sends. Migrated to github.com/coder/websocket (the successor x/net/websocket itself points at), which handles control frames in the library.
Origin enforcement: WebSockets are exempt from both the same-origin policy and CORS, so without an Origin check any web page loaded in a browser that can reach this server could open a socket and read the live traffic stream — and when no api.token is configured this route has no auth either. Requests whose Origin host matches the request Host are always accepted (the dashboard this server serves); anything else must be named in api.allowed_origins. Requests with no Origin header at all are accepted, because non-browser clients (the CLI, curl, integration tests) do not send one and are not subject to the browser-driven attack this check defends against.
func (*WebSocketHub) SetAllowedOrigins ¶
func (h *WebSocketHub) SetAllowedOrigins(origins []string)
func (*WebSocketHub) SkipsOriginCheck ¶
func (h *WebSocketHub) SkipsOriginCheck() bool
SkipsOriginCheck reports whether origin verification has been disabled via the "*" wildcard, so the server can log that fact at startup.
func (*WebSocketHub) Stop ¶
func (h *WebSocketHub) Stop()
Stop signals the hub to stop and close all connections.