Description
The Linux kernel filters packets with netfilter. On Ubuntu, its rules are managed by one of these tools, each started at boot by its own service:
ufw, a simple front end, active whenufw statusshowsStatus: active;nftables, rules loaded from/etc/nftables.confbynftables.service;firewalld;iptables-persistent, rules restored bynetfilter-persistent.service.
This control requires exactly one of them to be enabled. Its default policy is covered by CB-UBU-NET-006.
ufw is installed by default on every Ubuntu release but left inactive (ENABLED=no in
/etc/ufw/ufw.conf); ufw.service is enabled anyway, which is why the check reads ufw status
rather than the unit state. Ubuntu documents ufw and the CIS Benchmarks favour it; nftables,
firewalld and iptables-persistent are accepted too. On 22.04 and later, iptables is the
iptables-nft variant, so ufw rules also show in nft list ruleset: that does not count as a
second firewall as long as nftables.service is not enabled. 20.04 uses the legacy iptables
back end. Docker and libvirt add their own rules beside the host firewall; they are not counted.
The fix reads the SSH ports from sshd -T; on 24.04 and later, where sshd is started by
ssh.socket, it first creates /run/sshd if no connection has started the service yet, and
stops without enabling ufw if it cannot find any port.
Rationale
A host firewall limits the services reachable from the network to the ones that are meant to be, even when a package starts a new listener or a service binds to all addresses by mistake. Two firewall tools flush or reorder each other's rules on reload, which leaves the host either wide open or cut off after a restart.
Impact
The automatic fix installs and enables ufw when no firewall is active. It allows the SSH ports
first, but ufw then drops every other incoming connection: services such as web or database
servers become unreachable until they are allowed with ufw allow <port>/<protocol>. It is
disabled by default; list the listening services with ss -tulpn and allow them before
enabling it.
Audit
ufw status(if installed):Status: activecounts as one.systemctl is-enabled nftables.service firewalld.service netfilter-persistent.service: eachenabledcounts as one. Exactly one must be counted.
Automated: every check must pass. Scanners report each check by its id.
single-firewall
Firewall tools enabled at boot.
script: |
active=
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then
active="$active ufw"
fi
for u in nftables firewalld netfilter-persistent; do
[ "$(systemctl is-enabled "$u.service" 2>/dev/null)" = enabled ] && active="$active $u"
done
echo "active firewall tools:${active:- none}"
set -- $active
[ "$#" -eq 1 ]
Remediation
- No firewall: ufw is installed by default (otherwise
apt install ufw). Allow SSH and the other services that must stay reachable, then enable it:
Or writeufw allow 22/tcp ufw enable/etc/nftables.conf(see CB-UBU-NET-006) and runsystemctl enable --now nftables. - Several tools: keep one, then disable the others, for example
ufw disable,systemctl disable --now nftablesorapt purge iptables-persistent.
| Restart | none |
| Disruption | high |
| Lockout risk | yes: can cut off administrative access. Off by default in generated scripts and playbooks, opt in through a tailoring file. |
Rollback
Run ufw disable. Installed packages can stay.
enable-ufw
# CB-UBU-NET-005: when no firewall is active, enable ufw (installed by default on Ubuntu), allowing SSH first.
# Does nothing when exactly one firewall tool is active; fails when several are (choice left to a human).
PATH="$PATH:/usr/sbin"
active=
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then
active="$active ufw"
fi
for u in nftables firewalld netfilter-persistent; do
[ "$(systemctl is-enabled "$u.service" 2>/dev/null)" = enabled ] && active="$active $u"
done
# shellcheck disable=SC2086 # word splitting is the point
set -- $active
[ "$#" -eq 1 ] && exit 0
if [ "$#" -gt 1 ]; then
echo "several firewall tools are enabled:$active; keep one by hand" >&2
exit 1
fi
# sshd -T needs /run/sshd, which only exists once ssh.service has run (ssh.socket on 24.04 and later).
if command -v sshd >/dev/null 2>&1; then
[ -d /run/sshd ] || mkdir -p -m 0755 /run/sshd
ports=$(sshd -T 2>/dev/null | awk '$1 == "port" { print $2 }')
if [ -z "$ports" ]; then
echo 'cannot read the SSH ports with sshd -T; ufw left disabled' >&2
exit 1
fi
fi
if ! command -v ufw >/dev/null 2>&1; then
DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=300 install -y -q ufw >/dev/null || exit 1
fi
for f in /etc/ufw/ufw.conf /etc/ufw/user.rules /etc/ufw/user6.rules; do cb_backup "$f"; done
# Allow every port sshd listens on before turning the firewall on.
for p in $ports; do
ufw allow "$p/tcp" comment 'SSH (CIRSEC Baselines)' >/dev/null || exit 1
done
ufw --force enable
Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.
References
- ufw(8), Ubuntu manual pages Canonical, vendor-doc, accessed 2026-10-19
- Ubuntu Server documentation - Firewalls Canonical, vendor-doc, accessed 2026-10-19
- Ubuntu Security documentation - Firewall Canonical, vendor-doc, accessed 2026-10-19
- NIST SP 800-41 Rev. 1 - Guidelines on Firewalls and Firewall Policy NIST, guide, accessed 2026-10-19
Mappings
| Framework | ID | Relation | Note |
|---|---|---|---|
| NIST SP 800-53 Rev. 55.1.1 | SC-7 | partial |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
Related controls
- related
CB-UBU-NET-006Drop incoming and forwarded traffic by default, with SSH allowed
People and lifecycle
| Authors |
|
| Verified | 2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic, BIOS boot. ufw installed but inactive on a fresh install, reported; fix allows port 22 and enables ufw; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot, other ports dropped. nftables alone passes; iptables-persistent next to ufw reported as a second firewall. |
| Verified | 2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic, BIOS boot. ufw installed but inactive on a fresh install, reported; fix allows port 22 and enables ufw; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot, other ports dropped; second remediate leaves it alone. |
| Verified | 2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic, BIOS boot. ufw inactive on a fresh install, reported; fix run before any SSH connection (no /run/sshd) still reads port 22 and enables ufw; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot, other ports dropped. |
| Verified | 2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic, BIOS boot. ufw inactive on a fresh install, reported; fix allows port 22 and enables ufw; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot. ufw plus nftables.service reported and the fix refuses; after iptables-persistent removed ufw, the fix reinstalled it with apt. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-19, 2026-10-19 |
| Translations | fr |