Documentation
¶
Overview ¶
Package trust manages the publisher trust store — a local, file-backed registry of trusted publisher public keys with TOFU and manual pre-pinning support.
The trust store is persisted at <home>/trust/publishers.json (0600) inside a trust/ directory (0700). Concurrent access is serialized via flock on publishers.json.lock, and writes are atomic (temp file + rename + fsync).
Index ¶
- Constants
- Variables
- func DefaultStorePath(homeDir string) string
- func DisplayFingerprint(s string) string
- func FingerprintFromPEM(pemData string) (string, error)
- func IsValidAlias(alias string) bool
- func NormalizeFingerprint(s string) string
- type Publisher
- type PublisherStatus
- type Store
- func (s *Store) CheckKeyConflict(alias string, fingerprint string) *Publisher
- func (s *Store) Get(fingerprint string) (*Publisher, bool)
- func (s *Store) Len() int
- func (s *Store) Pin(pub Publisher, source TrustSource) error
- func (s *Store) Publishers() []Publisher
- func (s *Store) Remove(fingerprint string) error
- func (s *Store) Save() error
- type TrustSource
Constants ¶
const LockTimeout = 5 * time.Second
LockTimeout is the maximum duration to wait for the flock lock.
Variables ¶
var ( // ErrStoreCorrupt is returned when the on-disk trust store file exists but // contains malformed JSON. Callers MUST surface this as a hard error — the // store is never silently replaced with an empty one. ErrStoreCorrupt = errors.New("trust store file is corrupt: malformed JSON") // ErrDuplicateFingerprint is returned by Pin when a publisher with the // same fingerprint already exists in the store. ErrDuplicateFingerprint = errors.New("publisher fingerprint already trusted") // ErrFingerprintMismatch is returned by Pin when the PEM-derived // fingerprint does not match the stored fingerprint field. ErrFingerprintMismatch = errors.New("fingerprint does not match the public key PEM") // ErrNotFound is returned when a publisher lookup by fingerprint fails. ErrNotFound = errors.New("publisher not found") // ErrLockTimeout is returned when flock cannot be acquired within the // configured timeout. ErrLockTimeout = errors.New("could not acquire trust store lock") )
Sentinel errors returned by trust store operations.
Functions ¶
func DefaultStorePath ¶
DefaultStorePath returns the default path to the trust store file for the given home directory.
func DisplayFingerprint ¶
DisplayFingerprint formats a 64-character hex fingerprint into groups of 4 characters separated by spaces (e.g. "a1b2 c3d4 e5f6 ..."). If the input is not exactly 64 hex characters, it is returned as-is.
func FingerprintFromPEM ¶
FingerprintFromPEM parses a PEM-encoded public key and returns its hex-encoded SHA-256 fingerprint (same as pack.PublicKeyFingerprint). The PEM must contain a "PUBLIC KEY" block with an ECDSA P-256 key.
func IsValidAlias ¶
IsValidAlias returns true if the alias is a valid slug: lowercase alphanumeric with hyphens, 1-64 characters.
func NormalizeFingerprint ¶
NormalizeFingerprint strips all whitespace, colons, and dashes from a fingerprint string and lowercases it. This accepts both compact form (64 hex chars) and display form (groups of 4 hex chars separated by spaces, e.g. "a1b2 c3d4 ...").
Types ¶
type Publisher ¶
type Publisher struct {
// Fingerprint is the hex-encoded SHA-256 of the PKIX-encoded public key
// (64 lowercase hex characters).
Fingerprint string `json:"fingerprint"`
// PublicKeyPEM is the PEM-encoded public key (PKIX, SPKI block).
PublicKeyPEM string `json:"public_key_pem"`
// Alias is an optional human-readable slug for the publisher (e.g. "parvez").
Alias string `json:"alias"`
// FirstSeen is the RFC 3339 timestamp when the publisher was first trusted.
FirstSeen string `json:"first_seen"`
// LastUsed is the RFC 3339 timestamp when the publisher was last used.
LastUsed string `json:"last_used"`
// Source indicates how the publisher was added (tofu or manual).
Source TrustSource `json:"source"`
// Status is the trust state of the publisher.
Status PublisherStatus `json:"status"`
}
Publisher represents a single trusted publisher in the trust store.
type PublisherStatus ¶
type PublisherStatus string
PublisherStatus indicates the trust state of a publisher.
const ( // StatusTrusted is the normal operating state for a publisher. StatusTrusted PublisherStatus = "trusted" )
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is an in-memory representation of the trust store, backed by a file on disk. Use Load to create a Store from an existing file, or to initialize a new empty store. Use Save to persist changes.
func Load ¶
Load reads the trust store from the file at the given path. If the file does not exist, an empty store is returned (missing file is valid — it means no publishers have been trusted yet). If the file exists but contains malformed JSON, ErrStoreCorrupt is returned.
The trust directory is created with mode 0700 if it does not exist.
func (*Store) CheckKeyConflict ¶
CheckKeyConflict returns a trusted publisher that shares the same alias but has a different fingerprint than the given one. This is used for SSH-style warnings when a known alias (hostname) presents a new key. Returns nil if no conflict exists.
func (*Store) Get ¶
Get returns a publisher by fingerprint. The second return value is false if no publisher with that fingerprint exists.
func (*Store) Pin ¶
func (s *Store) Pin(pub Publisher, source TrustSource) error
Pin adds a publisher to the trust store. It validates that:
- The fingerprint is not already present (ErrDuplicateFingerprint).
- The public key PEM parses successfully.
- The SHA-256 of the PKIX-encoded public key matches the stored fingerprint field (ErrFingerprintMismatch).
If all checks pass, FirstSeen is set to the current time and the store is marked dirty (Save must be called to persist).
func (*Store) Publishers ¶
Publishers returns all publishers in the store as a slice.
func (*Store) Remove ¶
Remove deletes a publisher from the trust store by fingerprint. It returns ErrNotFound if no publisher with that fingerprint exists.
func (*Store) Save ¶
Save persists the trust store to disk using atomic write (temp file + rename + fsync). It acquires an exclusive flock on publishers.json.lock to serialize concurrent access, re-reads the current on-disk state to merge concurrent updates, and then writes the merged result atomically.
type TrustSource ¶
type TrustSource string
TrustSource indicates how a publisher was added to the trust store.
const ( // SourceTOFU means the publisher was trusted on first use (automatically). SourceTOFU TrustSource = "tofu" // SourceManual means the publisher was manually pre-pinned by the operator. SourceManual TrustSource = "manual" )