papyri/md/debian_server_setup.md
2024-03-20 11:40:22 -05:00

225 lines
7.8 KiB
Markdown

# Debian Server Setup
## Contents
- [Server Installation](#server-installation)
- [Server User Setup](#server-user-setup)
- [Disable root Login](#disable-root-login)
- [Server Hardening](#server-hardening)
- [fail2ban Setup](#fail2ban-setup)
## Server Installation
Install Debian using the Minimal setup method, add the SSH server option during the final steps on the installation. This is the default image delivered from many cloud providers; it may use the default hostname `localhost` - if desired, set a new one:
```
hostnamectl set-hostname myhostname
```
Ensure the hostname resolves locally - it does not have to be `127.0.0.1` (localhost) nor a FQDN, for example this works:
```
127.0.0.1 localhost
127.0.1.1 myhostname
```
Adjust as needed based on how `/etc/hosts` is already configured from the installation.
## Server User Setup
> **Use a very secure password** - at a minimum use `pwgen -sB 15`, strong password security encouraged!
Set up a non-root user and add to the `sudo` group, then add a password. Use this user for SSH access and become root once logged in with sudo; if you have used SSH keys to log in as root, copy to this new user's setup as well if needed:
```
apt-get update
apt-get install sudo
export MYUSER="frankthetank"
useradd -m -d /home/${MYUSER} -s /bin/bash -g users -G sudo ${MYUSER}
passwd ${MYUSER}
```
If you are unable to use `ssh-copy-id` from your workstation to add a new SSH key, perform the work manually:
```
mkdir /home/${MYUSER}/.ssh
cp /root/.ssh/authorized_keys /home/${MYUSER}/.ssh/
chmod 0700 /home/${MYUSER}/.ssh
chmod 0600 /home/${MYUSER}/.ssh/authorized_keys
chown -R ${MYUSER}:users /home/${MYUSER}/.ssh
```
> **SSH in as this user and test `sudo` several times** - log out completely between tests
### Disable root Login
**If the above is successful** and you are capable of gaining full root privileges via the non-root SSH session using sudo, now disable root logins in SSH from the outside world for an additional security layer. The `root` account still remains usable, just not via _direct_ SSH access.
The task is to set `PermitRootLogin no` - the setting varies from one provider to another, sometimes it's already set (either yes or no), sometimes it's commented out. This small scriptlet should handle these 2 most common cases, **be careful** and investigate for yourself:
```
_SCFG="/etc/ssh/sshd_config"
if $(grep -iEq '^PermitRootLogin[[:space:]]+yes' "${_SCFG}"); then
sed -i.bak -e 's/^PermitRootLogin.*/PermitRootLogin no/gi' "${_SCFG}"
else
sed -i.bak -e 's/^#PermitRootLogin.*/PermitRootLogin no/gi' "${_SCFG}"
fi
```
**After confirming the change is correct**, restart the SSH core daemon (it will not log you out):
```
systemctl restart sshd
```
**Test logging in again** to ensure the changes are as expected. Do not log out of the active, working SSH session as root until you've confirmed in _another_ session you can log in as your non-root user and still gain `sudo` to root.
## Server Hardening
**1.** The Debian default vimrc (`set mouse=a`, `/usr/share/vim/vim80/defaults.vim`) messes up middle-mouse click paste when remote via SSH, override the setting to just disable the mouse:
```
echo 'set mouse=' >> ~/.vimrc
```
**2.** Install a few basic packages to make life a little nicer; typically the minimal install / cloud instances are stripped down and need a few things added, both for security and ease of use. Adjust as desired:
```
apt-get update
echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | debconf-set-selections
echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | debconf-set-selections
echo "unattended-upgrades unattended-upgrades/enable_auto_updates boolean true" | debconf-set-selections
apt-get install sysstat unattended-upgrades iptables-persistent man less vim rsync bc net-tools git strace
```
The `smem` package will pull in a lot of X dependencies due to an embedded recommendation, install it while disabling that feature. This utility can be used to quickly query memory usage (including swap) on the memory constrained cloud server:
```
apt-get install smem --no-install-recommends
```
**3.** Enable `journald` to store logs on disk instead of just RAM. By default, the `journald` system is in automatic mode - on boot it will create the ephemeral tmpfs `/run` out of RAM, but will _only_ transition to storing the journal on disk (out of RAM) if this directory exists. (done in Debian 11+ by the installer)
```
# Debian 10 and earlier
mkdir /var/log/journal
```
**4.** Enable _sysstat_ for ongoing statistics capture of your instance (use `sar` to view):
```
sed -i.bak -e 's|^ENABLED=".*"|ENABLED="true"|g' /etc/default/sysstat
```
**5.** Enable _unattended-upgrades_ to ensure that all Security updates are applied:
```
cat << 'EOF' > /etc/apt/apt.conf.d/02periodic
APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "5";
APT::Periodic::Unattended-Upgrade "1";
EOF
```
**6.** Enable the basic _iptables_ rules to allow only port 22:
```
cat << 'EOF' > /etc/iptables/rules.v4
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
EOF
cat << 'EOF' > /etc/iptables/rules.v6
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
COMMIT
EOF
```
**7.** Add a bit of swap if needed - using swap is not bad in and of itself, the Linux kernel will attempt to cache it's small bits of data if available. A cloud instance may not be delivered with any swap configured.
```
# 128M swap file
dd if=/dev/zero of=/swap.file bs=1024 count=128000
chmod 0600 /swap.file
mkswap /swap.file
echo '/swap.file none swap defaults 0 0' >> /etc/fstab
swapon /swap.file
```
**8.** Finally, ensure all the services are enabled and apply all outstanding updates; reboot as needed for a new kernel. If you don't reboot here, you'll need to `service` _foo_ `restart` each one individually (just reboot, it's easier):
```
systemctl disable remote-fs.target rsync.service
systemctl enable sysstat unattended-upgrades netfilter-persistent
apt-get full-upgrade -y
reboot
```
### fail2ban Setup
Recommended: configure fail2ban to keep an eye on the SSH port for brute force attacks.
> **Note**: `fail2ban` tends to consume a fair amount of memory the longer it runs; if the cloud server is memory constrained, you may wish to skip this step or disable the service later. Use `smem` to monitor it periodically.
```
apt-get install fail2ban sqlite3
cat << 'EOF' > /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8
bantime = 3600
maxretry = 3
backend = auto
destemail = root@localhost
EOF
systemctl enable --now fail2ban
```
Additionally, add a weekly `cron` task to purge the database of old IPs (bug in 0.9.x series) and to restart the daemon to free up it's RAM usage:
```
cat << 'EOF' > /etc/fail2ban/dbpurge.sql
delete from bans where timeofban <= strftime('%s', date('now', '-7 days'));
vacuum;
.quit
EOF
cat << 'EOF' > /etc/cron.weekly/f2b-cleanup
#!/bin/sh
if [ -x /usr/bin/sqlite3 ]; then
sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 < /etc/fail2ban/dbpurge.sql
fi
systemctl restart fail2ban.service
EOF
chown root:root /etc/cron.weekly/f2b-cleanup
chmod 0755 /etc/cron.weekly/f2b-cleanup
```