Documentation
¶
Index ¶
- Constants
- Variables
- func AuditRecordedCommands(cmds []string) []string
- func AuditWrittenFiles(paths []string) []string
- func DetectInstallOrigin(exec executor.Executor, log *logging.Logger) string
- func DetectPackageTarget(exec executor.Executor, origin string, log *logging.Logger) (string, error)
- func DetectVersions(exec executor.Executor, sourceDir, origin string, log *logging.Logger) (current, target string, err error)
- type ApplyForbiddenSubstring
- type ApplyForbiddenWritePath
- type Plan
- type PreflightCheck
- type PreflightResult
- type RecoveryPlan
Constants ¶
const ApplyForbiddenConfLocalSuffix = ".conf.local"
ApplyForbiddenConfLocalSuffix enforces G3-U5 — apply must never write to any *.conf.local file, period. Byte-preservation is tested by comparing pre/post contents; this constant makes the rule explicit so violations stand out in audit output.
const PlanSchemaVersion = "1.99.0"
PlanSchemaVersion is the current wire contract for Plan. Consumers should reject plans with unexpected schema versions.
Variables ¶
var ApplyForbiddenSubstrings = []ApplyForbiddenSubstring{
{"nft add table", "apply must never add tables directly — rebuild owns kernel mutation"},
{"nft add chain", "apply must never add chains directly — rebuild owns kernel mutation"},
{"nft add rule", "apply must never add rules directly — rebuild owns kernel mutation"},
{"nft flush", "apply must never flush — rebuild's atomic switch handles this"},
{"nft delete", "apply must never delete kernel objects — rebuild owns teardown"},
{"systemctl stop", "apply must not stop services — rebuild owns service lifecycle"},
{"systemctl start", "apply must not start services — rebuild owns service lifecycle"},
{"systemctl restart", "apply must not restart services — rebuild owns service lifecycle"},
{"systemctl reload", "apply must not reload services — rebuild owns service lifecycle"},
{"systemctl enable", "apply must not enable services — authority-taking path"},
{"systemctl disable", "apply must not disable services — authority-taking path"},
{"systemctl mask", "apply must not mask services — authority-taking path"},
{"systemctl unmask", "apply must not unmask services — authority-taking path"},
{"apt-get remove", "apply must not remove packages — authority-taking path"},
{"dnf remove", "apply must not remove packages — authority-taking path"},
{"ufw disable", "apply must not touch external firewalls (INV-U-003)"},
{"iptables -F", "apply must not flush iptables (INV-U-003)"},
}
ApplyForbiddenSubstrings enumerates forbidden command patterns.
var ApplyForbiddenWritePaths = []ApplyForbiddenWritePath{
{"/etc/nftban/", "apply must not mutate config — rebuild's render pipeline owns /etc/nftban"},
{"/usr/lib/nftban/", "apply must not mutate payload — PR-14 payload stager owns /usr/lib/nftban"},
{"/usr/sbin/nftban", "apply must not replace binaries — payload stager owns /usr/sbin/nftban"},
}
ApplyForbiddenWritePaths enumerates forbidden write destinations.
var ApplyWhitelist = map[string]bool{ "sh -c command -v nft >/dev/null 2>&1": true, "rpm -q nftban-core": true, "rpm -q nftban": true, "dpkg -s nftban-core": true, "dpkg -s nftban": true, "rpm -q --queryformat %{VERSION} nftban-core": true, "rpm -q --queryformat %{VERSION} nftban": true, "nftban firewall rebuild": true, "/usr/lib/nftban/bin/nftban-validate --json": true, "nft list table ip nftban": true, "nft list table ip6 nftban": true, "systemctl is-active nftband.service": true, "systemctl is-active nftables": true, "nft list chain ip nftban input": true, }
ApplyWhitelist is the complete set of commands runUpdateApply is allowed to invoke. Anything outside this list appearing in a MockExecutor trace is a contract violation. Adding to it is a conscious contract change and requires a corresponding line in apply_contract.md.
var PackageNames = []string{"nftban-core", "nftban"}
PackageNames is the canonical ordered list of package identifiers to probe. Modern packages ship as "nftban-core"; pre-v1.73 packages shipped as "nftban". We try both for ownership probes so legacy hosts still classify correctly. This mirrors cli/lib/nftban/cli/cmd_update_detection.sh:: _detect_install_type so the Go and shell paths report the same origin.
Functions ¶
func AuditRecordedCommands ¶
AuditRecordedCommands runs the full command-trace audit against a whitespace-flattened list of "name arg1 arg2 ..." strings. Used by contract tests in this package AND the main-package runUpdateApply tests so the exact same rules apply to both surfaces.
Returns a list of violation messages (empty slice = clean).
func AuditWrittenFiles ¶
AuditWrittenFiles runs the file-system side of the audit.
func DetectInstallOrigin ¶
DetectInstallOrigin returns "rpm", "deb", or "source" when it can infer the host's install origin, or "" when it cannot. Probing order:
- rpm -q <pkg> for each name in PackageNames — if any exits 0, origin is "rpm"
- dpkg -s <pkg> for each name in PackageNames — if any exits 0, origin is "deb"
- NFTBAN_SOURCE_DIR env var set — origin is "source"
- otherwise — "" (unknown)
The probing is ORDER-SENSITIVE by design: package managers coexist on some systems (e.g. alien, conversion tooling), so we prefer rpm if it reports ownership. This is the Go mirror of the shell _detect_install_type helper; both must stay in sync until PR-21 removes the shell path.
func DetectPackageTarget ¶
func DetectPackageTarget(exec executor.Executor, origin string, log *logging.Logger) (string, error)
DetectPackageTarget queries the package database for the version of nftban-core that would be installed if an upgrade ran. For a real upgrade scenario this equals the version on disk in the package DB, which is ALSO the running version when the package is in sync.
Returns ("", nil) when the package manager is present but the package is not owned. Returns ("", err) only on plumbing failures (unlikely — we explicitly tolerate non-zero exits as "not owned").
PR-17 scope: this detection is advisory only. Apply work (PR-18) will use the SAME query to drive the rebuild-pipeline input delta — per INV-U-001 there is no separate package-specific apply path.
func DetectVersions ¶
func DetectVersions(exec executor.Executor, sourceDir, origin string, log *logging.Logger) (current, target string, err error)
DetectVersions reads the current installed version from the FHS VERSION file and the target version from:
- <sourceDir>/VERSION when sourceDir is non-empty (source installs)
- the package manager (rpm -q / dpkg -s) when origin is "rpm"/"deb" and sourceDir is empty (PR-17 scope)
Missing target is non-fatal — the plan carries a warning downstream. Missing current IS fatal — we must know where we are before planning any transition (INV-U-002 rollbackability).
Types ¶
type ApplyForbiddenSubstring ¶
ApplyForbiddenSubstring names a class of calls that must never appear anywhere in the recorded command trace, regardless of exact args.
type ApplyForbiddenWritePath ¶
ApplyForbiddenWritePath names a destination prefix that apply must never open in write mode.
type Plan ¶
type Plan struct {
// SchemaVersion locks the on-wire contract. Bump on any breaking change.
SchemaVersion string `json:"schema_version"`
// CurrentVersion is the version currently installed on the host,
// read from /usr/lib/nftban/VERSION (or the canonical lookup chain
// documented in cli/lib/nftban/lib/version.sh).
CurrentVersion string `json:"current_version"`
// TargetVersion is the version that would be installed if apply ran.
// For source installs this is the VERSION file in the source tree;
// for package installs this is the package's Version field.
TargetVersion string `json:"target_version"`
// InstallOrigin is "rpm", "deb", or "source" — determines which apply
// path would execute in PR-18.
InstallOrigin string `json:"install_origin"`
// NeedsUpdate is true when CurrentVersion != TargetVersion. False means
// a run would be a bounded no-op per G3-U16 (idempotency).
NeedsUpdate bool `json:"needs_update"`
// Preflight records the preflight result that was folded into this plan.
Preflight *PreflightResult `json:"preflight"`
// Warnings are non-fatal notes the operator should see (e.g. "current
// state is DEGRADED — update will proceed with caution").
Warnings []string `json:"warnings,omitempty"`
// Actions is a human-oriented list of what apply would do. PR-16 keeps
// this abstract ("reuse rebuild pipeline"); PR-18 fills in concrete
// steps.
Actions []string `json:"actions,omitempty"`
// Recovery carries planning-only metadata about the rollback route that
// PR-18's apply step will rely on. PR-17 populates this but does NOT
// execute any recovery — that's PR-18 scope per INV-U-002.
Recovery *RecoveryPlan `json:"recovery,omitempty"`
}
Plan describes the intended update transition. Produced by `nftban-installer --mode=upgrade --dry-run`; consumed by the shell update dispatcher for lifecycle recording and by CI for the G3-U2/U3 assertions.
PR-16 scope: Plan is purely descriptive. Apply logic lands in PR-18.
func BuildPlan ¶
func BuildPlan(pre *PreflightResult, current, target, origin string) *Plan
BuildPlan assembles a Plan from a preflight result + detected versions. It is the single constructor for Plan so that SchemaVersion and default fields are always set consistently.
func (*Plan) AttachRecovery ¶
func (p *Plan) AttachRecovery(r *RecoveryPlan)
AttachRecovery decorates the plan with the PR-17 recovery metadata. Separate from BuildPlan so callers without an exec handle (tests) can still build a plan without synthesizing recovery state.
func (*Plan) WriteJSONToFile ¶
WriteJSONToFile writes the plan as JSON to dst. Caller is responsible for destination path safety. Intended for history/audit records, NOT for operator-visible files.
type PreflightCheck ¶
type PreflightCheck struct {
Name string `json:"name"`
Passed bool `json:"passed"`
Detail string `json:"detail,omitempty"`
// Severity is "critical" (abort) or "warning" (continue with warning).
// Only "critical" failures contribute to Passed=false at the result level.
Severity string `json:"severity"`
}
PreflightCheck is one named precondition.
type PreflightResult ¶
type PreflightResult struct {
Passed bool `json:"passed"`
Checks []PreflightCheck `json:"checks"`
}
PreflightResult is the aggregate outcome.
func Preflight ¶
Preflight runs all update preconditions and returns an aggregate result. Called by phaseDetect when cfg.mode == "upgrade".
The function is READ-ONLY. It must never mutate host state.
origin is the operator-declared install origin ("rpm", "deb", "source", or ""). PR-17 uses it for the new P-7 coherence check.
type RecoveryPlan ¶
type RecoveryPlan struct {
// Available reports whether a rollback to the prior known-good state
// is possible without operator intervention.
Available bool `json:"available"`
// Mechanism is the rollback mechanism that would be used — currently
// "rebuild" (validate-in-namespace + flush+load + v1.96 recovery), the
// only canonized recovery surface today.
Mechanism string `json:"mechanism"`
// Notes carries human-oriented context that helps an operator reason
// about recovery without digging into code (e.g. "prior state file
// present and terminal", "no prior install_state — fresh recovery").
Notes []string `json:"notes,omitempty"`
}
RecoveryPlan is PR-17 metadata describing the rollback path apply will follow on failure. It is DESCRIPTIVE only; no state mutation in PR-17.
func BuildRecoveryPlan ¶
func BuildRecoveryPlan(exec executor.Executor) *RecoveryPlan
BuildRecoveryPlan derives the PR-17 recovery metadata from exec state. Planning-only: no mutation, no recovery execution.