web

package
v0.30.0 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrEnrolNotFound       = fmt.Errorf("enrolment not found")
	ErrEnrolAlreadyRunning = fmt.Errorf("enrolment already running")
	ErrEnrolBusy           = fmt.Errorf("another enrolment is running")
	ErrEnrolInvalidEngine  = fmt.Errorf("enrolment has no valid engine")
	ErrEnrolNotStartable   = fmt.Errorf("enrolment is not in a startable state")
	ErrEnrolNotResettable  = fmt.Errorf("enrolment is not in a resettable state")
)

Sentinel errors for enrolment operations.

Functions

func ValidateBrowseURL added in v0.28.0

func ValidateBrowseURL(raw string) (*url.URL, error)

ValidateBrowseURL enforces the remote-browse allowlist: the value must parse as an absolute http or https URL with a host and no embedded credentials. Everything else — file://, custom protocol handlers (vscode:, ssh:), scheme-relative or bare paths, user:pass@ forms — is rejected, because the browser opener hands the string to xdg-open / `open` / ShellExecute, which would happily dispatch non-web schemes to arbitrary local handlers, and userinfo would carry credentials into the opener and its logs. Returns the parsed URL so callers never re-parse (the canonical string form is u.String()).

The rule itself lives in internal/urlallow so the notification action link (internal/notify) enforces byte-for-byte the same allowlist from one implementation — notify cannot import web (web imports notify), so a shared leaf package is the seam.

Types

type CSRFStore

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

CSRFStore manages CSRF token generation and validation.

func NewCSRFStore

func NewCSRFStore() *CSRFStore

NewCSRFStore creates a new CSRF token store.

func (*CSRFStore) IssueHandler

func (cs *CSRFStore) IssueHandler() http.HandlerFunc

IssueHandler returns an HTTP handler that issues a new CSRF token.

func (*CSRFStore) Validate

func (cs *CSRFStore) Validate(r *http.Request) bool

Validate checks if the request contains a valid CSRF token.

type EnrolStateInfo

type EnrolStateInfo struct {
	Key          string   `json:"key"`
	Engine       string   `json:"engine"`
	EngineName   string   `json:"name"`
	Status       string   `json:"status"`
	Fields       []string `json:"fields"`
	Output       []string `json:"output,omitempty"`
	Error        string   `json:"error,omitempty"`
	HelpTextHTML string   `json:"help_text_html,omitempty"`
}

EnrolStateInfo is the JSON-serializable view of an enrolment's state.

type EnrolmentRunner

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

EnrolmentRunner manages per-enrolment lifecycle for web mode.

func NewEnrolmentRunner

func NewEnrolmentRunner(enrolments map[string]config.Enrolment) *EnrolmentRunner

NewEnrolmentRunner creates a runner from the enrolments config. All enrolments start as "pending". Call MarkComplete() for enrolments that are already satisfied in Vault before exposing to the frontend.

func (*EnrolmentRunner) AnyRunning added in v0.22.0

func (r *EnrolmentRunner) AnyRunning() bool

AnyRunning reports whether any enrolment is currently executing. The daemon's config-refresh loop checks it before swapping the runner for a changed enrolments map, so a mid-run engine is never orphaned.

func (*EnrolmentRunner) Complete

func (r *EnrolmentRunner) Complete()

Complete signals that the user is done with enrolments.

func (*EnrolmentRunner) GetState

func (r *EnrolmentRunner) GetState(key string) (EnrolStateInfo, error)

GetState returns the state of a single enrolment.

func (*EnrolmentRunner) HasPending

func (r *EnrolmentRunner) HasPending() bool

HasPending returns true if any enrolment is pending, running, or failed.

func (*EnrolmentRunner) MarkComplete

func (r *EnrolmentRunner) MarkComplete(key string)

MarkComplete sets an enrolment to "complete" (e.g. already in Vault).

func (*EnrolmentRunner) Reset

func (r *EnrolmentRunner) Reset(key string) error

Reset returns a complete or skipped enrolment to pending so it can be re-run. Returns an error if the key is not found, the enrolment is running, or it is in a state that cannot be reset (e.g. pending or failed).

func (*EnrolmentRunner) Skip

func (r *EnrolmentRunner) Skip(key string) error

Skip marks an enrolment as skipped. Returns error if key not found or running.

func (*EnrolmentRunner) Start

func (r *EnrolmentRunner) Start(ctx context.Context, key string, vc *vault.Client, kvMount, userPrefix, username string, promptSecret PromptSecretFunc) error

Start launches an enrolment engine in a background goroutine. Returns error if the key is unknown, the enrolment is already running, or another enrolment is currently running (only one may run at a time because the secret prompt mechanism is global).

func (*EnrolmentRunner) States

func (r *EnrolmentRunner) States() []EnrolStateInfo

States returns the current state of all enrolments in stable order.

func (*EnrolmentRunner) Wait

func (r *EnrolmentRunner) Wait()

Wait blocks until Complete() is called. Returns immediately if there are no pending enrolments.

func (*EnrolmentRunner) WaitForKey

func (r *EnrolmentRunner) WaitForKey(key string)

WaitForKey blocks until the given enrolment is no longer "running". Returns immediately if the enrolment is not found or not running.

type OAuthManager

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

OAuthManager manages OAuth2 state parameters for CSRF protection.

func NewOAuthManager

func NewOAuthManager() *OAuthManager

NewOAuthManager creates a new OAuth state manager.

func (*OAuthManager) CreateState

func (om *OAuthManager) CreateState(ruleName string) (string, error)

CreateState generates a cryptographically random state parameter for an OAuth flow.

func (*OAuthManager) ValidateState

func (om *OAuthManager) ValidateState(state string) (string, bool)

ValidateState checks and consumes a state parameter. Returns the rule name and true if valid.

type PromptSecretFunc

type PromptSecretFunc func(ctx context.Context, label string) (string, error)

PromptSecretFunc is the function signature for web-based secret prompting.

type Server

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

Server is the web UI HTTP server.

func NewServer

func NewServer(sc ServerConfig) (*Server, error)

NewServer creates a new web server.

func (*Server) EnrolPromptSecret

func (s *Server) EnrolPromptSecret(ctx context.Context, label string) (string, error)

EnrolPromptSecret implements a web-based PromptSecret. It sets the pending prompt state and blocks until the frontend submits a value via the /api/v1/enrol/secret endpoint, or the context is cancelled.

func (*Server) ForceReauth

func (s *Server) ForceReauth()

ForceReauth clears the in-memory Vault token so /api/v1/status reports authenticated=false on the next poll. The SPA is configured to redirect to the login screen whenever that flag flips, which effectively invalidates any browser session that was sitting on a stale "logged-in" view while the underlying token rotted. The token file on disk is intentionally left in place — operators may have written a fresh token out-of-band that the daemon can pick up without involving the user.

Also resets the authDone channel so a fresh call to WaitForAuth (e.g. from a re-entry into the startup auth flow) will block until the user completes the new login.

func (*Server) InitEnrolments

func (s *Server) InitEnrolments(ctx context.Context, enrolments map[string]config.Enrolment)

InitEnrolments sets up the enrolment runner for web-driven enrolment. It checks Vault for already-completed enrolments and marks them as such.

func (*Server) InitialSyncComplete

func (s *Server) InitialSyncComplete() bool

InitialSyncComplete reports whether MarkInitialSyncComplete has fired. Exposed for tests and the /readyz handler.

func (*Server) MarkInitialSyncComplete

func (s *Server) MarkInitialSyncComplete()

MarkInitialSyncComplete flips the readiness flag. The daemon calls this once engine.RunOnce returns at startup (success or per-rule failure — partial progress is still "we've tried"). /readyz only reports ready once this has fired AND the daemon holds a Vault token, so a k8s readinessProbe or the OTel httpcheckreceiver doesn't observe a green daemon before secrets exist on disk.

func (*Server) SetReauthGate added in v0.30.0

func (s *Server) SetReauthGate(g interface{ NeedsReauth() bool })

SetReauthGate wires the daemon's token lifecycle manager so /api/v1/token can decline to hand out a token the daemon already knows is dead. Safe to call while requests are in flight; the daemon calls it once, after the lifecycle manager is constructed.

func (*Server) Shutdown

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

Shutdown gracefully stops the server and cleans up resources.

func (*Server) Start

func (s *Server) Start() error

Start begins serving HTTP on every configured listener. It signals WaitReady once all of them are bound, or sends the bind error so the caller can fail fast.

There may be one or two: the loopback TCP listener (web.enabled) and the per-user Unix socket (api.enabled). They share one http.Server and one mux, so a request means the same thing whichever way it arrived — the surfaces differ in who can reach them, not in what they do.

func (*Server) URL

func (s *Server) URL() string

URL returns the web UI root URL.

func (*Server) UpdateDynamicConfig added in v0.22.0

func (s *Server) UpdateDynamicConfig(rules []config.Rule, syncCfg config.SyncConfig)

UpdateDynamicConfig swaps the rule and sync sections the web server serves. Called by the daemon's config-refresh loop when the remote overlay (or an edited local config) changes them at runtime, so the dashboard, the rules API, and the Effective Configuration download all reflect the live state.

func (*Server) UpdateEnrolments added in v0.22.0

func (s *Server) UpdateEnrolments(ctx context.Context, enrolments map[string]config.Enrolment) bool

UpdateEnrolments swaps the web enrolment runner for a changed enrolments map at runtime. It refuses (returning false) while an enrolment is mid-run, because InitEnrolments replaces the runner wholesale and would orphan the running engine's progress — the caller retries on its next refresh tick. The check-then-swap is not atomic with a concurrent start, but the refresh cadence makes that window academic and the consequence is a re-runnable enrolment, not corruption.

func (*Server) WaitForAuth

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

WaitForAuth blocks until authentication completes or the context is cancelled.

func (*Server) WaitForEnrolments

func (s *Server) WaitForEnrolments()

WaitForEnrolments blocks until the user completes the enrolment page. Returns immediately if there are no pending enrolments or no runner.

func (*Server) WaitReady

func (s *Server) WaitReady() error

WaitReady blocks until the web server is listening and returns any startup error.

type ServerConfig

type ServerConfig struct {
	WebCfg   config.WebConfig
	VaultCfg config.VaultConfig
	SyncCfg  config.SyncConfig
	ObsCfg   config.ObservabilityConfig
	Rules    []config.Rule
	Vault    *vault.Client
	Engine   *internalsync.Engine
	// Agent, when non-nil, exposes the SSH agent status on /api/v1/status.
	Agent agentStatusProvider
	// AgentCfg is the loaded agent configuration. It is the section the
	// config-download endpoint re-emits, so it round-trips through the same
	// YAML/.reg renderers as every other section even when the daemon loaded
	// its config from a Windows GPO.
	AgentCfg config.AgentConfig
	// APICfg is the loaded local-API-socket configuration, retained for the
	// config-download round-trip (like AgentCfg).
	APICfg config.APIConfig
	// APISocketPath is the resolved path for the local API socket. Empty
	// serves no socket. The caller resolves it (config.APISocketPath) so this
	// package holds no path-defaulting policy; when it is set, Start binds it
	// with owner-only permissions in addition to — or instead of — the
	// loopback TCP listener that WebCfg.Enabled controls.
	APISocketPath string
	// RemoteCfg is the local-only remote_config section, retained for the
	// config-download round-trip (like AgentCfg).
	RemoteCfg config.RemoteConfig
	// RemoteStatus, when non-nil, reports the remote-config overlay's last
	// fetch outcome; surfaced on /api/v1/status alongside the per-rule sync
	// state. It may return nil before the first fetch.
	RemoteStatus  func() *remoteconfig.Status
	Username      string
	TokenFilePath string
	Version       string
	// OpenBrowser, when non-nil, overrides how the remote-browse endpoint
	// launches URLs in this host's default browser (tests inject a fake).
	// Nil selects the real browser.OpenURL.
	OpenBrowser func(string) error
	// SendNotification, when non-nil, overrides how the remote-notify
	// endpoint delivers desktop notifications (tests inject a fake). Nil
	// selects the real notify.Send.
	SendNotification notify.Notifier
	// SetClipboard, when non-nil, overrides how the remote-clipboard
	// endpoint writes to this host's clipboard (tests inject a fake). Nil
	// selects the real clipboard.Set.
	SetClipboard clipboard.Setter
}

ServerConfig holds all dependencies for the web server.

Jump to

Keyboard shortcuts

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