Host old apps in containers with Debian 12

I have recently had to start some software based on a very old PHP version. Since having that directly on the operating system will open it to vulnerabilities, containerization seems to be a good choice.

To accomplish this task we will use a VPS from DigitalOcean, also known as a droplet. We will start with steps to minimally harden the security of the droplet, then use Docker as that is the most popular option for containerization. Then we will integrate it with our hosting solution based on the popular Virtualmin.

We will assume that Virtualmin is already setup on your other server. Some help for that you might find here:

Read more:

Improve the operating system

We made these steps many times on this site until now. With only slight variations, the steps to follow will be the same. And every good thing starts with an update: 🙂

  • sudo apt update && sudo apt upgrade -y

Read more:

Looks and performance

zsh

We will start by making the prompt prettier:

  • sudo apt-get install zsh git
  • sudo wget -O /usr/local/bin/install_antigen https://raw.githubusercontent.com/cristidraghici/debian-server-bash-scripts/master/install_antigen.sh && sudo chmod +x /usr/local/bin/install_antigen
  • install_antigen
  • Maybe remember to update /etc/passwd to use zsh as your default shell.

Swap

We will also create a swap file, especially if we are not using SSDs:

  • sudo fallocate -l 4G /swapfile || dd if=/dev/zero of=/swapfile bs=1024 count=$((4*1024*1024))
  • sudo chmod 600 /swapfile
  • sudo mkswap /swapfile
  • sudo swapon /swapfile
  • echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab

Locales

We also need to clarify the locales:

  • sudo apt install locales
  • sudo dpkg-reconfigure locales

Security

sudoer

We will continue with adding a sudo user:

  • adduser sudouser
  • usermod -aG sudo sudouser
  • passwd -l root
  • su - sudouser

Then we will generate our SSH key:

  • mkdir ~/.ssh
  • touch ~/.ssh/authorized_keys
  • sudo apt update && sudo apt install openssh-client
  • ssh-keygen
  • chmod 700 ~/.ssh
  • chmod 400 ~/.ssh/id_rsa
  • chmod 600 ~/.ssh/authorized_keys

The next step will be to setup the access via an ssh key:

  • cat ~/.ssh/id_rsa.pub # on your own system to get your public key
  • nano ~/.ssh/authorized_keys
  • paste the content of your key and save with Ctrl+X

Ports

Another thing is to change the ssh port to a different one than the default:

  • sudo sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
  • sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
  • sudo systemctl restart sshd

fail2ban

Fail2ban is an open-source security tool designed to protect servers from brute-force attacks and unauthorized access by monitoring system logs for suspicious activity, such as repeated failed login attempts.

  • sudo apt-get install fail2ban -y
  • sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
  • sudo systemctl start fail2ban
  • sudo systemctl enable fail2ban

Firewall

One last thing will be to add the ufw firewall, but first we have to make sure firewalld is removed:

  • sudo systemctl stop firewalld
  • sudo systemctl disable firewalld
  • sudo apt purge firewalld

Now we add ufw:

  • sudo apt-get install ufw -y
  • sudo ufw default deny incoming
  • sudo ufw default allow outgoing
  • sudo ufw allow OpenSSH # opens port 22
  • sudo ufw allow in "WWW Full"
  • sudo ufw allow 2222/tcp # use the port set for your ssh server
  • sudo ufw allow from 192.168.0.0/16 to any port 22
  • yes | sudo ufw enable

To remove a rule:

  • sudo ufw status verbose
  • sudo ufw status numbered
  • sudo ufw delete <number to delete>

Once you do changes, make sure to reload ufw:

  • sudo ufw reload

Read more:

Containerization

There are multiple ways we can go to create and run the containers: docker, podman, containerd.

Docker installation

The most used choice for containerization is Docker, which is the go to solution when it comes to containerization, often going hand in hand with the docker-compose.

First of all, install the dependencies and add the repository to the OS:

  • sudo apt install -y ca-certificates curl gnupg
  • sudo install -m 0755 -d /etc/apt/keyrings
  • curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  • sudo chmod a+r /etc/apt/keyrings/docker.gpg
  • echo \ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Then we can actually install docker and docker-compose and add ourselves to the docker group, so that we won’t have to use sudo. Make sure you remember to logout and login again.

  • sudo apt update
  • sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
  • sudo systemctl enable --now docker
  • sudo usermod -aG docker $USER
  • sudo apt install -y docker-compose-plugin

We can remove docker with the following commands:

  • sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin
  • sudo rm -rf /var/lib/docker
  • sudo rm -rf /etc/apt/keyrings/docker.gpg
  • sudo rm /etc/apt/sources.list.d/docker.list

More info:

Firewall rules

This might not be obvious, but by default docker overrides the rules enforced by ufw.

We can fix this situation by doing the following:

  • sudo nano /etc/docker/daemon.json
  • add the following json and save: { "iptables": false }
  • sudo systemctl restart docker

Be aware that this solution will isolate docker from the outside world. Among others, this means that you won’t be able so solve DNS inside a Dockerfile and thus access outside resources (e.g. run the apt update command successfully).

Application

As mentioned in the title, we are setting up an old php application on its own server. Obviously, we will use git and secrets and other tools which are now popular in an application’s lifecycle.

One important security consideration is that we will use the internal IP addresses provided by digitalocean. This way, even if we open some ports, the attacker will have to be inside our network to be able to exploit possible vulnerabilities.

Webmin

Webmin goes hand in hand with Virtualmin. While the latter is specially designed to deal with hosting websites, the former deals with your base operating system.

What we need to do here is add some modules to Apache:

  • Navigate to Webmin > Servers > Apache Webserver
  • Go to Configure Apache Modules, and enable the following modules (if not already): proxy, proxy_http, proxy_connect, proxy_ajp (optional), then click Enable Selected Modules
  • Restart Apache: sudo systemctl restart apache2

More info:

Virtualmin

Virtualmin will be responsible for acting like a proxy to the VPS containing the older PHP 7 version. Assuming our host is already created, we need to go to Virtualmin > Your host > Services > Configure Website > Edit Directives and apply the following rules:

ProxyPreserveHost On
ProxyPass / http://192.168.1.100:81/
ProxyPassReverse / http://192.168.1.100:81/Code language: Apache (apache)

Make sure it’s inside the <VirtualHost _:80> block (or \_:443 for HTTPS), or at root level if the VirtualHost tag is not visible.

Obviously, we assume the PHP 7 host has the internal ip of 192.168.1.100 and it runs on port 81.

Mysql

For storing the data, we have multiple choices we can use:

  • use directly docker with a mysql volume for data persistency;
  • use the virtualmin database via a port connection;
  • connect via ssh to the virtualmin host and use the mysql database there;
  • still use ssh, but inside the docker container.

Remember to set the proper permissions for your key:

  • chmod 600 ~/.ssh/id_rsa

More information: