Server setup

In this article you will find some suggestions on how to setup a VPS properly. I will assume that you have installed Ubuntu server or Debian.

You can get a small and cheap VPS from:

The latest version of this article is at:
https://docker-scripts.gitlab.io/server-setup.html

1. Basic setup

1.1. Update packages

After the first login, usually you want to update and upgrade the installed packages:

apt update
apt upgrade --yes

apt install vim tmux git

# do also a reboot, in case the kernel is updated
reboot

1.2. Set a better prompt

The default prompt is usually dull and boring, and a nice prompt makes your work easier. So let’s try to improve it:

# customize ~/.bashrc
sed -i ~/.bashrc \
    -e '/bashrc_custom/d'
echo 'source ~/.bashrc_custom' >> ~/.bashrc

cat <<'EOF' > ~/.bashrc_custom
# set a better prompt
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;36m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
EOF

We have created the file ~/.bashrc_custom, which we source (include) at the end of ~/.bashrc.

1.3. Enable colorized ls

In Debian we should enable colorized ls output (on Ubuntu it is enabled by default). Edit ~/.bashrc and uncomment ls aliases.

sed -i ~/.bashrc \
    -e 's/^# export LS_OPTIONS/export LS_OPTIONS/' \
    -e '/dircolors/ s/^# eval/eval/' \
    -e 's/^# alias ls=/alias ls=/' \
    -e 's/^# alias ll=/alias ll=/' \
    -e 's/^# alias l=/alias l=/'

1.4. Enable bash-completion

Strange, but on an Ubuntu server it is not enabled by default (in Debian it is already enabled).

# make sure that bash-completion is installed
apt install --yes bash-completion

cat <<'EOF' >> ~/.bashrc_custom
# enable programmable completion features
if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
    source /etc/bash_completion
fi
EOF

1.5. Change the hostname

Edit /etc/hostname, change the hostname, and reboot.

Instead of rebooting you can also run hostname newhostname, then exit and re-login.

1.6. Fix configuration

In Linux everything is a file, and if you install lots of containers and applications, the number of files that need to be opened increases. Make sure that the limit of open files is not small:

cat /proc/sys/fs/file-max
echo 9223372036854775807 > /proc/sys/fs/file-max

To make this change permanent, edit the file /etc/sysctl.conf and append a line like this:

fs.file-max = 9223372036854775807

Then enable it with sysctl -p.

2. Secure the server

2.1. Install firewalld and fail2ban

Let’s try to protect the server from the attacks.

# install firewalld
apt install --yes firewalld
firewall-cmd --list-all
firewall-cmd --permanent --zone=public --set-target=DROP
firewall-cmd --reload

# install fail2ban
apt install --yes fail2ban
fail2ban-client status
fail2ban-client status sshd

Their default configuration is usually fine, so for the time being we don’t need to change anything.

2.2. Disable password login

If you are using a password to login to the server, it is strongly recommended to enable key-based login and to disable password login.

Generate SSH key

Generate e SSH key pair on the server, like this:

ssh-keygen --help
ssh-keygen -t ecdsa -q -N '' -f server-admin

It will create the files server-admin (the private key) and server-admin.pub (the public key). The option -N '' tells to the command ssh-keygen to generate a key pair without password.

We want to append the public key to ~/.ssh/authorized_keys, but first let’s make sure that the directory exists and has propper permissions:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
cat server-admin.pub >> ~/.ssh/authorized_keys
rm server-admin.pub

Login with private key

We want to transfer the private key to our local machine (laptop). We can just copy/paste its content or we can use scp like this:

scp root@10.11.12.13:~/server-admin .
chmod 600 server-admin
ssh root@10.11.12.14 rm server-admin

Now let’s try to login using this private key:

ssh -i server-admin root@10.11.12.13

You should be able to login without a password.

To make things easier, let’s create a configuration file on ~/.ssh (on the local machine):

touch ~/.ssh/config
chmod 600 ~/.ssh/config

cat >> ~/.ssh/config <<EOF
Host server1
    HostName 10.11.12.13
    Port 22
    User root
    IdentityFile ~/.ssh/server1.key
    IdentitiesOnly yes
EOF

mv server-admin ~/.ssh/server1.key
chmod 600 ~/.ssh/server1.key

Now you should be able to login to the server just by giving ssh server1. You don’t need to remember the IP of the server, the port, the identity file (private key), etc. It’s so convenient!

Disable password login

Now that we can login with an identity file (private key), we can disable the password login on the server, to make it more secure. Someone may guess a password, or may find it by a brute force attack (trying lots of passwords), but it is almost impossible to guess or find a private key.

Edit the file /etc/ssh/sshd_config on the server and make sure to change the setting PasswordAuthentication from yes to no:

#PasswordAuthentication yes
PasswordAuthentication no

Save the file and restart the sshd service:

systemctl restart sshd

Make sure that you can still login with the private key.

Test also that you cannot login with a password anymore.

2.3. Change the SSH port

This is another step for making the server a bit more secure.

  • Edit /etc/ssh/sshd_config on the server and change the port from 22 to something else (for example with 4 or 5 digits), like this:

    #Port 22
    Port 1234
  • Open the new port in the firewall:

    firewall-cmd --zone=public --add-port=1234/tcp
  • Restart the SSH service

    systemctl restart sshd
  • Change the port in ~/.ssh/config on the local machine and test that you can still login to the server.

  • Make the firewall change permanent:

    firewall-cmd --permanent --zone=public --add-port=1234/tcp
    firewall-cmd --permanent --zone=public --remove-service=ssh

2.4. Use a script to login

The configuration file ~/.ssh/config is convenient, but if you want to login to the server from anywhere, you need something more portable. In this case you can use a script like this:

#!/bin/bash

server=10.11.12.13
port=1234

keyfile=$(mktemp)
sed -n -e '/^----/,/^-----/p' $0 > $keyfile

ssh -i $keyfile -p $port root@$server

rm -f $keyfile
exit 0

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRIRkXtvwyUiLDSLSqV0V0RClTakKDt
SkP/4besU++elsvtZaaY97GSdn0kTqF+0LiBCTOaEROgRHB7aKU8YjwjAAAAqJKniRSSp4
kUAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEhGRe2/DJSIsNIt
KpXRXREKVNqQoO1KQ//ht6xT756Wy+1lppj3sZJ2fSROoX7QuIEJM5oRE6BEcHtopTxiPC
MAAAAhAJXThzR7EhbYn9fykJaG5hUA4h+RCfIkpwo83yl+r/5qAAAADmRhc2hvQGRhc2hh
bWlyAQ==
-----END OPENSSH PRIVATE KEY-----

It includes the IP and the port along with the private key (identity file), so that you don’t have to remember them. If this script is called server1.sh, make sure to give it the right permissions, like this:

chmod 700 server1.sh

You may also consider using something like these scripts: https://gitlab.com/dashohoxha/server-scripts

3. Install docker

  1. Install packages that allow apt to use a repository over HTTPS:

    apt install --yes \
        ca-certificates \
        curl \
        gnupg \
        lsb-release
  2. Add Docker’s official GPG key:

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
        | gpg --dearmor \
              -o /usr/share/keyrings/docker-archive-keyring.gpg
  3. Create the file /etc/apt/sources.list.d/docker.list and add this line in it:

    deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]
            https://download.docker.com/linux/ubuntu focal stable
    Don’t break the line, here it is broken for readability.
  4. Install the docker engine:

    apt update
    apt install --yes \
        docker-ce \
        docker-ce-cli \
        containerd.io
    docker --version

3.1. Install docker-compose

base_url=https://github.com/docker/compose
release=$(basename $(curl -fs -o/dev/null -w %{redirect_url} $base_url/releases/latest))
curl -L "$base_url/releases/download/$release/docker-compose-Linux-x86_64" \
     -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version