update

package
v1.111.0 Latest Latest
Warning

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

Go to latest
Published: May 12, 2026 License: MPL-2.0 Imports: 9 Imported by: 0

Documentation

Index

Constants

View Source
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.

View Source
const PlanSchemaVersion = "1.99.0"

PlanSchemaVersion is the current wire contract for Plan. Consumers should reject plans with unexpected schema versions.

Variables

View Source
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.

View Source
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.

View Source
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.

View Source
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

func AuditRecordedCommands(cmds []string) []string

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

func AuditWrittenFiles(paths []string) []string

AuditWrittenFiles runs the file-system side of the audit.

func DetectInstallOrigin

func DetectInstallOrigin(exec executor.Executor, log *logging.Logger) string

DetectInstallOrigin returns "rpm", "deb", or "source" when it can infer the host's install origin, or "" when it cannot. Probing order:

  1. rpm -q <pkg> for each name in PackageNames — if any exits 0, origin is "rpm"
  2. dpkg -s <pkg> for each name in PackageNames — if any exits 0, origin is "deb"
  3. NFTBAN_SOURCE_DIR env var set — origin is "source"
  4. 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

type ApplyForbiddenSubstring struct {
	Pattern string
	Why     string
}

ApplyForbiddenSubstring names a class of calls that must never appear anywhere in the recorded command trace, regardless of exact args.

type ApplyForbiddenWritePath

type ApplyForbiddenWritePath struct {
	Prefix string
	Why    string
}

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) JSON

func (p *Plan) JSON() ([]byte, error)

JSON returns the plan as pretty-printed JSON. Used by --dry-run --json.

func (*Plan) Render

func (p *Plan) Render(w io.Writer)

Render prints a human-readable plan. Used by --dry-run text mode.

func (*Plan) WriteJSONToFile

func (p *Plan) WriteJSONToFile(dst string) error

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

func Preflight(exec executor.Executor, log *logging.Logger, origin string) *PreflightResult

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.

Jump to

Keyboard shortcuts

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