How to configure hardened Debian server
Requirements
- Virtual private server (VPS) or dedicated server running Debian 10 (buster)
- Linux or macOS computer
Caveats
- When copy/pasting commands that start with
$
, strip out$
as this character is not part of the command - When copy/pasting commands that start with
cat << "EOF"
, select all lines at once (fromcat << "EOF"
toEOF
inclusively) as they are part of the same (single) command
Guide
Step 1: create server
SSH key pair (on computer)
When asked for file in which to save key, enter server
.
When asked for passphrase, use output from openssl rand -base64 24
(and store passphrase in password manager).
Use server.pub
public key when setting up server.
$ mkdir -p ~/.ssh
$ cd ~/.ssh
$ ssh-keygen -t rsa -C "server"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): server
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in server.
Your public key has been saved in server.pub.
The key fingerprint is:
SHA256:De1pasRJ2n0ggfRSWRJqrcensqboAc2i+/+/FxAo3xI server
The key's randomart image is:
+---[RSA 3072]----+
| ..o=+. |
| . E=o+ |
| o+o*.o |
| o .oOoB o |
|o o o.S.B . |
|.o o =.. |
|. . . + . |
| . o .+ . |
|.o+.o+o.oo |
+----[SHA256]-----+
$ cat server.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDP58i1vuFEe3zoHT+hZRh0YaXQY+ADa8OgBIoTji+AqzZRAa3ve8yDLwtoQKYpAZ2OcHoWDJP2pB/4unLJfKu6ILqKjRLkrnWvMGqcFs2QSVFg4ernmjiSAf3l2qrM+jxwElPEUo0Ht1GByEnWw2yfq0RNg0fukVrczWnUzvJMyhzhG2sjncFHIe2L6SPLlqRW46uHQBhFuHHb2gERV6smH/1ZS8YJtjq1klgVshhZWlBtodbIHo70owAeIpkeped966fSfzcAVksr3lTLR5jQyqgcTlDLj9vJn8nhGX0S/ETUs9dUNAOz0HWDvAaRyw95g/KWrctHvvng4VzjoU4qJlkjnhutoyDhz/medMnm4rkD6g6hOCkNKhMrCKby45TlMWFCZLjDwB70DZwqJChfWXlo0Ov0lah0a+ZgZ7Quz4yvzlrJt7vZkqFfr5LBI8AOB3yfFbeOZR564Q0jaH7C6yeRRvYVZkNCCZAVK9K2v1X7Bl0x42WN/MCzsA6embk= server
Step 2: log in to server as root
Replace 185.112.147.115
with IP of server.
ssh root@185.112.147.115 -i ~/.ssh/server
Step 3: disable root Bash history
echo "HISTFILESIZE=0" >> ~/.bashrc
source ~/.bashrc
Step 4: set root password
When asked for password, use output from openssl rand -base64 24
(and store password in password manager).
passwd
Step 5: create server-admin user
When asked for password, use output from openssl rand -base64 24
(and store password in password manager).
All other fields are optional, press enter to skip them and then press Y.
$ adduser server-admin
Adding user `server-admin' ...
Adding new group `server-admin' (1000) ...
Adding new user `server-admin' (1000) with group `server-admin' ...
Creating home directory `/home/server-admin' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for server-admin
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
Step 6: copy root authorized_keys
file to server-admin home folder
mkdir /home/server-admin/.ssh
cp /root/.ssh/authorized_keys /home/server-admin/.ssh/authorized_keys
chown -R server-admin:server-admin /home/server-admin/.ssh
Step 7: log out
exit
Step 8: log in as server-admin
Replace 185.112.147.115
with IP of server.
ssh server-admin@185.112.147.115 -i ~/.ssh/server
Step 9: disable server-admin Bash history
sed -i -E 's/^HISTSIZE=/#HISTSIZE=/' ~/.bashrc
sed -i -E 's/^HISTFILESIZE=/#HISTFILESIZE=/' ~/.bashrc
echo "HISTFILESIZE=0" >> ~/.bashrc
source ~/.bashrc
Step 10: switch to root
When asked, enter root password.
su -
Step 11: disable root login and password authentication
sed -i -E 's/^(#)?PermitRootLogin (prohibit-password|yes)/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i -E 's/^(#)?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
Step 12: update APT index and upgrade packages
Update APT index
apt update
Upgrade packages
apt upgrade -y
Step 13: install and configure Vim
Install Vim
apt install -y vim
Configure Vim
cat << "EOF" > ~/.vimrc
set encoding=UTF-8
set termencoding=UTF-8
set nocompatible
set backspace=indent,eol,start
set autoindent
set tabstop=2
set shiftwidth=2
set expandtab
set smarttab
set ruler
set paste
syntax on
EOF
Step 14: set timezone (the following is for Montreal time)
See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for available timezones.
timedatectl set-timezone America/Montreal
Step 15: configure sysctl (if server is IPv4-only)
Heads-up: only run the following if server is IPv4-only.
cp /etc/sysctl.conf /etc/sysctl.conf.backup
cat << "EOF" >> /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF
sysctl -p
Step 16: install iptables-persistent
When asked to save current IPv4 or IPv6 rules, answer Yes
.
apt install -y iptables-persistent
Step 17: configure iptables
iptables -N SSH_BRUTE_FORCE_MITIGATION
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --set
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[ssh-brute-force]: "
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
iptables -A SSH_BRUTE_FORCE_MITIGATION -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSH_BRUTE_FORCE_MITIGATION
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -P FORWARD DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
If server is IPv4-only, run:
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
If server is dual stack (IPv4 + IPv6) run:
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
Step 18: log out and log in to confirm iptables didn’t block SSH
Log out
exit
exit
Log in
Replace 185.112.147.115
with IP of server.
ssh server-admin@185.112.147.115 -i ~/.ssh/server
Switch to root
When asked, enter root password.
su -
Step 19: make iptables rules persistent
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
👍