One-command Linux server hardening.
Hardens SSH, firewall, fail2ban, kernel, auditd, services, and automatic updates. Works across Ubuntu/Debian, RHEL/CentOS/AlmaLinux/Rocky, Fedora, Arch, and SUSE. Built for production servers and cloud instances (EC2, GCP, DigitalOcean, etc.).
One-liner (review + run):
curl -fsSL https://raw.githubusercontent.com/poppyszn/lockdown/main/harden.sh -o /tmp/harden.sh
sudo bash /tmp/harden.sh --ssh-user deployer --ssh-pubkey ~/.ssh/id_ed25519.pubFully automated (CI/CD, cloud-init, Ansible):
curl -fsSL https://raw.githubusercontent.com/poppyszn/lockdown/main/harden.sh | sudo bash -s -- \
--auto --accept-warning \
--ssh-user deployer \
--ssh-port 2222 \
--ssh-pubkey /tmp/deploy_key.pub \
--skip-firewall
⚠️ WARNING: This script disables password authentication and changes the SSH port. Make sure you have console access and an SSH key ready before running.
| Area | What Lockdown Configures |
|---|---|
| SSH | Custom port, key-only auth, root login disabled, idle timeout, AllowUsers, no X11/TCP forwarding |
| Users | Creates sudo user with NOPASSWD, locks root, password policy (14+ chars), umask 027, restricts su |
| Firewall | UFW with default deny in/out, rate-limited SSH, only opens ports you specify |
| Fail2ban | SSH jail with aggressive mode, configurable ban time/retries, optional email alerts |
| Kernel | ASLR, SYN flood protection, ICMP hardening, reverse path filtering, source routing disabled, BPF/ptrace lockdown |
| Logging | auditd with 25+ rules, logwatch daily digests, log rotation, optional remote syslog |
| Services | Disables avahi, cups, bluetooth, rpcbind, telnet; provides systemd sandboxing template |
| Network | Disables IPv6 (optional), blocks unused protocols (DCCP, SCTP, RDS, TIPC), disables unused filesystems |
| Updates | Unattended security upgrades (apt/dnf/yum), optional auto-reboot scheduling |
| Docker | no-new-privileges, ICC disabled, log rotation, hardened container run helper script |
| Maintenance | Weekly security audit cron, port checker, SSH key rotation reminders |
Usage: sudo bash harden.sh [OPTIONS]
Options:
--auto Non-interactive mode (accept all defaults)
--dry-run Preview changes without applying them
--accept-warning Skip the critical safety warning prompt
--ssh-user USER Set the sudo user and SSH AllowUsers (default: deployer)
--ssh-port PORT Set SSH port (default: 2222, range: 1024-65535)
--ssh-pubkey FILE Path to SSH public key file for the sudo user
--skip-firewall Skip UFW setup (for EC2/GCP with security groups)
--skip-docker Skip Docker hardening section
--help, -h Show help message
# Interactive — prompts for confirmation at each step
sudo bash harden.sh --ssh-user myadmin --ssh-pubkey ~/.ssh/id_ed25519.pub
# Fully automated — no prompts
sudo bash harden.sh --auto --accept-warning \
--ssh-user deployer --ssh-pubkey /tmp/key.pub
# EC2 instance — skip UFW (use Security Groups instead)
sudo bash harden.sh --auto --accept-warning --skip-firewall \
--ssh-user ec2-user --ssh-pubkey /tmp/key.pub
# Dry run — see what would change without touching anything
sudo bash harden.sh --dry-run --ssh-user deployer --ssh-pubkey ~/.ssh/key.pub
# Custom SSH port
sudo bash harden.sh --ssh-user admin --ssh-port 2200 --ssh-pubkey ~/.ssh/key.pubUse this in your EC2 launch template, GCP startup script, or cloud-init config:
#cloud-config
write_files:
- path: /tmp/deploy_key.pub
content: |
ssh-ed25519 AAAA... your-key-comment
permissions: '0644'
runcmd:
- curl -fsSL https://raw.githubusercontent.com/poppyszn/lockdown/main/harden.sh -o /tmp/harden.sh
- bash /tmp/harden.sh --auto --accept-warning --skip-firewall --ssh-user deployer --ssh-pubkey /tmp/deploy_key.pub
- rm -f /tmp/harden.sh /tmp/deploy_key.pub- name: Harden server with Lockdown
hosts: all
become: yes
tasks:
- name: Copy SSH public key
copy:
src: ~/.ssh/id_ed25519.pub
dest: /tmp/deploy_key.pub
mode: '0644'
- name: Download hardening script
get_url:
url: https://raw.githubusercontent.com/poppyszn/lockdown/main/harden.sh
dest: /tmp/harden.sh
mode: '0755'
- name: Run hardening
command: >
bash /tmp/harden.sh
--auto --accept-warning
--ssh-user deployer
--ssh-port 2222
--ssh-pubkey /tmp/deploy_key.pub
--skip-firewall
- name: Cleanup
file:
path: "{{ item }}"
state: absent
loop:
- /tmp/harden.sh
- /tmp/deploy_key.pubAfter running, verify everything is applied:
curl -fsSL https://raw.githubusercontent.com/poppyszn/lockdown/main/verify-hardening.sh | sudo bashOr download and run:
curl -fsSL https://raw.githubusercontent.com/poppyszn/lockdown/main/verify-hardening.sh -o /tmp/verify.sh
sudo bash /tmp/verify.shOutput looks like:
── 1. SSH HARDENING ──
✓ PASS SSH port changed from default (port 2222)
✓ PASS Port 22 is closed
✓ PASS SSH is listening on port 2222
✓ PASS Root login disabled
✓ PASS Password authentication disabled
...
══════════════════════════════════════════
VERIFICATION SUMMARY
══════════════════════════════════════════
PASS: 42 FAIL: 0 WARN: 3
Server hardening looks solid! ✓
Every file modified by the script is backed up to:
/root/hardening-backups/<timestamp>/
Full execution log is saved to:
/var/log/server-hardening-<timestamp>.log
| Command | Description |
|---|---|
sudo /usr/local/bin/security-audit |
Full security audit (runs weekly via cron) |
sudo /usr/local/bin/check-open-ports |
Quick view of open ports + firewall status |
| Distro | Versions Tested |
|---|---|
| Ubuntu | 20.04, 22.04, 24.04 |
| Debian | 11, 12 |
| RHEL / CentOS / AlmaLinux / Rocky | 8, 9 |
| Amazon Linux | 2, 2023 |
| Fedora | 38+ |
| Arch Linux | Rolling |
| openSUSE | Leap 15+ |
- SSH Keys Required: Password authentication is disabled. You must provide an SSH public key or you will be locked out.
- EC2 / Cloud: Use
--skip-firewallif your cloud provider has security groups. Don't double-firewall. - Docker: The script does NOT enable
userns-remapby default — it breaks existing containers. Enable it manually on fresh installs. - Run Once: The script is idempotent — safe to run multiple times.
- Backups: Every modified file is backed up. Check
/root/hardening-backups/to restore anything.
- Test SSH access from a new terminal before closing your current session
- Set up TLS for web services (Let's Encrypt / certbot)
- Configure a reverse proxy (Nginx) — never expose app ports directly
- Set up secrets management — no plaintext keys in config files
- Schedule key rotation — SSH keys and API keys on a regular cadence
PRs welcome. Please test on at least Ubuntu and one RHEL-based distro before submitting.