Comprehensive automated security hardening for Raspberry Pi. One script to transform your Pi into a fortress.
This script transforms a fresh Raspberry Pi installation from default configuration to a hardened, secure system. It automates hours of manual security configuration into a single command.
Perfect for:
- Home servers and NAS systems
- IoT projects exposed to the internet
- Development environments
- Learning security best practices
- Any Pi that needs protection
- Automatic system updates with unattended security patches
- Secure file permissions and system limits
- Unused account lockdown
- Kernel hardening (restricted
dmesgand kernel pointers)
- Root login disabled
- Strong encryption (ChaCha20-Poly1305, AES-256-GCM)
- Key-based authentication support
- Maximum 3 login attempts
- Connection timeouts and session limits
- Configuration validated with
sshd -tand rolled back automatically if invalid
- UFW firewall (default deny incoming, SSH rate-limited)
- Fail2Ban intrusion prevention
- SYN flood protection
- IP spoofing protection
- Suspicious packet logging
- Rootkit detection (rkhunter, chkrootkit)
- System audit logging
- Custom security check script
- Disable Bluetooth (if not needed)
- Disable WiFi (for Ethernet-only setups)
- Secure shared memory
Important: This script is designed for Raspberry Pi OS installations configured with the Raspberry Pi Imager. Make sure you've set up your username, password, and SSH settings during the imaging process.
Compatibility: Tested on Raspberry Pi OS Bookworm (current) and Bullseye. The script detects the correct boot config path, the SSH port in use, whether Fail2Ban should read auth.log or the systemd journal, and it adds the sshd_config.d include directive if your image is missing it.
Download the script:
wget https://raw.githubusercontent.com/Sharonhazan/pi-fortress/main/pi-fortress.shMake it executable:
chmod +x pi-fortress.shRun as root:
sudo ./pi-fortress.shThe script asks before disabling Bluetooth and WiFi. To run it without any prompts:
| Flag | Effect |
|---|---|
-y, --yes |
Answer yes to every optional prompt (disables Bluetooth and WiFi) |
-n, --no |
Answer no to every optional prompt (leaves the radios on) |
-h, --help |
Show usage and exit |
sudo ./pi-fortress.sh --noThe script is safe to re-run: it never duplicates entries in /etc/fstab or config.txt, and it writes its own drop-in files rather than appending to shared ones.
The script will:
- β Update all system packages
- β
Configure SSH security settings, verify them with
sshd -t, then restart the service - β Install and configure Fail2Ban
- β Set up UFW firewall
- β Install security monitoring tools
- β Apply network and kernel hardening
- β Set proper permissions
- β Disable unused accounts
- β Create security check script
- β Record an rkhunter baseline so your first scan isn't full of false positives
Total time: ~5-10 minutes (depending on your internet speed)
Everything is logged to /var/log/pi_security_hardening.log. If a step fails, the script stops and tells you which line it stopped on rather than leaving you guessing.
| Path | What it's for |
|---|---|
/etc/ssh/sshd_config.d/hardening.conf |
All SSH hardening settings |
/etc/ssh/sshd_config.backup.<timestamp> |
Timestamped backup, one per run |
/etc/fail2ban/jail.local |
Fail2Ban jail for SSH |
/etc/sysctl.d/99-security.conf |
Network and kernel hardening |
/etc/security/limits.d/99-pi-fortress.conf |
Core dump and process limits |
/etc/apt/apt.conf.d/50unattended-upgrades |
Automatic security updates |
/etc/fstab |
noexec,nosuid,nodev on /dev/shm |
/boot/firmware/config.txt (or /boot/config.txt) |
Only if you opt into disabling Bluetooth or WiFi |
/usr/local/bin/security-check.sh |
The security check command |
/root/setup_ssh_keys.sh |
SSH key setup instructions |
Settings live in their own drop-in files rather than being appended to shared ones, which is what makes re-running the script safe.
| Package | Purpose |
|---|---|
fail2ban |
Blocks IPs after failed login attempts |
ufw |
Simple, effective firewall |
unattended-upgrades |
Automatic security updates |
rkhunter |
Rootkit detection |
chkrootkit |
Additional rootkit scanner |
logwatch |
Log analysis |
auditd |
System auditing |
The script keeps password authentication enabled initially. After setting up SSH keys, you should disable it.
On your local machine: Generate SSH key (if you don't have one):
ssh-keygen -t ed25519 -C "your_email@example.com"Copy to your Pi:
ssh-copy-id username@your-pi-ipTest it works:
ssh username@your-pi-ipOnce working, disable password authentication: Edit SSH configuration:
sudo nano /etc/ssh/sshd_config.d/hardening.confUncomment this line:
PasswordAuthentication no
Restart SSH:
sudo systemctl restart sshReboot your Pi:
sudo rebootRun the security check:
sudo security-check.shThis shows:
- Failed login attempts (from
auth.logor the journal, whichever exists) - Fail2Ban status
- Firewall rules
- Recent logins
- Open ports
- Count of pending security updates
- Rootkit scan
β Root login: DISABLED
β Password auth: Enabled initially (disable after key setup)
β Max auth tries: 3
β Login timeout: 60 seconds
β Idle timeout: 5 minutes
β Strong ciphers only
These settings go in /etc/ssh/sshd_config.d/hardening.conf, which sshd only reads if sshd_config contains an Include /etc/ssh/sshd_config.d/*.conf line. Recent images ship with it; if yours doesn't, the script adds it, otherwise the hardening would be silently ignored. The config is then checked with sshd -t before the service restarts, and removed again if sshd rejects it.
β Ban after: 3 failed attempts
β Ban duration: 2 hours
β Find time: 10 minutes
β Email alerts: Configurable
On Bookworm, Debian no longer installs rsyslog, so /var/log/auth.log may not exist. The script detects this and points Fail2Ban at the systemd journal instead, which keeps the sshd jail working either way.
β Default incoming: DENY
β Default outgoing: ALLOW
β SSH: ALLOW, rate-limited (6 connections / 30s per IP)
β Custom ports: Easy to add
The script reads the port SSH is actually listening on (via sshd -T) and opens that one, so a Pi already moved off port 22 will not get locked out.
Allow HTTP:
sudo ufw allow 80/tcpAllow HTTPS:
sudo ufw allow 443/tcpAllow custom application:
sudo ufw allow 8080/tcpCheck firewall status:
sudo ufw status numberedView Fail2Ban status:
sudo fail2ban-client status sshdSee banned IPs:
sudo fail2ban-client status sshd | grep "Banned IP"Unban an IP:
sudo fail2ban-client set sshd unbanip 192.168.1.100Run rootkit scan:
sudo rkhunter --checkView auth log:
sudo tail -f /var/log/auth.logIf that file doesn't exist (Bookworm without rsyslog), read the journal instead:
sudo journalctl -u ssh -f- β Configure your Pi with Raspberry Pi Imager (set username, password, and enable SSH)
- β This script is for fresh installations - test on existing systems carefully
- β Have physical access available in case of lockout
- β Know your current IP address for SSH access
- π Set up SSH keys before disabling password authentication
- π§ͺ Test SSH access in a new terminal before closing your current one
- π Document any custom ports you open in the firewall
- π Reboot to apply all changes
β Disabling password auth without SSH keys β Lockout
β Not allowing custom ports in UFW β Services won't work
β Forgetting to reboot β Some changes won't apply
β Opening more than 6 SSH connections in 30 seconds β UFW's rate limit blocks you temporarily; this trips up backup and deployment scripts that reconnect in a loop
Option 1: Physical Access Connect with keyboard and monitor, then:
sudo nano /etc/ssh/sshd_config.d/hardening.confSet this line:
PasswordAuthentication yes
Restart SSH:
sudo systemctl restart sshOption 2: SD Card Method
- Remove SD card from Pi
- Mount on another computer
- Edit
/etc/ssh/sshd_config.d/hardening.conf - Enable
PasswordAuthentication yes - Reinsert and boot
From another IP or local access:
sudo fail2ban-client set sshd unbanip YOUR_IPCheck firewall status:
sudo ufw statusAllow the port:
sudo ufw allow PORT_NUMBER/tcpIt prints the line number it stopped on and stops rather than continuing with a half-applied config. Check the log, fix the cause, and run it again β re-running is safe.
sudo tail -50 /var/log/pi_security_hardening.logConfirm the jail is actually up. An empty or missing sshd jail usually means Fail2Ban can't find a log source:
sudo fail2ban-client status sshd
sudo journalctl -u fail2ban -n 50The script picks backend = systemd when /var/log/auth.log is absent. If you install rsyslog later, re-run the script so the jail switches back to the log file.
Edit SSH config:
sudo nano /etc/ssh/sshd_config.d/hardening.confAdd this line:
Port 2222
Update firewall (the script creates SSH as a rate-limited rule, so delete it with limit, not allow):
sudo ufw limit 2222/tcp
sudo ufw delete limit 22/tcpRestart SSH:
sudo systemctl restart sshRe-running pi-fortress.sh afterwards will pick up the new port automatically for both the firewall and Fail2Ban.
Note: By default, email alerts in Fail2Ban are disabled because they require a mail server.
Install mail utilities:
sudo apt install mailutilsEdit Fail2Ban config:
sudo nano /etc/fail2ban/jail.localSet your email and action:
destemail = your@email.com
sendername = Fail2Ban
action = %(action_mwl)sRestart Fail2Ban:
sudo systemctl restart fail2banEdit sysctl config:
sudo nano /etc/sysctl.d/99-security.confUncomment these lines:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
Apply changes (sysctl -p on its own only reads /etc/sysctl.conf, so point it at the file):
sudo sysctl -e -p /etc/sysctl.d/99-security.confContributions welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/improvement) - Commit your changes (
git commit -m 'Add improvement') - Push to the branch (
git push origin feature/improvement) - Open a Pull Request
- Support for other Linux distributions
- Additional security checks
- Automated testing
- Documentation improvements
- Translations
MIT License - see LICENSE file for details.
This script implements security best practices but no system is 100% secure. Use at your own risk. Always:
- Test in a safe environment first
- Keep backups of important data
- Monitor your logs regularly
- Stay updated on security threats
The author is not responsible for any damage, data loss, or security breaches.
Sharon Hazan
- GitHub: @Sharonhazan
- Repository: pi-fortress
- Issues: Report bugs or request features
- Discussions: Ask questions or share ideas
- Security Issues: Report privately via GitHub Security Advisories
β If Pi Fortress helped secure your Raspberry Pi, please star the repository!
Made with β€οΈ for the Raspberry Pi community