Documentation
¶
Overview ¶
Package config loads the server configuration: one YAML file, decoded strictly, plus MYOWNPKI_* environment overrides (go-conventions.md "Configuration", PLAN §14).
Rules:
- Unknown keys, a second YAML document and files over MaxFileSize are errors.
- Unknown MYOWNPKI_* variables are errors too: a typo must not be ignored.
- Dev mode is a command-line flag (serve --dev), never a config key.
- No secrets: the unseal passphrase is never read from a file or variable.
Load returns a validated Config or an error that names every problem.
Index ¶
- Constants
- Variables
- func ACMEBaseURL(s string) (string, error)
- func ACMEHost(base string) string
- func ACMEResolver(s string) (string, error)
- func AgentBaseURL(s string) (string, error)
- func AgentHost(base string) string
- func DefaultPath(goos string, getenv func(string) string) string
- func EnvNames() []string
- func Load(path string, environ []string) (Config, Source, error)
- func ParseNetworks(s string) ([]netip.Prefix, error)
- func ParseTLSNames(s string) ([]string, []netip.Addr, error)
- func PasskeyRPID(origin string) (string, error)
- func WebhookURL(s string) (*url.URL, error)
- type ACME
- type Admin
- type Agent
- type Auth
- type Config
- type Email
- type Log
- type Monitor
- type MonitorValues
- type Notify
- type Public
- type ScanValues
- type Source
- type Webhook
Constants ¶
const ( SMTPStartTLS = "starttls" SMTPTLS = "tls" SMTPNone = "none" )
Email security modes.
const DefaultScanPorts = "443, 465, 636, 853, 993, 995, 5061, 6514, 8443, 8883, 9443"
DefaultScanPorts are the ports where TLS starts with the first byte.
const EnvConfigFile = EnvPrefix + "CONFIG"
EnvConfigFile names the config file when --config is not given.
const EnvPrefix = "MYOWNPKI_"
EnvPrefix starts every environment variable this package reads.
const MaxFileSize = 64 << 10
MaxFileSize limits the config file; a real one is a few hundred bytes.
Variables ¶
var ( LogLevels = []string{"debug", "info", "warn", "error"} LogFormats = []string{"json", "text"} )
Allowed values.
var ErrInvalid = errors.New("invalid configuration")
ErrInvalid is wrapped by every error about a malformed or inconsistent configuration.
Functions ¶
func ACMEBaseURL ¶ added in v0.2.0
ACMEBaseURL checks acme.base_url and returns it without a trailing slash: https only (the proxy terminates TLS; RFC 8555 §6.1), a host, no user, query or fragment.
func ACMEHost ¶ added in v0.2.2
ACMEHost is the host of acme.base_url in lower case: the name (or IP) in the certificate of the ACME listener with acme.tls_issuer.
func ACMEResolver ¶ added in v0.2.0
ACMEResolver checks acme.resolver: empty, an IP (port 53) or IP:port.
func AgentBaseURL ¶ added in v0.3.0
AgentBaseURL checks agent.base_url: https://host[:port], no path.
func AgentHost ¶ added in v0.3.0
AgentHost is the host of agent.base_url in lower case: the name (or IP) in the agent listener's certificate.
func DefaultPath ¶
DefaultPath is the config file used when neither --config nor MYOWNPKI_CONFIG is given: %ProgramData%\MyOwnPKI\config.yaml on Windows, /etc/myownpki/config.yaml elsewhere. getenv is os.Getenv in production.
func ParseNetworks ¶
ParseNetworks parses a comma-separated list of IPs and CIDRs (trusted proxies, the admin allowlist). A single IP becomes a /32 or /128. Zones and host bits in a CIDR are refused – a typo like 10.0.0.1/8 must not silently trust a whole network.
func ParseTLSNames ¶
ParseTLSNames splits admin.tls_names into DNS names and IPs. DNS names are LDH labels in lower case (a wildcard is not allowed: the bootstrap certificate names exactly the hosts admins use).
func PasskeyRPID ¶
PasskeyRPID checks auth.passkey_origin and returns its host name, the WebAuthn RP ID ("" when passkeys are off). Browsers allow WebAuthn only in a secure context: https, or http on localhost.
Types ¶
type ACME ¶ added in v0.2.0
type ACME struct {
// Listen is IP:port of the ACME listener – never the public CRL port, so
// nobody talks ACME in clear text. Without TLSIssuer it is for the reverse
// proxy only; with it, for the clients (e.g. :443).
Listen string `yaml:"listen"`
// BaseURL is what clients use (the proxy's https URL, e.g.
// https://pki.homelab.lan/acme); every URL the server hands out is built
// from it. Empty: ACME is off.
BaseURL string `yaml:"base_url"`
// TrustedProxies may connect to Listen (others get 403), and only their
// X-Forwarded-For is believed (IPs or CIDRs, comma-separated). Ignored
// with TLSIssuer: then every client connects directly and the peer is
// the client address.
TrustedProxies string `yaml:"trusted_proxies"`
// Resolver is the DNS server (IP or IP:port) for dns-01 and for resolving
// http-01 names. Empty: the system resolver.
Resolver string `yaml:"resolver"`
// TLSIssuer names a CA of this instance: ACME runs with TLS on Listen,
// with a certificate for the host of BaseURL that the server issues from
// this CA itself. Empty: plain HTTP behind a reverse proxy.
TLSIssuer string `yaml:"tls_issuer"`
}
ACME is the ACME server (PHASE2): by default plain HTTP on its own listener, only behind a reverse proxy that terminates TLS (owner Q1 2026-10-22); with TLSIssuer it speaks TLS itself (v0.2.2, owner 2026-10-24).
type Admin ¶
type Admin struct {
// Listen is IP:port; an empty IP means all interfaces.
Listen string `yaml:"listen"`
// TLSCertFile and TLSKeyFile name an operator-provided certificate. Both
// empty: a self-signed bootstrap certificate is created (PLAN §5).
TLSCertFile string `yaml:"tls_cert_file"`
TLSKeyFile string `yaml:"tls_key_file"`
// TLSNames are further DNS names or IPs (comma-separated) for the
// bootstrap certificate, besides the host name, the listen IP and
// loopback – the names admins type into the browser. With TLSIssuer they
// are exactly the names of the issued certificate.
TLSNames string `yaml:"tls_names"`
// TLSIssuer names a CA of this instance (v0.2.2): the server issues the
// listener's certificate from it itself, while unsealed, and renews it.
// Empty: the operator certificate or the bootstrap certificate.
TLSIssuer string `yaml:"tls_issuer"`
// AllowedNetworks limits who may connect (IPs or CIDRs, comma-separated);
// empty: everybody. Checked against the direct peer before TLS (ADR-08).
AllowedNetworks string `yaml:"allowed_networks"`
}
Admin is the admin listener: web UI and REST API (ADR-08).
type Agent ¶ added in v0.3.0
type Agent struct {
// BaseURL is what agents connect to, e.g. https://pki.homelab.lan:8444;
// its host is the name in the listener certificate. Empty: no agents.
BaseURL string `yaml:"base_url"`
// Listen is IP:port of the agent listener – its own port, never the
// admin, ACME or public one.
Listen string `yaml:"listen"`
// TLSIssuer names the CA that issues the listener certificate, as for
// admin.tls_issuer: an intermediate, issued while unsealed.
TLSIssuer string `yaml:"tls_issuer"`
// CA names the agent CA: an intermediate with the EKU limit clientAuth
// only. It signs the agents' 7-day certificates, and only certificates
// from it pass the listener's client authentication.
CA string `yaml:"ca"`
// PackageDir is the immutable release-artifact directory exposed by the
// agent listener. The host installer stages it; the container uses /opt.
PackageDir string `yaml:"package_dir"`
}
Agent is the agent listener (ADR-08, docs/PHASE3.md §1.4): Connect-RPC over mTLS for myownpki-agent. Off while BaseURL is empty.
type Auth ¶
type Auth struct {
// KeyFile holds the key that encrypts the TOTP secrets and keys the
// recovery code hashes. Empty: <data_dir>/auth.key. It may live outside
// the data directory (Docker secret, systemd credential): then a copy of
// the databases alone holds no usable TOTP secret.
KeyFile string `yaml:"key_file"`
// TrustedProxies lists reverse proxies (IPs or CIDRs, comma-separated)
// whose X-Forwarded-For header is believed. Empty: the direct peer is
// the client – the safe default.
TrustedProxies string `yaml:"trusted_proxies"`
// PasskeyOrigin is the address users open the admin UI with, e.g.
// https://pki.homelab.lan:9443 (PHASE1 step 17). Passkeys are bound to its
// host name (the WebAuthn RP ID). Empty: no passkeys, TOTP only.
PasskeyOrigin string `yaml:"passkey_origin"`
}
Auth configures sign-in (PHASE1 step 10).
type Config ¶
type Config struct {
// DataDir holds system.db and the tenant directories. Absolute path.
DataDir string `yaml:"data_dir"`
Log Log `yaml:"log"`
Admin Admin `yaml:"admin"`
Public Public `yaml:"public"`
Auth Auth `yaml:"auth"`
ACME ACME `yaml:"acme"`
Agent Agent `yaml:"agent"`
Monitor Monitor `yaml:"monitor"`
Notify Notify `yaml:"notify"`
}
Config is the server configuration. Field order is the order of the example file.
func Default ¶
func Default() Config
Default returns the configuration used for every key the file leaves out. The admin listener binds to loopback only: remote administration is a deliberate decision of the operator.
func (*Config) ApplyEnv ¶
ApplyEnv applies MYOWNPKI_* overrides from environ (os.Environ format) and returns the names it applied, sorted. An unknown MYOWNPKI_* variable is an error: a typo would otherwise be ignored silently. An empty value is applied as well, so MYOWNPKI_PUBLIC_LISTEN= disables the public listener.
func (Config) AuthKeyFile ¶
AuthKeyFile returns the path of the auth key: auth.key_file or <data_dir>/auth.key.
func (Config) NotifyFileProblems ¶ added in v0.3.3
NotifyFileProblems reads the files that the configured notification channels need and names each one that is missing, unreadable or useless: the password and secret files and the extra CA files. The server reads them at each send, so a problem here is no start error (the alerts still open); `config check` reports it, because every message would fail. Contents are checked, never returned or printed. Run as the service user: a file only root can read is a finding too.
type Email ¶ added in v0.3.3
type Email struct {
// SMTP is host:port of the mail server.
SMTP string `yaml:"smtp"`
// Security: starttls (default), tls (implicit, port 465) or none (only
// without username – a password never travels in plain text).
Security string `yaml:"security"`
Username string `yaml:"username"`
PasswordFile string `yaml:"password_file"`
From string `yaml:"from"`
// To: one or more addresses, comma-separated.
To string `yaml:"to"`
// CAFile: extra trusted roots for the mail server's certificate, e.g.
// this PKI's root when the mail server has a certificate from it.
CAFile string `yaml:"ca_file"`
}
Email is the SMTP channel; off while smtp is empty.
func (Email) Recipients ¶ added in v0.3.3
Recipients splits to.
type Log ¶
type Log struct {
Level string `yaml:"level"` // debug, info, warn, error
Format string `yaml:"format"` // json (production) or text
}
Log configures the process log (not the audit log).
type Monitor ¶ added in v0.3.3
type Monitor struct {
// ExpiryDays: warning and critical days before an end-entity
// certificate expires, "14, 3".
ExpiryDays string `yaml:"expiry_days"`
// CAExpiryDays: the same for CA certificates, "90, 30".
CAExpiryDays string `yaml:"ca_expiry_days"`
// SealedAfter: how long the server may stay sealed before it alerts.
SealedAfter string `yaml:"sealed_after"`
// AgentSilentAfter: how long an agent may be silent before it alerts.
AgentSilentAfter string `yaml:"agent_silent_after"`
// RepeatEvery: the reminder for an alert that is still open.
RepeatEvery string `yaml:"repeat_every"`
// ScanInterval: how often each endpoint is scanned, "5m" to "24h", or
// "off" when the server must not connect anywhere.
ScanInterval string `yaml:"scan_interval"`
// ScanTimeout: resolve, connect and handshake of one scan together.
ScanTimeout string `yaml:"scan_timeout"`
// ScanPorts: the only ports an endpoint may have, "443, 8443".
ScanPorts string `yaml:"scan_ports"`
// ScanMaxEndpoints: how many endpoints may be entered.
ScanMaxEndpoints string `yaml:"scan_max_endpoints"`
// ScanUnreachableAfter: failed scans in a row before scan_unreachable opens.
ScanUnreachableAfter string `yaml:"scan_unreachable_after"`
// ScanMismatchAfter: how old the newest certificate for a name must be
// before an older one still being served is an alert.
ScanMismatchAfter string `yaml:"scan_mismatch_after"`
}
Monitor holds the thresholds of the monitoring (docs/PHASE3.md §9.2). Like every key they are strings, so environment variables can set them.
func (Monitor) Scan ¶ added in v0.3.4
func (m Monitor) Scan() (ScanValues, error)
Scan parses and checks the TLS scan keys; the error names the key.
func (Monitor) Values ¶ added in v0.3.3
func (m Monitor) Values() (MonitorValues, error)
Values parses and checks the thresholds; the error names the key.
type MonitorValues ¶ added in v0.3.3
type MonitorValues struct {
ExpiryWarn, ExpiryCrit time.Duration
CAWarn, CACrit time.Duration
SealedAfter, AgentSilent time.Duration
RepeatEvery time.Duration
}
MonitorValues are the parsed thresholds.
type Notify ¶ added in v0.3.3
Notify holds the notification channels (docs/PHASE3.md §9.3). They live in the configuration, not the database: a sealed server must still send "sealed", and it can't decrypt anything. Secrets are files the service user reads at each send; the configuration only names them.
type Public ¶
type Public struct {
// Listen is IP:port; empty disables the listener.
Listen string `yaml:"listen"`
}
Public is the public listener: CRL now, OCSP, AIA and ACME later (ADR-08).
type ScanValues ¶ added in v0.3.4
type ScanValues struct {
// Interval is 0 when scanning is off.
Interval time.Duration
Timeout time.Duration
Ports []int
MaxEndpoints int
UnreachableAfter int
MismatchAfter time.Duration
}
ScanValues are the parsed monitor.scan_* keys.
func (ScanValues) Off ¶ added in v0.3.4
func (v ScanValues) Off() bool
Off reports whether scanning is switched off.
type Source ¶
type Source struct {
File string // the config file that was read
Env []string // MYOWNPKI_* variables that overrode a value, sorted
}
Source describes where the configuration came from, for `config check`.