171 lines
5.3 KiB
Markdown
171 lines
5.3 KiB
Markdown
# Linux Server Hardener
|
|
|
|
POSIX-compliant shell script that automates server security hardening on a new Linux/FreeBSD server.
|
|
It is intended to be executed **interactively** as `root`.
|
|
|
|
## Usage
|
|
|
|
- WARNING: Make sure you:
|
|
- Have root access to the server
|
|
- Have 2 ssh sessions active to the server:
|
|
- 1st: for running the script
|
|
- 2nd: for viewing script's logs and to recover from it's failure
|
|
- SAVE ALL CREDENTIALS SHOWN POST EXECUTION: THEY AREN'T SAVED ANYWHERE AND WON'T BE DISPLAYED AGAIN.
|
|
|
|
- Options:
|
|
- `-r`: Reset root password
|
|
- `-u USERNAME`: Create a new user with sudo privileges
|
|
- `-h`: Display help message
|
|
|
|
```sh
|
|
curl -L -o harden.sh https://sot.li/hardensh
|
|
cat harden.sh # review content
|
|
chmod +x harden.sh
|
|
|
|
# Harden server: SSH hardening, Fail2ban, Firewalld/pf
|
|
./harden.sh
|
|
|
|
# Create new privileged (sudo) user & harden server
|
|
./harden.sh -u jay
|
|
|
|
# Create new privileged user, reset root password & harden server
|
|
./harden.sh -r -u jay
|
|
```
|
|
|
|
- Quick & Dirty:
|
|
|
|
```sh
|
|
curl -sL https://sot.li/hardensh | sh -s -- -r -u jay
|
|
```
|
|
|
|
> There are security risks involved with running scripts directly from web, as done above. Everyone does it; but, you have been warned.
|
|
|
|
## Post Installation
|
|
|
|
- Linux:
|
|
|
|
```sh
|
|
# Check firewall status
|
|
sudo firewall-cmd --state && sudo firewall-cmd --list-services
|
|
|
|
# Allow a port/service (e.g., dhcp)
|
|
sudo firewall-cmd --permanent --add-service=dhcp
|
|
|
|
# Block a port/service (e.g., http)
|
|
sudo firewall-cmd --permanent --remove-service=http
|
|
|
|
# List all active jails
|
|
sudo fail2ban-client status
|
|
|
|
# List all IPs banned by a jail (e.g., sshd)
|
|
sudo fail2ban-client status sshd
|
|
|
|
# Manually ban an IP
|
|
sudo fail2ban-client set sshd banip 192.0.2.1
|
|
|
|
# Manually un-ban an IP
|
|
sudo fail2ban-client set sshd unbanip 192.0.2.1
|
|
```
|
|
|
|
- FreeBSD:
|
|
|
|
```sh
|
|
# Show active firewall rules
|
|
sudo pfctl -s rules
|
|
|
|
# Allow or block port/service
|
|
# Edit /etc/pf.conf & add/remove the port/service to the comma separated list in { }
|
|
#
|
|
# OR use the following command (e.g., allow dhcp)
|
|
sed -i.bak 's/[[:space:]]}/, dhcp }/' /etc/pf.conf && pfctl -nf /etc/pf.conf && pfctl -vvf /etc/pf.conf
|
|
|
|
# List all active Fail2ban jails
|
|
sudo fail2ban-client status
|
|
|
|
# List all IPs banned by a Fail2ban jail (e.g., sshd)
|
|
sudo fail2ban-client status sshd
|
|
|
|
# Manually ban an IP
|
|
sudo fail2ban-client set sshd banip 192.0.2.1
|
|
|
|
# Manually un-ban an IP
|
|
sudo fail2ban-client set sshd unbanip 192.0.2.1
|
|
```
|
|
|
|
## Status
|
|
|
|
Tested and working on:
|
|
|
|
- Linux:
|
|
- Debian 13
|
|
- Ubuntu 22.04, 24.04
|
|
- Fedora 42
|
|
- Rocky Linux
|
|
- Alma Linux
|
|
- CentOS Stream 10
|
|
- openSUSE
|
|
- FreeBSD:
|
|
- FreeBSD 14.3
|
|
|
|
> Tested with each OS's official qcow2 file through KVM virtualisation.
|
|
|
|
## What does it do exactly?
|
|
|
|
Depending on options chosen & OS (Linux vs FreeBSD), it does the following:
|
|
|
|
1. (Optional) Resets `root` users password
|
|
2. Creates new user & give it `sudo` privileges
|
|
3. Generates OpenSSH (ed25519) keys (public & private) for the user with a passphrase
|
|
4. Updates SSH configuration to:
|
|
- Disable `root` login
|
|
- Disable password login
|
|
- Enable sshkey-only login
|
|
5. Installs applications:
|
|
- Linux: curl, sudo, firewalld, fail2ban
|
|
- FreeBSD: curl, sudo, fail2ban
|
|
6. Configures firewall which allows incoming sshd, http, https traffic & blocks everything else:
|
|
- Linux: `firewalld` is used as firewall
|
|
- FreeBSD: `pf` is used as firewall
|
|
7. Linux: Configures `fail2ban` to with following jails (FreeBSD: `pf` table is used to block IPs):
|
|
- sshd
|
|
- nginx-botsearch
|
|
- nginx-http-auth
|
|
- nginx-limit-req
|
|
- haproxy-http-auth
|
|
- recidive
|
|
8. Displays following on console:
|
|
- New root password
|
|
- New user name & password
|
|
- SSH Private & Public keys
|
|
- SSH Passphrase
|
|
9. Deletes SSH Private Key from server
|
|
|
|
> Handling Operation Failure
|
|
>
|
|
> - The script creates back up of each file it changes, in the same location as the original file. Backup file name: [original-name].bak.[timestamp]
|
|
> - On failure of an operation that depends on a configuration file, the script restores the original file and restarts the relevant service.
|
|
> - Reason for failures can be found in the log file.
|
|
|
|
### Why `firewalld` and not `ufw`?
|
|
|
|
- `firewalld` is default firewall on Rocky Linux, SUSE, Fedora, RHEL
|
|
- Commands for basic administration are similar to that of `ufw`
|
|
- Comes with a lot more power when needed
|
|
|
|
## To-do
|
|
|
|
- [ ] LUKS encryption
|
|
- [ ] Unattended-updates if distro supports it (do it during installations)
|
|
- [ ] Layer 2 security: Midtier: OSSEC
|
|
- [ ] Audit: Lynis
|
|
- [ ] Monitoring + Alerts: Goaccess???
|
|
- [ ] Backups: ???
|
|
|
|
## Retrospect: Why a script?
|
|
|
|
You CAN do everything this script does with Ansible. That is, if you know how it works (not trivial) and have it's *control node* installed on your local machine. I don't.
|
|
|
|
Personally, writing the script has given me deeper understanding of cloud security and about the similarities (and differences) between Unix-like operating systems.
|
|
|
|
That said, the quirks of shell scripting is tiring to keep up with. Also, most VPS providers support cloud-init. Cloud-init can't do everything the script does; but it's *trivial* to accomplish 80% of it using cloud-init. That makes it worthwhile to learn and use.
|