TABLE OF CONTENT
Introduction
Prerequisites
Boot Loader & Interface Names
GRUB - Boot Verbosity
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="quiet"/GRUB_CMDLINE_LINUX_DEFAULT=""/g' /etc/default/grub
sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0"/g' /etc/default/grub
update-grub
Network Interface Names
Debian uses the predictable names scheme when setting network interface names by default. I like to change this (it's a matter of habit) back to the old simple scheme. Now, since we passed kernel parameters for legacy network interface names in GRUB, this is mandatory to do for this guide.
Under the presumption there is only one wired active network interface on our server, this can be done by first setting the variable that will "extract" the current name of our interface:
INTERFACE_CURRENT=$(ip a | grep "2: " | awk '{print $2;}' | cut -d: -f1)
sed -i "s/$INTERFACE_CURRENT/eth0/" /etc/network/interfaces
Software Sources
echo -e "# Main Repos
deb http://deb.debian.org/debian bullseye main contrib non-free
deb http://deb.debian.org/debian-security/ bullseye-security main contrib non-free
deb http://deb.debian.org/debian bullseye-updates main contrib non-free
# Sources - enable only when needed
#deb-src http://deb.debian.org/debian bullseye main
#deb-src http://deb.debian.org/debian-security/ bullseye-security main
#deb-src http://deb.debian.org/debian bullseye-updates main
# Backports - For software like Git, Redis, etc.
deb http://deb.debian.org/debian bullseye-backports main contrib non-free" > /etc/apt/sources.list
echo -e "# Main Repos
deb http://deb.debian.org/debian bookwork main contrib non-free non-free-firmware
deb http://deb.debian.org/debian-security/ bookworm-security main contrib non-free non-free-firmware
deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
# Sources - enable only when needed
#deb-src http://deb.debian.org/debian bookworm main
#deb-src http://deb.debian.org/debian-security/ bookworm-security main
#deb-src http://deb.debian.org/debian bookworm-updates main
# Backports - For software like Git, Redis, etc.
deb http://deb.debian.org/debian bookworm-backports main contrib non-free non-free-firmware" > /etc/apt/sources.list
apt update
Sysdig Repository - Optional
Sysdig is awesome! Period. And thus, it is mandatory on my servers. That is all I'm going to say. Still, if you want to learn more about what it does, please check out the official Sysdig GitHub repository.
This step is optional, but I highly recommend you give Sysdig a try. To set up its repository, we need to install curl and gnupg2 software packages first:
apt install -y --no-install-recommends curl gnupg2 ca-certificates
curl -sS https://download.sysdig.com/DRAIOS-GPG-KEY.public | gpg --dearmor | tee /usr/share/keyrings/draios.gpg
curl -o /etc/apt/sources.list.d/draios.list https://download.sysdig.com/stable/deb/draios.list
sed -i "0,/deb/ s/deb/& [signed-by=\/usr\/share\/keyrings\/draios.gpg]/" /etc/apt/sources.list.d/draios.list
apt update
Aptitude - Optional
apt install --no-install-recommends -y aptitude apt-transport-https
echo -e "debconf debconf/frontend select Dialog\ndebconf debconf/priority select low" | debconf-set-selections
Software Installation
Initial System Update
aptitude update -q2
aptitude forget-new
aptitude full-upgrade --purge-unused -y
Standard Software Packages
aptitude install -R -y busybox_ bash-completion bind9-host busybox-static dnsutils dosfstools \
friendly-recovery ftp fuse geoip-database groff-base hdparm info install-info iputils-tracepath \
lshw lsof ltrace man-db manpages mlocate mtr-tiny parted powermgmt-base psmisc rsync sgml-base strace \
tcpdump telnet time uuid-runtime xml-core iptables resolvconf lsb-release openssh-server
At this point, you will be asked a few questions related to the configuration of the software you're installing. Please read and try to understand each option carefully before deciding what to do. A quick summary follows:
- PACKAGE: man-db
- Should man and mandb be installed 'setuid man'? -> No
- PACKAGE: resolvconf
- Prepare /etc/resolv.conf for dynamic updates? -> Yes
- Append original file to dynamic file? -> No
Once all the standard software is installed, we need to configure resolvconf service. First, the service needs to be started and enabled. To do so, please execute the following:
systemctl start resolvconf.service
systemctl enable resolvconf.service
systemctl start resolvconf-pull-resolved.service
systemctl enable resolvconf-pull-resolved.service
cat <<-EOF > /etc/resolvconf/resolv.conf.d/head
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF
systemctl restart resolvconf.service
systemctl restart systemd-resolved.service
systemctl restart resolvconf-pull-resolved.service
resolvconf -u
Development Tools
aptitude install -R -y linux-headers-amd64 build-essential
Additional Software
aptitude install -R -y safecat sharutils lynx zip unzip lrzip pbzip2 p7zip p7zip-full rar pigz unrar acpid \
zstd inotify-tools sysfsutils dstat htop lsscsi iotop nmap ifstat iftop tcptrack whois atop sysstat gpm \
localepurge mc screen vim ethtool apt-file sysdig net-tools sudo wget bsd-mailx dma pwgen
Same as with standard software, at this point, you will be asked a few questions about the configuration of the software you're installing. Please read and try to understand each option carefully before deciding what to do. A quick summary follows:
- PACKAGE: gpm
- Do you want to (re)start GPM while X is running? -> No
- Mouse device for GPM: /dev/input/mice
- Mouse type: exps2
- Mouse responsiveness: leave empty
- Protocol to use for repeating mouse events: none
- Mouse sample rate: leave empty
- Additional arguments for the GPM daemon: leave empty
- PACKAGE: dma
- System mail name: leave default
- Smarthost: leave empty
- PACKAGE: localepurge
- Locale files to keep on this system: Select only en and en_US.UTF-8
- Use dpkg --path-exclude? -> Yes
- Also delete localized man pages? -> Yes
- PACKAGE: sysstat
- Activate sysstat's cron job? -> No
There is one Debian version-related exception here; netcat. To install it on Debian 11, please execute the following:
aptitude install -R -y netcat
aptitude install -R -y netcat-openbsd
apt-file update
sed -i 's/^#startup_message/startup_message/g' /etc/screenrc
Server Access
Local Root Access
ssh-keygen -b 4096 -t rsa -f /root/.ssh/id_rsa -q -N ""
The command above will create a private (/root/.ssh/id_rsa) and public (/root/.ssh/id_rsa.pub) portion of the key. You can use your public key for access authorization, but you should never share your private key with anyone.
To control which user(s) can access the server (and potentially from where), we need to set a control file where we will allow access. To do so, please execute the following:
touch /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
echo -e "from=\"127.0.0.1\" $(cat /root/.ssh/id_rsa.pub)\n" >> /root/.ssh/authorized_keys
Remote Access
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication yes/g' /etc/ssh/sshd_config
systemctl restart ssh
Now you can add the public key of each user who needs root access to the server. You will add those keys in /root/.ssh/authorized_keys control file.
To do it, you need to know the public IP address of your server. While it is easy to determine that on servers with only one active interface, it may be a bit more tricky if your server has multiple active interfaces. In such a case, maybe the best approach would be to determine a default gateway and the IP address used to communicate with that gateway. To do so, please execute the following:
ip route get "$(ip route show 0.0.0.0/0 | grep -oP 'via \K\S+')" | grep -oP 'src \K\S+'
The command from the example above will give you the correct server IP address. Once you have it, you can add your user's public key to the server. Bare in mind please here, you will need your root password to complete this step.
If your client machine is running Windows, you will first have to enable SSH Client. You can do it by going to Settings -> Apps -> Optional features. There you will find OpenSSH Client. Click on it and install it. Now you can execute the following command in the PowerShell (please replace $MACHINE_IP with the IP address from the ip route get result from above):
type C:\Users\$Env:USERNAME\.ssh\id_rsa.pub | ssh root@$MACHINE_IP -T "cat >> /root/.ssh/authorized_keys"
cat /home/$(whoami)/.ssh/id_rsa.pub | ssh root@$MACHINE_IP -T "cat >> /root/.ssh/authorized_keys"
sed -i 's/^PermitRootLogin yes/PermitRootLogin without-password/g' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config
systemctl restart ssh
Server Time Sources
A lot of services and software you may potentially install on your server depend on the correct time. That said, we need to tell our server which sources it can rely on to get the right time. Also, later in this guide, when we configure the basic firewall, we will allow our server to communicate explicitly with the time servers we configure in this step.
We are going to use a local /etc/hosts file to map network time server hostnames with their respective IP addresses. First, we need to define a couple of variables that will fetch those IP addresses:
POOL_NTP_0=$(dig +short 0.pool.ntp.org | head -n1)
POOL_NTP_1=$(dig +short 1.pool.ntp.org | head -n1)
POOL_NTP_2=$(dig +short 2.pool.ntp.org | head -n1)
POOL_NTP_3=$(dig +short 3.pool.ntp.org | head -n1)
echo -e "$POOL_NTP_0 0.debian.pool.ntp.org" >> /etc/hosts
echo -e "$POOL_NTP_1 1.debian.pool.ntp.org" >> /etc/hosts
echo -e "$POOL_NTP_2 2.debian.pool.ntp.org" >> /etc/hosts
echo -e "$POOL_NTP_3 3.debian.pool.ntp.org" >> /etc/hosts
Basic Firewall
Default Firewall Chains
Many system administrators have a nasty habit of stuffing all firewall rules in the INPUT firewall chain. This is a terrible practice that can lead to a completely unmanageable firewall. To avoid this, we will set two default firewall chains; one where we will add our basic rules and one that will reject all other traffic.
To do so, we will first flush all firewall chains:
iptables -F INPUT
iptables -F FORWARD
iptables -F OUTPUT
iptables -N GENERAL-ALLOW
iptables -N REJECT-ALL
To continue with this, let me first explain a bit about how iptables firewall works. When an incoming connection hits the server, it is passed to the INPUT chain of the firewall. There it is matched to rules set in that chain (in the order they are set) until it matches the rule that will allow connection to the server. If there is no rule it can be matched against, the connection is dropped (unable to connect to the server).
In our case, the same will happen, but a bit differently. Incoming connections will be passed to the INPUT chain, but we will create a rule that will immediately forward (-j) that connection to the GENERAL-ALLOW chain we created in the previous step. Execute the following to create such a rule:
iptables -I INPUT -m comment --comment "No rules of any kind below this rule" -j GENERAL-ALLOW
iptables -A INPUT -j REJECT-ALL
Basic Firewall Rules
iptables -I GENERAL-ALLOW -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "Established Connections" -j ACCEPT
iptables -A GENERAL-ALLOW -p tcp -m tcp --dport 22 --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "SSH Access" -j ACCEPT
iptables -A GENERAL-ALLOW -s 8.8.4.4/32 -p udp -m udp --sport 53 -m comment --comment "Google DNS UDP" -j ACCEPT
iptables -A GENERAL-ALLOW -s 8.8.4.4/32 -p tcp -m tcp --sport 53 -m comment --comment "Google DNS TCP" -j ACCEPT
iptables -A GENERAL-ALLOW -s 8.8.8.8/32 -p udp -m udp --sport 53 -m comment --comment "Google DNS UDP" -j ACCEPT
iptables -A GENERAL-ALLOW -s 8.8.8.8/32 -p tcp -m tcp --sport 53 -m comment --comment "Google DNS TCP" -j ACCEPT
iptables -A GENERAL-ALLOW -s $POOL_NTP_0/32 -p udp -m udp --sport 123 -m comment --comment "NTP Pool Servers" -j ACCEPT
iptables -A GENERAL-ALLOW -s $POOL_NTP_1/32 -p udp -m udp --sport 123 -m comment --comment "NTP Pool Servers" -j ACCEPT
iptables -A GENERAL-ALLOW -s $POOL_NTP_2/32 -p udp -m udp --sport 123 -m comment --comment "NTP Pool Servers" -j ACCEPT
iptables -A GENERAL-ALLOW -s $POOL_NTP_3/32 -p udp -m udp --sport 123 -m comment --comment "NTP Pool Servers" -j ACCEPT
iptables -A GENERAL-ALLOW -p icmp -j ACCEPT
iptables -A GENERAL-ALLOW -i lo -j ACCEPT
iptables -A REJECT-ALL -p tcp -j REJECT --reject-with tcp-reset
iptables -A REJECT-ALL -p udp -j REJECT --reject-with icmp-port-unreachable
iptables -A REJECT-ALL -p icmp -j DROP
aptitude install -R -y iptables-persistent
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
Finishing Touches
Vim Editor
There are a lot of editors you can use on your Debian server, and I like Vim. Vim allows users to set a configuration file (per user), where various options like tab spaces, indentation, highlighting, and others can be set. I like to set a location for backup files and a few tab space related options.
I like the option of having a backup of the original file while I'm editing that same file. For that, I need to create a directory for such backup files:
mkdir -p /root/.vim/saves
cat <<-EOF > /root/.vimrc
set tabstop=4
set softtabstop=4
set expandtab
set shiftwidth=4
set backupdir=~/.vim/saves/
set mousemodel=popup
syntax on
EOF
Custom .bashrc
By default, Debian runs Bash shell, and Bash will run the .bashrc script every time it is started interactively. You can think of the .bashrc file as a configuration file for your interactive shell. You can set in it how your prompt will look, history settings, colorization settings, aliases, and many more.
Since the default .bashrc file supplied with Debian has very few options, and potential options vary from user to user (or, better say, system to system), a whole new guide would be required to cover the topic. That said, you can explore the topic on Google, or you can check my example in the GitHub repository for the script which automatizes everything we did in this guide (more on that later).
Changes Log
Since this is a server environment, there is a possibility that more than one user will be able to log in to your server. To make them all "behave" and not mess up your nice server, some ground rules must be communicated to them all.
Now, Debian (and other Debian-based distros) has a really nice "service" called motd (message of the day). It is using pam_motd to print system and user-generated messages on every login:
printf "\n"
echo "$(tput -T xterm setaf 2)--- $(hostname)$(tput -T xterm sgr0) $(tput -T xterm setaf 1)**GENERAL**$(tput -T xterm sgr0) $(tput -T xterm setaf 2)changes log --- $(tput -T xterm sgr0)"
printf "\n"
echo "$(tput -T xterm setaf 2) * Please append this file ($(tput -T xterm sgr0) $(tput -T xterm setaf 1)/etc/update-motd.d/20-asset-log$(tput -T xterm sgr0) $(tput -T xterm setaf 2)) for any system changes made. $(tput -T xterm sgr0)"
echo "$(tput -T xterm setaf 2) * Please run$(tput -T xterm sgr0) $(tput -T xterm setaf 1)iptables-persistent$(tput -T xterm sgr0) $(tput -T xterm setaf 2)if doing any changes to firewall. $(tput -T xterm sgr0)"
echo "$(tput -T xterm setaf 2) * Please add any new custom service/system script in /usr/local/sbin. This is mandatory! $(tput -T xterm sgr0)"
echo "$(tput -T xterm setaf 2) * Please set up log rotation (if required) for any new custom service/system script. This is also mandatory! $(tput -T xterm sgr0)"
printf "\n"
echo "$(tput -T xterm setaf 2)--- $(hostname)$(tput -T xterm sgr0) $(tput -T xterm setaf 1)**GENERAL**$(tput -T xterm sgr0) $(tput -T xterm setaf 2)changes log end --- $(tput -T xterm sgr0)"
printf "\n"
chmod 755 /etc/update-motd.d/20-changes-log
Post-install Cleanup
apt autoremove -y
aptitude clean
aptitude autoclean
shutdown -r now