feat(firewall): drive firewalld on Alma/RHEL with full CLI parity

A fresh Alma box has firewalld active, and the iptables-based harden-firewall.sh
refused to run there (caught by harden-ssh's '|| warn', so the host firewall was
silently skipped). Use firewalld natively on the rhel family instead of fighting it.

- harden-firewall.sh: family-aware backend. On rhel, apply/allow/deny/list/disable
  drive firewall-cmd (deny-by-default zone, SSH + registered ports, ping policy,
  source-restricted rich rules); Alpine/Debian keep the iptables engine unchanged.
  FW_BACKEND=iptables|firewalld overrides.
- oslib: install_firewalld(); sshguard_backend() prefers sshg-fw-firewalld on rhel
  so brute-force blocks land in firewalld (no INPUT->sshguard jump needed).
- Deployments already fall through to a firewall-cmd branch when the iptables
  engine is absent, so they need no changes.
- README + script header document the per-family backend.

harden-ssh / harden-jumphost are unchanged -- they call harden-firewall.sh apply
and read sshguard_backend(), so the switch happens underneath them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 17:16:24 -05:00
co-authored by Claude Opus 4.8
parent c3e2e9c52b
commit fe25f35305
3 changed files with 180 additions and 31 deletions
+26 -1
View File
@@ -481,6 +481,17 @@ fw_enable_restore() {
esac
}
# Install + enable firewalld (the native RHEL/Alma firewall). harden-firewall.sh
# uses this on the rhel family instead of raw iptables; sshguard then blocks via
# the sshguard-firewalld backend (no INPUT->sshguard jump, no boot hook).
install_firewalld() {
_require_detected
command -v firewall-cmd >/dev/null 2>&1 || pkg_install firewalld
svc_enable firewalld
# Must be running before we push --permanent rules and --reload.
firewall-cmd --state >/dev/null 2>&1 || svc_start firewalld || true
}
# ============================================================================
# gum (Charm TUI) -- multi-OS installer. Best-effort; callers that can fall
# back to CLI prompts should not treat failure as fatal.
@@ -585,9 +596,23 @@ CONF
return 1
}
# Locate the sshguard iptables backend binary (path varies by packaging).
# Locate the sshguard firewall-backend binary (path varies by packaging).
# On RHEL/Alma we run firewalld, so prefer sshguard's firewalld backend
# (from the sshguard-firewalld package) -- blocks land in firewalld and there
# is no iptables INPUT->sshguard jump to maintain.
sshguard_backend() {
_require_detected
local c
if [[ "${OS_FAMILY:-}" == rhel ]]; then
for c in /usr/libexec/sshguard/sshg-fw-firewalld \
/usr/lib/sshguard/sshg-fw-firewalld; do
[[ -x "$c" ]] && { echo "$c"; return; }
done
# firewalld backend expected but not found yet; name it anyway so
# sshguard.conf points at the right binary once the package is in.
echo /usr/libexec/sshguard/sshg-fw-firewalld
return
fi
for c in /usr/libexec/sshguard/sshg-fw-iptables \
/usr/libexec/sshg-fw-iptables \
/usr/lib/sshguard/sshg-fw-iptables \