Documentation
¶
Overview ¶
Package integrity verifies the plexd binary and hook scripts by SHA-256 checksum, and the SSH host key by its OpenSSH fingerprint.
Package integrity verifies the plexd binary and hook scripts by SHA-256 checksum, and the SSH host key by its OpenSSH fingerprint.
Index ¶
- Constants
- func HashFile(path string) (string, error)
- func HostKeyFingerprint(path string) (string, error)
- func SelfChecksum() (string, error)
- func WireChecksum(hexDigest string) (string, error)
- type CheckResult
- type Config
- type Store
- type Verifier
- func (v *Verifier) BinaryChecksum() string
- func (v *Verifier) Run(ctx context.Context, nodeID string) error
- func (v *Verifier) VerifyBinary(ctx context.Context, nodeID string) error
- func (v *Verifier) VerifyHook(ctx context.Context, nodeID, hookPath, expectedChecksum string) (bool, error)
- func (v *Verifier) VerifyHooksDir(ctx context.Context, nodeID string)
- func (v *Verifier) VerifyHostKey(ctx context.Context, nodeID string) error
- type ViolationReporter
Constants ¶
const DefaultVerifyInterval = 5 * time.Minute
DefaultVerifyInterval is the default interval between integrity verification runs.
Variables ¶
This section is empty.
Functions ¶
func HostKeyFingerprint ¶ added in v0.4.0
HostKeyFingerprint parses the OpenSSH private key at path and renders its public half as the canonical `SHA256:<base64>` fingerprint — the form `ssh-keygen -l` prints, the one the capability manifest already carries, and the one the integrity contract's fingerprint fields accept.
The fingerprint, not a file digest, is what identifies a host key: the same key re-serialised is a different PEM but the same identity, and it is the identity a peer pins.
func SelfChecksum ¶ added in v0.2.0
SelfChecksum computes the SHA-256 checksum of the currently running binary.
func WireChecksum ¶ added in v0.4.0
WireChecksum re-encodes a hex digest from HashFile into the form the control plane's checksum fields carry: the 32 raw bytes in standard-padded base64.
Hex is this package's own currency — it is what the baseline store holds and what hook comparisons run on — but on the wire those fields are declared `format: byte`, so a hex string is decoded as base64 and yields 48 bytes instead of 32. The capability manifest refuses that outright, and while the heartbeat contract also documents a hex form, the deployed control plane answers it with 400 `binary_checksum_empty`. Base64 is the one encoding both operations accept, so it is the one this agent sends.
Types ¶
type CheckResult ¶
type CheckResult struct {
// Path is the filesystem path that was verified.
Path string
// Expected is the hex-encoded SHA-256 checksum that was expected.
Expected string
// Actual is the hex-encoded SHA-256 checksum that was computed.
Actual string
// OK is true when Expected matches Actual (or when establishing a new baseline).
OK bool
}
CheckResult holds the outcome of a file integrity check.
func VerifyFile ¶
func VerifyFile(path, expectedChecksum string, requireChecksum bool) (CheckResult, error)
VerifyFile computes the SHA-256 checksum of the file at path and compares it against expectedChecksum. When requireChecksum is true and expectedChecksum is empty, an error is returned (hooks must have a control-plane-provided checksum). When requireChecksum is false and expectedChecksum is empty, the computed checksum is returned as a new baseline with OK=true.
type Config ¶
type Config struct {
// Enabled controls whether integrity verification is active.
// Default: true (set by ApplyDefaults).
Enabled bool `yaml:"enabled"`
// BinaryPath is the path to the plexd binary to verify.
BinaryPath string `yaml:"binary_path"`
// HooksDir is the directory containing hook scripts to verify.
HooksDir string `yaml:"hooks_dir"`
// HostKeyPath is the SSH host key whose fingerprint is verified. It is
// derived from the agent's data dir rather than configured, so it is kept
// off the YAML surface.
HostKeyPath string `yaml:"-"`
// VerifyInterval is the interval between integrity verification runs.
// Must be at least 30s when enabled.
// Default: 5m
VerifyInterval time.Duration `yaml:"verify_interval"`
// WatchEnabled controls whether inotify file watching is active.
// When enabled, file changes in HooksDir trigger immediate checksum
// recomputation instead of waiting for the next periodic verification.
// Default: true (set by ApplyDefaults).
WatchEnabled bool `yaml:"watch_enabled"`
}
Config holds the configuration for integrity verification.
func (*Config) ApplyDefaults ¶
func (c *Config) ApplyDefaults()
ApplyDefaults sets default values for zero-valued fields. On a zero-valued Config, Enabled defaults to true. To disable integrity verification, set Enabled=false before or after calling ApplyDefaults.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store persists known-good checksums as a JSON file in the agent's data directory.
func NewStore ¶
NewStore creates a Store backed by dataDir/checksums.json. If the file does not exist, an empty store is created.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier orchestrates integrity verification for the plexd binary, the hook scripts, and the SSH host key.
func NewVerifier ¶
func NewVerifier(cfg Config, store *Store, reporter ViolationReporter, logger *slog.Logger) *Verifier
NewVerifier creates a Verifier with the given configuration, store, reporter, and logger.
func (*Verifier) BinaryChecksum ¶
BinaryChecksum returns the last computed binary checksum (thread-safe). Returns an empty string before any verification has run.
func (*Verifier) Run ¶
Run performs periodic integrity verification for the binary, the hooks directory, and the SSH host key. When WatchEnabled is true, it also monitors the hooks directory via inotify for real-time change detection. Run blocks until the context is cancelled.
func (*Verifier) VerifyBinary ¶
VerifyBinary computes the binary checksum, compares against the stored baseline, and reports a violation on mismatch. On first run (no baseline), the checksum is stored as the new baseline.
func (*Verifier) VerifyHook ¶
func (v *Verifier) VerifyHook(ctx context.Context, nodeID, hookPath, expectedChecksum string) (bool, error)
VerifyHook verifies a hook script against the expected checksum from the control plane. Returns true if the hook is safe to execute, false if there is a mismatch. An error is returned if the expected checksum is empty (hooks require a checksum).
func (*Verifier) VerifyHooksDir ¶
VerifyHooksDir computes checksums for all files in the hooks directory, compares against stored baselines, and reports violations on mismatch. Violations are attributed to the scanning detector; the fsnotify watcher enters the same sweep through verifyHooksDir with the inotify detector.
func (*Verifier) VerifyHostKey ¶ added in v0.4.0
VerifyHostKey computes the SSH host key's fingerprint, compares it against the stored baseline, and reports a violation on mismatch. On first run the fingerprint becomes the baseline, as for the binary.
A key that changes under a running agent is the tamper signal: the SSH server keeps serving the key it loaded at startup, so a divergence on disk means something replaced it. The fingerprint identifies the key, not the file — the same key re-serialised keeps its fingerprint, which is why this is not a checksum comparison. An unset HostKeyPath or an absent file is a no-op: a node that never started the tunnel has no key to watch.
type ViolationReporter ¶
type ViolationReporter interface {
ReportViolations(ctx context.Context, nodeID string, reports []api.IntegrityViolationReport) error
}
ViolationReporter abstracts control plane violation reporting for testability.
The contract's ingest endpoint takes a batch, so the interface does too: a directory sweep that finds three tampered hooks delivers them as one request rather than three.