Hardening a Raspberry Pi 5 for Server-Grade Use
A phased, real-world hardening guide for transforming a default Raspberry Pi 5 installation into a minimal, secure, and server-grade Linux node. Covers system updates, SSH hardening, firewalling, service reduction, kernel tuning, hardware disablement, and verification.
This entry documents a disciplined, server-grade hardening process for a Raspberry Pi 5 running Debian. The goal is to transform a default installation into a minimal, secure, and predictable Linux node suitable for infrastructure workloads.
The steps below were applied to a real Raspberry Pi 5 (cassini-pi) and are structured as incremental phases. Each phase tightens the system posture while keeping the setup understandable, reversible, and maintainable.
đ§Phase 0: Baseline & Assumptions
- Raspberry Pi 5 running Debian GNU/Linux 13 (trixie)
- 64-bit (
aarch64) kernel - Static IP and proper LAN DNS in place
- SSH access available using an existing
ed25519keypair - No desktop environment; headless server usage
âŦī¸Phase 1: System Update & Firmware
The very first step on a new Raspberry Pi should always be to bring the base system fully up to date. This ensures the latest security patches, kernel fixes, and Raspberry Pi-specific firmware updates are applied before any hardening or service configuration begins.
# Refresh package metadata
sudo apt update
# Perform a full system upgrade (handles kernel & dependency changes)
sudo apt full-upgrade -y
# Reboot to apply new kernel / firmware
sudo rebootA full-upgrade is preferred over a standard upgrade on Raspberry Pi systems, as it safely handles kernel transitions, firmware packages, and dependency changes that are common on ARM platforms.
After rebooting, verify that the system is running the expected kernel and OS release:
# Verify kernel version
uname -a
# Verify OS release
cat /etc/os-releasecassini-pi, this resulted in a clean Debian GNU/Linux 13 (trixie) installation running a recent Raspberry Pi-optimized 6.x kernel, with no pending upgrades.At this stage, the system is considered baseline-clean: fully patched, rebooted, and ready for security hardening without carrying legacy cruft or outdated firmware.
đPhase 2: SSH Hardening
Secure Shell (SSH) is typically the primary â and often only â remote access vector into a headless Raspberry Pi. Hardening SSH early is therefore critical, as it dramatically reduces the attack surface and eliminates entire classes of brute-force and credential-based attacks.
The guiding principles applied here are simple:
- Key-based authentication only â passwords are disabled entirely.
- Non-standard SSH port â reduces automated scanning and log noise.
- No root login â privilege escalation must be explicit and auditable.
- Minimal SSH feature set â forwarding and tunneling disabled unless explicitly required.
SSH was reconfigured to listen on a custom port and to accept public key authentication only. The relevant excerpts from/etc/ssh/sshd_config are shown below:
# Custom SSH port
Port 9110
Protocol 2
# Authentication hardening
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
# Reduce attack surface
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
# Connection limits
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2After updating the configuration, the SSH daemon was reloaded in-place to avoid disconnecting active sessions:
sudo systemctl reload sshAt the end of this phase, SSH access is limited to explicitly authorized clients using cryptographic keys, with no password-based fallback and no exposed legacy features. This forms the foundation for the firewall and intrusion-prevention steps that follow.
đĨPhase 3: Firewall (UFW)
With SSH access hardened, the next step is to enforce a strict network boundary around the system. A host-based firewall ensures that only explicitly approved traffic is allowed to reach the node, even if additional services are installed in the future.
The firewall strategy applied here follows a deny-by-default model:
- All inbound traffic is blocked unless explicitly allowed.
- Outbound traffic is permitted to allow normal package updates, DNS, and time synchronization.
- Only the hardened SSH port is exposed to the network.
Uncomplicated Firewall (ufw) was selected for its clarity, predictability, and tight integration with Debian-based systems.
# Install UFW
sudo apt install -y ufw
# Reset to a known-clean state
sudo ufw reset
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH on the hardened port
sudo ufw allow 9110/tcp comment 'SSH'
# Enable the firewall
sudo ufw enableOnce enabled, the firewall state can be verified using:
sudo ufw status verbose9110). All other inbound connections are silently dropped by default.This firewall configuration ensures that even if new services are installed later, they remain inaccessible from the network until an explicit firewall rule is added. The node is now protected against accidental exposure as well as opportunistic scanning.
đ§šPhase 4: Service Surface Reduction
A freshly installed Linux system often includes background services intended for convenience, discovery, or desktop usage. While useful on consumer systems, these services increase the attack surface and can expose network listeners that are unnecessary on a headless server.
The goal of this phase is to ensure that only services explicitly required for the node's role are running â and more importantly, listening on the network.
The following services were identified and removed:
- Avahi (mDNS) â local network service discovery (
.localhostnames). - CUPS â printing subsystem and IPP listener (port
631). - rpcbind â legacy RPC service mapper (port
111), commonly associated with NFS and older network services.
Avahi requires special attention, as it can be activated automatically via systemd sockets even if the main service is disabled. Both the service and its socket unit were therefore explicitly disabled:
sudo systemctl disable --now avahi-daemon.service
sudo systemctl disable --now avahi-daemon.socketPrinting and RPC services were fully stopped and removed from the system:
# Disable and remove CUPS
sudo systemctl disable --now cups
sudo systemctl disable --now cups-browsed
sudo apt purge -y cups cups-browsed
# Disable and remove rpcbind
sudo systemctl disable --now rpcbind
sudo systemctl disable --now rpcbind.socket
sudo apt purge -y rpcbind
# Remove orphaned dependencies
sudo apt autoremove --purge -yAfter these changes, all unnecessary TCP listeners were eliminated. Any remaining open sockets are either outbound-only or required for core system functionality.
đ§ŦPhase 5: Network & Kernel Hardening
Beyond service-level hardening, Linux provides a number of kernel and network tuning parameters that can significantly improve resilience against common network-based attacks. These settings do not affect normal operation but reduce the system's exposure to spoofing, misrouting, and denial-of-service patterns.
The following adjustments focus on hardening IPv4 and IPv6 networking behaviour at the kernel level, using sysctl.
A dedicated configuration file was created to ensure the settings are explicit, auditable, and persist across reboots:
# Enable reverse path filtering (anti-spoofing)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
# Enable SYN cookies (basic DoS protection)
net.ipv4.tcp_syncookies = 1Once written, the configuration was applied immediately without requiring a reboot:
sudo sysctl --systemThese settings collectively enforce sane defaults for packet routing, eliminate legacy behaviours that are rarely needed on modern systems, and improve observability of suspicious traffic patterns.
đĄPhase 6: Radios & Hardware Disablement
Raspberry Pi devices ship with integrated wireless radios to support a wide range of use cases. On a server-class, wired-only node, these radios are not only unnecessary but represent additional firmware, drivers, and potential attack surface.
Since this system is intended to operate exclusively over Ethernet, both Wi-Fi and Bluetooth were permanently disabled at boot time.
Disabling radios at the firmware level ensures they never initialize, consume power, or expose kernel interfaces â even transiently during boot.
# Disable onboard Wi-Fi
dtoverlay=disable-wifi
# Disable onboard Bluetooth
dtoverlay=disable-btAfter applying these changes, the system was rebooted to ensure the overlays took effect:
sudo rebootOnce rebooted, wireless interfaces no longer appeared inip link or rfkill output, confirming that the radios were fully disabled at the hardware abstraction layer.
đĄī¸Phase 7: Intrusion Prevention (Fail2Ban)
With SSH hardened, firewalled, and running on a non-standard port, the remaining attack vector is persistent brute-force or automated probing. While key-only authentication eliminates the risk of successful credential compromise, these attacks still consume resources and generate log noise.
fail2ban provides an adaptive defense layer by monitoring authentication logs and temporarily banning IP addresses that exhibit malicious patterns. This reduces attack persistence and makes the system less attractive to automated scanners.
Installation is straightforward:
# Install fail2ban
sudo apt install -y fail2ban
# Enable and start the service
sudo systemctl enable fail2ban
sudo systemctl start fail2banAfter installation, fail2ban requires configuration to monitor the custom SSH port. The default configuration only watches port 22, so a local override must be created.
A dedicated jail configuration was written to/etc/fail2ban/jail.d/sshd.local to specify the hardened SSH port:
[sshd]
enabled = true
port = 9110
logpath = /var/log/auth.log
maxretry = 5
bantime = 3600
findtime = 600This configuration enables the SSH jail with the following policies:
- Port 9110 - matches the hardened SSH port.
- maxretry = 5 - allows up to 5 failed authentication attempts.
- bantime = 3600 - bans the offending IP for 1 hour.
- findtime = 600 - detection window of 10 minutes.
After creating the configuration, the service was reloaded to apply the changes:
sudo systemctl reload fail2banThe SSH jail status can be verified using:
sudo fail2ban-client status sshdfail2ban integrates directly with UFW, automatically adding temporary firewall rules to block offending IPs. This approach is transparent and requires no additional firewall management.At the conclusion of this phase, the system is protected not just by passive hardening, but by active monitoring and automated response to malicious behavior. This makes the node more resilient to both opportunistic scanning and targeted attacks.
đPhase 8: Verification & Audit
Hardening work is only meaningful if the resulting system state can be verified. This phase focuses on validating that all previous changes produced the intended effect and that no unintended services or network listeners remain exposed.
The primary verification step is to inspect active network listeners usingss, which provides an accurate, kernel-level view of open sockets:
ss -tulpnOn cassini-pi, the output confirmed that the only listening TCP sockets were for SSH on the hardened port:
tcp LISTEN 0 128 0.0.0.0:9110 0.0.0.0:*
tcp LISTEN 0 128 [::]:9110 [::]:*Any remaining UDP sockets observed at this stage were ephemeral or outbound in nature (e.g. DNS or NTP) and were not reachable from the network due to the firewall's default deny policy.
Firewall status was also verified to ensure only the intended rules were active:
sudo ufw status verboseIntrusion prevention status was validated by verifying the SSH jail configuration applied in Phase 7:
sudo fail2ban-client status sshd9110, protected by key-based authentication, firewall rules, and intrusion prevention.This verification step serves as both a confidence check and a future audit baseline. Re-running these commands after system changes provides a fast way to detect configuration drift or accidental exposure.
â Final State & Takeaways
At the conclusion of this hardening process, the Raspberry Pi 5 operates as a disciplined, server-grade Linux node rather than a general-purpose or consumer-oriented device. Every exposed capability is intentional, minimal, and verifiable.
The resulting system state can be summarized as follows:
- Fully updated Debian GNU/Linux 13 system running a current Raspberry Pi-optimized kernel.
- SSH hardened with key-only authentication, a non-standard port, and reduced protocol surface.
- Host-based firewall enforcing a deny-by-default inbound policy with a single explicit exception.
- All unnecessary services removed, including discovery, printing, and legacy RPC components.
- Kernel and network-level protections applied via persistent
sysctlconfiguration. - Unused wireless radios disabled at the firmware level.
- Clean, auditable verification confirming a single intentional network ingress point.
This approach emphasizes intentional minimalism: reducing complexity not by layering tools, but by removing what is unnecessary and validating what remains.
The same phased methodology can be reused across additional Raspberry Pi nodes, ARM-based servers, or small virtual machines to establish a consistent and predictable security baseline before deploying workloads such as Docker, Kubernetes, or custom services.
Changelog
Added Phase 7 (Intrusion Prevention with Fail2Ban) to address missing fail2ban installation and configuration. Renumbered verification phase to Phase 8.
Initial release documenting complete hardening process for Raspberry Pi 5 running Debian.
Filed under: Raspberry Pi, Debian, Linux Hardening, SSH, UFW, Fail2Ban, Infrastructure