225 lines
7.8 KiB
Markdown
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
|
|
```
|
|
|