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:
- https://www.debian.org/intro/why_debian
- https://www.digitalocean.com/ (with referral link)
- https://www.docker.com/
- https://www.virtualmin.com/
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 usezsh
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 keynano ~/.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 22sudo ufw allow in "WWW Full"
sudo ufw allow 2222/tcp
# use the port set for your ssh serversudo 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:
- https://linuxize.com/post/how-to-list-and-delete-ufw-firewall-rules/
- https://askubuntu.com/questions/115940/how-can-i-setup-ssh-so-that-it-is-restricted-to-my-local-network
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 clickEnable 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: