config

package
v0.4.6 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package config loads and validates /etc/mooring/config.yaml — the root of trust (plan §5.1). Everything here is fail-closed: any precondition violation returns an error and the binary refuses to boot. No web route ever reads or writes this file; it is edited only over SSH.

Index

Constants

View Source
const DefaultPath = "/etc/mooring/config.yaml"

DefaultPath is where the root-of-trust config lives in production.

View Source
const DevInsecurePermsEnv = "MOORING_DEV_INSECURE_PERMS"

DevInsecurePermsEnv, when set to a truthy value, relaxes the config-file ownership/permission enforcement for LOCAL DEVELOPMENT only. It is read from the environment (never from the config file itself) so the file can never disable its own root-of-trust checks (review #6).

Variables

This section is empty.

Functions

func DecodeKey

func DecodeKey(s string) ([]byte, error)

DecodeKey decodes a base64 (std or raw) master key.

Types

type AdminConfig

type AdminConfig struct {
	Hostname string `yaml:"hostname"`
	Listen   string `yaml:"listen"`
}

AdminConfig optionally fronts the admin UI through the edge and points at the Caddy admin endpoint (plan §6.1 SBD-1/SBD-2).

type AlertingConfig

type AlertingConfig struct {
	Enabled           bool     `yaml:"enabled"`
	EvalInterval      Duration `yaml:"eval_interval"`       // evaluator tick
	NotifyMinInterval Duration `yaml:"notify_min_interval"` // global rate limit between sends
	QuietStartHour    int      `yaml:"quiet_start_hour"`    // [0..23], -1 disables (suppresses WARNING; CRITICAL always pages)
	QuietEndHour      int      `yaml:"quiet_end_hour"`
	DeadMansURL       string   `yaml:"dead_mans_url"` // outbound heartbeat to an external cron-monitor
	DeadMansInterval  Duration `yaml:"dead_mans_interval"`
}

AlertingConfig is the Tier-1 tuning for the read-and-notify alert engine (plan §8). Channels + rules are operator-managed in the DB; this only tunes the engine. Off by default (no engine goroutines run unless enabled).

type AuthConfig

type AuthConfig struct {
	Username     string `yaml:"username"`
	PasswordHash string `yaml:"password_hash"`
	TOTPSecret   string `yaml:"totp_secret"`
}

AuthConfig holds the single operator's credentials.

type Config

type Config struct {
	BindAddr string `yaml:"bind_addr"`

	EncryptionKey         string `yaml:"encryption_key"`
	EncryptionKeyPrevious string `yaml:"encryption_key_previous"`

	IPAllowlist    []string `yaml:"ip_allowlist"`
	TrustProxy     bool     `yaml:"trust_proxy"`
	TrustedProxies []string `yaml:"trusted_proxies"`

	Auth    AuthConfig    `yaml:"auth"`
	Edge    EdgeConfig    `yaml:"edge"`
	Admin   AdminConfig   `yaml:"admin"`
	Session SessionConfig `yaml:"session"`
	Cookie  CookieConfig  `yaml:"cookie"`
	Docker  DockerConfig  `yaml:"docker"`
	Monitor MonitorConfig `yaml:"monitor"`
	Git     GitConfig     `yaml:"git"`
	GitHub  GitHubConfig  `yaml:"github"`

	CaddyEditor       EditorBlock     `yaml:"caddy_editor"`
	ComposeValidation EditorBlock     `yaml:"compose_validation"`
	Setup             SetupConfig     `yaml:"setup"`
	Retention         RetentionConfig `yaml:"retention"`
	Alerting          AlertingConfig  `yaml:"alerting"`
	Server            ServerConfig    `yaml:"server"`

	DataDir string `yaml:"data_dir"`

	// ProtectedProjects are Compose projects Mooring must never start/stop/
	// redeploy (the socket-proxy, and later the edge) — plan §3 protected set.
	ProtectedProjects []string `yaml:"protected_projects"`
	// contains filtered or unexported fields
}

Config is the typed root-of-trust document. Secret-bearing fields are kept as plain strings here (the file itself is 0600 root:root) but are never echoed: String() is overridden to redact them.

func Load

func Load(path string) (*Config, error)

Load reads, parses, and fully validates the config at path. It is the only entry point; a returned error means refuse-to-boot.

func Parse

func Parse(raw []byte) (*Config, error)

Parse decodes and validates config bytes (used by Load and by tests). Unknown keys are a hard error so a typo or smuggled key cannot slip through.

func (*Config) Allowlist

func (c *Config) Allowlist() []netip.Prefix

Allowlist returns the parsed allowlist prefixes (valid after Validate).

func (*Config) EdgeCAByName

func (c *Config) EdgeCAByName(name string) (EdgeCA, bool)

EdgeCAByName returns the configured CA with the given name.

func (*Config) HasEdgeCA

func (c *Config) HasEdgeCA(name string) bool

HasEdgeCA reports whether a named edge CA is configured.

func (*Config) IsProtectedProject

func (c *Config) IsProtectedProject(project string) bool

IsProtectedProject reports whether a compose project is in the protected set.

func (Config) String

func (c Config) String() string

String redacts the secret-bearing fields so a Config can never be logged in the clear (plan §5.5 / §15 lint).

func (*Config) TrustedProxyPrefixes

func (c *Config) TrustedProxyPrefixes() []netip.Prefix

TrustedProxyPrefixes returns the parsed trusted-proxy prefixes.

func (*Config) Validate

func (c *Config) Validate() error

Validate runs every fail-closed boot check. All violations are collected so a misconfigured operator sees them at once; any violation refuses boot.

type CookieConfig

type CookieConfig struct {
	Prefix   string `yaml:"prefix"`    // "__Host-" (default) or "__Secure-"
	BasePath string `yaml:"base_path"` // required iff prefix is __Secure-
}

CookieConfig selects the session cookie prefix model (plan §5.3). __Host- mandates Path=/ and no base path; __Secure- pairs with a base_path.

type DockerConfig

type DockerConfig struct {
	ProxyAddr     string `yaml:"proxy_addr"`
	ExternalProxy bool   `yaml:"external_proxy"`
}

DockerConfig points at the read-only docker-socket-proxy (plan §3). Mooring NEVER talks to the raw socket; ProxyAddr must be a loopback endpoint.

By default Mooring MANAGES this proxy itself: at boot it brings up the embedded, Mooring-owned read-only proxy compose so the operator never runs a docker command (they only ever write mooring.yaml). Set external_proxy: true if you run your own proxy/endpoint at proxy_addr and want Mooring to leave it alone.

type Duration

type Duration time.Duration

Duration is a yaml-friendly time.Duration ("20s", "30m", "12h").

func (Duration) D

func (d Duration) D() time.Duration

D returns the value as a time.Duration.

func (*Duration) UnmarshalYAML

func (d *Duration) UnmarshalYAML(value *yaml.Node) error

type EdgeCA

type EdgeCA struct {
	Name         string `yaml:"name"`          // [a-z][a-z0-9-]{0,30}; referenced as `ca: <name>`
	DirectoryURL string `yaml:"directory_url"` // the ACME directory URL (https)
	Email        string `yaml:"email"`         // optional ACME contact; falls back to acme_email
	TrustedRoot  string `yaml:"trusted_root"`  // optional PEM file Caddy trusts for the CA's OWN https
}

EdgeCA is an additional ACME issuer — a private/internal CA (e.g. step-ca). A mooring.yaml edge route or cert binding opts into it by Name; everything else keeps using the default edge.acme_ca. Defined ONLY here in the root-of-trust config (an app repo must never be able to introduce a trusted CA).

type EdgeConfig

type EdgeConfig struct {
	Mode             EdgeMode `yaml:"mode"`
	ACMEEmail        string   `yaml:"acme_email"`
	ACMECA           string   `yaml:"acme_ca"`
	CAs              []EdgeCA `yaml:"cas"` // extra named issuers (private CAs) routes/cert-bindings can opt into by name
	ApplyProbeWindow Duration `yaml:"apply_probe_window"`
	L4Enabled        bool     `yaml:"l4_enabled"`      // own a managed L4 (TCP/UDP) load balancer (nginx-stream)
	L4NginxDigest    string   `yaml:"l4_nginx_digest"` // pinned SHA-256 of the nginx binary (optional)
}

EdgeConfig configures the managed edge (plan §3.1, §6).

type EdgeMode

type EdgeMode string

EdgeMode is the managed/external switch (plan §3.1). Default managed.

const (
	EdgeManaged  EdgeMode = "managed"
	EdgeExternal EdgeMode = "external"
)

type EditorBlock

type EditorBlock struct {
	Mode EditorMode `yaml:"mode"`
}

EditorBlock is the strict|review knob shared by the Caddy editor and the compose importer.

type EditorMode

type EditorMode string

EditorMode gates how far the Caddy editor / compose importer may soften lints.

const (
	EditorStrict EditorMode = "strict"
	EditorReview EditorMode = "review"
)

type GitConfig

type GitConfig struct {
	PollInterval *Duration `yaml:"poll_interval"`
}

GitConfig tunes the connected-repo auto-fetch poller. So a repo connected in the dashboard "just works" with no webhook setup, Mooring fetches every connected repo on this cadence — READ-PLANE ONLY: the poller never deploys, it just surfaces an "update available" the operator then deploys with a click. (Push-to-deploy stays an explicit opt-in via the webhook + auto_deploy, never the background poller.)

PollInterval is a pointer so an omitted key (nil → the 2 min default) is distinguishable from an explicit "poll_interval: 0" (disables polling). A negative value also disables.

func (GitConfig) PollIntervalD

func (g GitConfig) PollIntervalD() time.Duration

PollIntervalD returns the effective poll interval (0 when disabled / unset).

type GitHubConfig

type GitHubConfig struct {
	ClientID     string `yaml:"client_id"`
	ClientSecret string `yaml:"client_secret"`
}

GitHubConfig holds the optional "Connect with GitHub" OAuth App credentials. The operator registers an OAuth App once in their GitHub settings and pastes the id + secret here (install-time, SSH — never a browser). When both are set, the dashboard offers one-click repo connect (pick a repo; Mooring installs a read-only deploy key for it — no key pasting). Empty = the feature is simply off.

func (GitHubConfig) Enabled

func (g GitHubConfig) Enabled() bool

Enabled reports whether GitHub connect is configured.

type MonitorConfig

type MonitorConfig struct {
	PollInterval     Duration `yaml:"poll_interval"`
	MetricsRetention Duration `yaml:"metrics_retention"`
}

MonitorConfig tunes the read-plane poller (plan §4 read plane).

type RetentionConfig

type RetentionConfig struct {
	Interval      Duration `yaml:"interval"`        // how often the retention pass runs
	EventsMaxAge  Duration `yaml:"events_max_age"`  // audit rows older than this are prunable
	EventsMaxRows int      `yaml:"events_max_rows"` // hard cap on audit rows (oldest trimmed)
	ArchiveMaxMB  int      `yaml:"archive_max_mb"`  // rotate the security-archive NDJSON past this
}

RetentionConfig is the Tier-1 (SSH-only, SIGHUP-reloadable) audit-retention block (plan §16.1). It bounds the events/audit table so it can never become the disk-wedge that kills the write plane — while NEVER silently dropping a security row (those are archived to NDJSON first, fail-closed).

type ServerConfig

type ServerConfig struct {
	// FileRoots are directories the operator may browse READ-ONLY in the Server
	// tab (e.g. an app's log dir). Each needs a stable name (used in the URL).
	FileRoots []ServerFileRoot `yaml:"file_roots"`
	// DebCacheDir is the directory the operator downloads Mooring .deb packages
	// into; the Server tab can delete OLD ones there (never the running version),
	// behind password+TOTP re-auth. Must be absolute. It also must be within the
	// service's writable paths for the delete to succeed (see docs).
	DebCacheDir string `yaml:"deb_cache_dir"`

	// VersionCheckEnabled controls the periodic self-update check: Mooring asks the
	// GitHub API whether a newer release exists and whether the RUNNING version is
	// affected by a published security advisory (→ a persistent banner + a CRITICAL
	// alert). It is ON by default (a *bool so "unset" means on); set it to false to
	// disable — then Mooring never contacts GitHub. When on it contacts ONLY
	// api.github.com and sends no telemetry payload.
	VersionCheckEnabled *bool `yaml:"version_check_enabled"`
	// VersionCheckInterval is how often to poll (default 6h; clamped to a 1h floor).
	VersionCheckInterval Duration `yaml:"version_check_interval"`

	// ImageScanEnabled controls periodic vulnerability scanning of each deployed app's
	// images + dependencies with Trivy (High/Critical → an alert; results on the Server
	// tab). ON by default (set false to disable). It is HEAVY — Trivy downloads a
	// vulnerability database and scanning uses CPU/RAM — so a small box may want it off.
	// The scanner never gets the Docker socket: upstream images are scanned from the
	// registry, build checkouts as a filesystem.
	ImageScanEnabled *bool `yaml:"image_scan_enabled"`
	// ImageScanInterval is how often to re-scan (default 24h; clamped to a 1h floor).
	// Re-scanning unchanged images matters — new CVEs are disclosed daily.
	ImageScanInterval Duration `yaml:"image_scan_interval"`
}

ServerConfig configures the read-only "Server" tab (host inspection + cleanup). Both knobs are OPT-IN and default to off/empty: with no file_roots the file view is disabled entirely (fail-closed), and with no deb_cache_dir the old-.deb cleanup is unavailable. The host monitor (CPU/memory/disk/processes) needs no config. Secret/state paths (the data dir, config, keys, DB) are ALWAYS denied to the file view regardless of what's listed here — see internal/web.

func (ServerConfig) ImageScanOn added in v0.4.6

func (s ServerConfig) ImageScanOn() bool

ImageScanOn reports whether app vulnerability scanning is enabled (default on).

func (ServerConfig) VersionCheckOn added in v0.4.6

func (s ServerConfig) VersionCheckOn() bool

VersionCheckOn reports whether the self-update check is enabled (default on).

type ServerFileRoot

type ServerFileRoot struct {
	Name string `yaml:"name"` // [a-z0-9][a-z0-9-]{0,30}; used in the URL/UI
	Path string `yaml:"path"` // absolute directory path
}

ServerFileRoot is one named, browsable directory.

type SessionConfig

type SessionConfig struct {
	IdleTimeout     Duration `yaml:"idle_timeout"`
	AbsoluteTimeout Duration `yaml:"absolute_timeout"`
}

SessionConfig holds idle/absolute timeouts (plan §5.3).

type SetupConfig

type SetupConfig struct {
	Enabled bool `yaml:"enabled"`
	// Image is the digest-pinned jail base image (the throwaway container backend).
	Image string `yaml:"image"`
	// Resource limits — counted against the global one-docker-child semaphore.
	WallClock   Duration `yaml:"wall_clock"`    // hard wall-clock timeout
	CPUs        string   `yaml:"cpus"`          // docker --cpus value, e.g. "1.0"
	MemoryMB    int      `yaml:"memory_mb"`     // hard memory cap (MemorySwapMax=0)
	PidsLimit   int      `yaml:"pids_limit"`    // max processes
	ScratchMB   int      `yaml:"scratch_mb"`    // writable scratch quota
	OutputCapKB int      `yaml:"output_cap_kb"` // captured stdout/stderr cap
}

SetupConfig gates the Mode-3 setup-script sandbox (plan §7/§9, OFF by default, hard-gated). When enabled, scripts run in a throwaway jail with the limits below; the binary refuses to boot if the host can't provide a working sandbox (plan §5.1), and a live self-test runs before EVERY execution.

Jump to

Keyboard shortcuts

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