Conversation
…ON-NFTBAN-AUTHORITY-001)
dns2 (2026-04-30) install evidence reported `inet ssh_safety` (operator-
retained kernel safety table) silently wiped during PR26.4 install.
Survey traced root cause to `cli/lib/nftban/cli/cmd_firewall.sh` step 4
of `_firewall_rebuild_core`, which used an allowlist-regex sweep:
ALLOWED_TABLES_PATTERN="^table (ip|ip6) (nftban|raw)$|^table inet (filter|nftban|nftban_install_emergency)$"
... if NOT matching ... nft delete table "$TABLE_SPEC" 2>/dev/null
Anything not matching was silently deleted. `inet ssh_safety` failed
the regex and was destroyed. The 2>/dev/null hid the only log signal.
The same shape existed in `helpers/autoheal.sh` section 9 with an
even tighter regex.
Fix (6A) — replace both with a 4-class table classifier:
cli/lib/nftban/core/nftban_table_classify.sh
NFTBAN_OWNED -> flush, no nft-delete
EXTERNAL_AUTHORITY_GHOST -> delete (CSF/firewalld iptables-nft compat)
KERNEL_DEFAULT -> preserve silently (ip raw, ip6 raw)
OPERATOR_SAFETY -> WARN to stderr, preserve, continue
Default policy is WARN-and-preserve. Strict refuse / explicit purge
modes are deferred to a future PR; PR26.6 must not change install
success semantics for normal hosts.
Lock-in (6B) — Go takeover behavior tests under switchop:
- csf binary RENAMED to /usr/sbin/csf.disabled with sha256 match
(reversible disarm; PR-26 amendment 2 already accepts this for
restore-to-CSF preflight). NOT a destructive deletion.
- /usr/sbin/lfd untouched (no mv, no rm) — mask-only contract.
- /etc/csf, /usr/local/csf, /var/lib/csf preserved.
- cron-backup manifest written before /etc/cron.d/{csf,lfd}-cron rm.
- Go ghost cleanup also preserves operator and kernel-default tables
(defense-in-depth, in case future refactors move logic there).
Fixture (6C) — cli/lib/nftban/tests/test_table_classifier.sh:
27 assertions covering all 4 classes including `inet ssh_safety`
survival. Pure shell test, runs without nft / root.
Lab proof on lab2 (Ubuntu 24.04, go1.22.2):
- bash test_table_classifier.sh: 27/27 PASS
- go test -run TestPR26_6_ ./internal/installer/switchop/...: 5/5 PASS
- go test ./internal/installer/...: all 17 installer packages PASS
- go vet ./internal/installer/switchop/...: clean
Out of scope (deferred):
- --strict-unknown-tables / --purge-unknown-tables flags
- cPanel/Plesk adapters (PR26.7 / PR26.8)
- source-install payload work (PR26.5 already merged)
- restore redesign
- destructive dns2 retry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
inet ssh_safetybecause_firewall_rebuild_corestep 4 silentlynft delete table'd anything outside its tight regex. New classifier (cli/lib/nftban/core/nftban_table_classify.sh) splits tables into NFTBAN_OWNED / EXTERNAL_AUTHORITY_GHOST / KERNEL_DEFAULT / OPERATOR_SAFETY and WARNs-and-preserves the last class.helpers/autoheal.shis rerouted through the same classifier.internal/installer/switchop/takeover_pr26_6_test.gopin current Go-side takeover behavior —/usr/sbin/csfis renamed tocsf.disabledwith byte-identical sha256 (reversible disarm, NOT destructive);/usr/sbin/lfduntouched;/etc/csf//usr/local/csf//var/lib/csfpreserved; cron-backup manifest written before cron rm; ghost-table cleanup never targets operator or kernel-default tables.cli/lib/nftban/tests/test_table_classifier.shwith 27 assertions covering all 4 classes includinginet ssh_safetysurvival.Invariant
Default policy is WARN-and-preserve.
--strict-unknown-tables/--purge-unknown-tablesmodes are deferred — PR26.6 does not change install-success semantics for normal hosts.Corrected narrative — CSF binary
The dns2 evidence reported "/usr/sbin/csf MISSING". Code survey of
internal/installer/switchop/takeover.go:117-125showed the actual operation ismv /usr/sbin/csf /usr/sbin/csf.disabled— a reversible rename. PR-26 amendment 2 (#520) already accepts the.disabledsibling for restore-to-CSF preflight. The dns2 path-absence was real but the binary content was preserved. The 6B tests now lock that contract.Test plan
bash cli/lib/nftban/tests/test_table_classifier.sh— 27/27 PASS on lab2go test -count=1 -v -run TestPR26_6_ ./internal/installer/switchop/...— 5/5 PASS on lab2go test -count=1 ./internal/installer/...— 17/17 packages PASS on lab2go vet ./internal/installer/switchop/...— cleanOut of scope
--strict-unknown-tables/--purge-unknown-tables/--refuse-on-unknown-tablesflags🤖 Generated with Claude Code