Hardening an SBC for the Internet in 2026: SSH Keys, Firewall Defaults, Fail2ban, and Safer Updates

Server hardening checklist with SSH key setup, firewall rules, and fail2ban on an SBC terminal

Threat Model for an Internet-Facing SBC

The moment you forward a port or assign a public IP to a Banana Pi, automated scanners find it within minutes. They try default credentials, known CVEs, and brute-force SSH. An unhardened SBC running Debian or Armbian with password auth enabled will be compromised within hours — not days.

This guide covers the minimum viable hardening: lock down SSH, block everything you aren't using, auto-ban attackers, and keep packages patched. It assumes you already have a working Debian 13 or Armbian installation (Debian 13 guide or Armbian guide).

Do this before exposing the board to the internet. Every step here should be completed while the SBC is behind NAT or on a private network. Open ports only after hardening is verified.

SSH Key-Only Authentication

Generate a key pair on your local machine (not the SBC):

# On your workstation — Ed25519 is preferred over RSA for new keys
ssh-keygen -t ed25519 -C "banana-pi-admin" -f ~/.ssh/bpi_ed25519

# Copy the public key to the SBC (while password auth still works)
ssh-copy-id -i ~/.ssh/bpi_ed25519.pub user@192.168.1.50

# Test key-based login before disabling passwords
ssh -i ~/.ssh/bpi_ed25519 user@192.168.1.50
Test the key login before the next step. If you disable password auth without a working key, you lock yourself out. Recovery requires pulling the SD card and editing sshd_config from another machine.

Disable Root Login and Password Auth

Edit the SSH daemon configuration:

sudo nano /etc/ssh/sshd_config

Set these directives (or add them if missing):

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
MaxAuthTries 3
LoginGraceTime 30
AllowUsers your-username

Restart and verify:

sudo sshd -t          # Syntax check — fix errors before restarting
sudo systemctl restart sshd

# From another terminal, verify password login is rejected:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@192.168.1.50
# Should fail with: Permission denied (publickey).

UFW Firewall: Default Deny + Allow Rules

sudo apt install ufw

# Default policy: deny all incoming, allow all outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (adjust port if you moved it)
sudo ufw allow 22/tcp comment 'SSH'

# Allow other services as needed — examples:
# sudo ufw allow 80/tcp comment 'HTTP'
# sudo ufw allow 443/tcp comment 'HTTPS'
# sudo ufw allow 51820/udp comment 'WireGuard'

# Enable the firewall
sudo ufw enable

# Verify rules
sudo ufw status verbose
Tip: Rate-limit SSH connections to slow brute-force attempts at the firewall level: sudo ufw limit 22/tcp. This allows 6 connections per 30 seconds from one IP before blocking.

Fail2ban for SSH Brute Force

sudo apt install fail2ban

# Create a local override (never edit jail.conf directly — it gets overwritten on upgrade)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local and set these under [sshd]:

[sshd]
enabled  = true
port     = ssh
filter   = sshd
backend  = systemd
maxretry = 3
findtime = 600
bantime  = 3600

This bans an IP for 1 hour after 3 failed attempts within 10 minutes. Start and verify:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check jail status
sudo fail2ban-client status sshd

# After some time, check banned IPs:
sudo fail2ban-client status sshd | grep "Banned IP"
On Armbian: fail2ban uses the systemd backend by default on Trixie. If you see "no logs" errors, confirm backend = systemd is set, not auto.

Automatic Security Updates

sudo apt install unattended-upgrades apt-listchanges

# Enable automatic security updates
sudo dpkg-reconfigure -plow unattended-upgrades
# Select "Yes" when prompted

Verify the configuration in /etc/apt/apt.conf.d/50unattended-upgrades:

# These lines should be uncommented:
Unattended-Upgrade::Origins-Pattern {
    "origin=Debian,codename=${distro_codename}-security,label=Debian-Security";
};

# Optional: auto-reboot at 3 AM if a kernel update requires it
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";

Test the configuration with a dry run:

sudo unattended-upgrades --dry-run --debug 2>&1 | tail -20
Kernel upgrades on SBCs can break boot. If you enable auto-reboot, make sure your board reliably boots the new kernel. Test a manual apt upgrade + reboot cycle first. If boot fails, see the crash diagnosis guide for recovery.

Monitoring with journalctl

Check for SSH attack attempts and system issues regularly:

# Failed SSH attempts in the last hour
journalctl -u sshd --since "1 hour ago" | grep -i "failed\|invalid"

# All authentication failures
journalctl _COMM=sshd --priority=warning --since today

# Fail2ban actions
journalctl -u fail2ban --since today | grep -i "ban\|unban"

# System-level errors
journalctl --priority=err --since "24 hours ago"

# Disk health (if running root on SATA)
sudo smartctl -a /dev/sda | grep -E "Reallocated|Pending|Temperature"
Tip: If the board runs root on SD, reduce logging to extend card life. See the microSD wear guide for journal configuration that reduces writes by 80%.

Port Scan Yourself to Verify

From another machine on the same network (or from the internet if already exposed):

# Scan all ports from another machine
nmap -sS -p- 192.168.1.50

# Expected output: only port 22 open (or whichever ports you allowed)
# If you see unexpected open ports, check what's listening:
sudo ss -tlnp

If nmap shows ports you didn't allow in UFW, something is binding before UFW loads or you have a rule you forgot about. Fix it before going live.

Ongoing Maintenance Checklist

TaskFrequencyCommand
Check fail2ban bansWeeklysudo fail2ban-client status sshd
Review auth logsWeeklyjournalctl _COMM=sshd --since "7 days ago" | grep Failed | wc -l
Verify unattended-upgrades ranWeeklycat /var/log/unattended-upgrades/unattended-upgrades.log
Full manual update + rebootMonthlysudo apt update && sudo apt upgrade && sudo reboot
Check UFW rules for stale entriesMonthlysudo ufw status numbered
Verify image integrity after updatesAfter kernel upgradesSee image integrity guide
Rotate SSH keysYearlyGenerate new key, deploy, remove old