CIS Hardening of a Debian Linux Server Part 4: Level 1 Host Based Firewall

This is the fifth entry in the series documenting the Debian 13 server CIS hardening project.

This entry covers sections 4.1.1 through 4.1.5 of the benchmark. This is a Level 1 pass. Level 2 recommendations are deferred.

4.1: Configure Uncomplicated Firewall #

This section follows the benchmark’s approach of configuring the firewall through the UFW front end only. Per the benchmark’s own note, using another configuration method alongside UFW could produce unexpected results.

Since the workflow here involves connecting to the server over SSH, the benchmark’s warning about losing access when configuring a live firewall remotely is directly relevant. Special attention is given throughout this section to preserving the connection and verifying the configuration before it’s applied.

4.1.1: Ensure UFW Is Installed #

# dpkg-query -s ufw &>/dev/null && echo "ufw is installed"

No output, so ufw is not installed.

# apt install ufw
Installing:                     
  ufw

Suggested packages:
  rsyslog

Summary:
  Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 0
  Download size: 169 kB
  Space needed: 880 kB / 36.8 GB available

Get:1 http://deb.debian.org/debian trixie/main amd64 ufw all 0.36.2-9 [169 kB]
Fetched 169 kB in 0s (5,523 kB/s)
Preconfiguring packages ...
Selecting previously unselected package ufw.
(Reading database ... 39213 files and directories currently installed.)
Preparing to unpack .../archives/ufw_0.36.2-9_all.deb ...
Unpacking ufw (0.36.2-9) ...
Setting up ufw (0.36.2-9) ...
Creating config file /etc/ufw/before.rules with new version
Creating config file /etc/ufw/before6.rules with new version
Creating config file /etc/ufw/after.rules with new version
Creating config file /etc/ufw/after6.rules with new version
Created symlink '/etc/systemd/system/multi-user.target.wants/ufw.service' → '/usr/lib/systemd/system/ufw.service'.
Processing triggers for man-db (2.13.1-1) ...
# dpkg-query -s ufw &>/dev/null && echo "ufw is installed"
ufw is installed

4.1.2: Ensure UFW Service Is Configured #

If no rule exists for the service used to administer the host, in this case OpenSSH, enabling the firewall risks cutting off access entirely. The benchmark’s example rule for this is:

# ufw allow proto tcp from any to any port 22

This opens SSH from any location, which is a starting point rather than an end state; the benchmark recommends scoping this down to specific hosts per local policy.

The active listening ports are checked first:

# ss -tulnp
Netid    State     Recv-Q    Send-Q                          Local Address:Port        Peer Address:Port    Process                             
udp      UNCONN    0         0                                  <REDACTED>:68               0.0.0.0:*        users:(("dhcpcd",pid=665,fd=3))    
udp      UNCONN    0         0                                [<REDACTED>]:546                 [::]:*        users:(("dhcpcd",pid=770,fd=3))    
udp      UNCONN    0         0                           [<REDACTED>]%eth0:546                 [::]:*        users:(("dhcpcd",pid=640,fd=3))    
tcp      LISTEN    0         128                                   0.0.0.0:22               0.0.0.0:*        users:(("sshd",pid=906,fd=6))      
tcp      LISTEN    0         128                                      [::]:22                  [::]:*        users:(("sshd",pid=906,fd=7))

This confirms the OpenSSH service is listening on port 22. The allow rule is added to preserve the connection once UFW is active:

# ufw allow proto tcp from any to any port 22
Rules updated
Rules updated (v6)

The benchmark’s recommendation to restrict this rule to specific source IPs is not practical here, since the administering location can change frequently.

# systemctl is-enabled ufw.service
enabled
# systemctl is-active ufw.service
inactive
# ufw status
Status: inactive

ufw is enabled at the service level but not yet active. The pending rule set is previewed before activation:

# ufw show added
Added user rules (see 'ufw status' for running firewall):
ufw allow 22/tcp

With the rule confirmed, the firewall is unmasked, enabled, and started:

# systemctl unmask ufw.service
# systemctl --now enable ufw.service
Synchronizing state of ufw.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable ufw
# ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
# ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)

4.1.3: Ensure UFW Incoming Default Is Configured #

A default allow policy means anything not explicitly denied gets through, which inverts the usual security posture; it is simpler and generally more secure to allow only the required services than to attempt to deny every unwanted one. A deny policy also behaves differently from a reject policy in terms of feedback: deny silently drops packets, leading to a connection timeout, while reject sends back an explicit refusal.

# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

The incoming default is already set to deny, so this recommendation passes without further changes.

4.1.4: Ensure UFW Outgoing Default Is Configured #

Level 2, deferred.

4.1.5: Ensure UFW Routed Default Is Configured #

The earlier ufw status verbose output already shows the routed default as disabled, so this recommendation passes as configured.

With this section complete, a Lynis rerun was performed:

================================================================================

  Lynis security scan details:

  Scan mode:
  Normal [▆]  Forensics [ ]  Integration [ ]  Pentest [ ]

  Lynis modules:
  - Compliance status      [?]
  - Security audit         [V]
  - Vulnerability scan     [V]

  Details:
  Hardening index : 69 [#############       ]
  Tests performed : 274
  Plugins enabled : 2

  Software components:
  - Firewall               [V]
  - Intrusion software     [X]
  - Malware scanner        [X]

  Files:
  - Test and debug information      : /var/log/lynis.log
  - Report data                     : /var/log/lynis-report.dat

================================================================================

The hardening index holds at 69, unchanged from the previous audit.