Setting up a VPS running on Debian with LAMP

Alexandru Calcatinge bio photo By Alexandru Calcatinge

Last year I purchased a CX20 virtual server from Hetzner.com, for some of my other online projects. If you are from Europe, there is a chance that you already heard about Hetzner. If not, you can pay them a visit and see what they offer.

hetzner_offers

Decisions

Now, I must tell you that, before deciding my online provider, I have created a short list that consisted of DigitalOcean, Linode, Vultr and Hetzner. I ended up going with Hetzner, as their offers get you more processing power/space/memory for the buck, compared to the others from the list. But, tutorials and how to’s for virtual server setup is where they fall short compared to the other players. Therefore, I decided to write a short how to article on setting up a virtual server based on Debian.

First, you have a large list of choices when it comes to the operating system installed on your virtual server. You can choose from CentOS 6.9 and 7.4, Debian 9.3, Ubuntu 16.04.3 and 17.10 and openSUSE 42.3 and, surprise surprise, Arch Linux.

hetzner_linux

As you may already know, I am an avid Fedora/openSUSE and Debian user, but I decided to go with Debian 9.1 LAMP (at the time of my purcase) pre-configured on the server, as there was no option, yet, for the latest version of openSUSE 42.3, and the support period for 42.2 is limited. Therefore, Debian all the way, as 9.1 will be supported until 2022, similar to an LTS release. Note that you can change your option afterwards, so you could install whatever system you want from the list above.

First steps

Now, after you get all the “paper work” done, and received your confirmation e-mail with your server setup and the team at Hetzner gives you the root login and password, you are good to go, somehow… You will log in into your account with their (very nicely named) robot, and you will have your first interaction with your new virtual server. To get everything working well from the start, you will need to get the Nameserver Robot (which is free for the vServer clients). After your request is processed, you will see that a new "DNS entries" option will be available under the "Main functions" menu. This is the place where you will create new DNS entries and nameservers for your future websites. Now, you will notice that a new DNS entry will cost you 0.59 Euros per year, which is a total bargain. Go for it.

First remote console, first issue: the keyboard layout

After accessing the "Server" entry in the "Main functions" menu, you will select your CX20 server and then you can select the “vServer” tab. There you will have controls over starting, stopping and rebooting your server, and down below you will see a link that says "Start remote console". This will be your first contact with the command line console on your server, and you should use the user and password credentials that you received in your e-mail from the Hetzner team. In my case, after logging in, I have noticed a very unusual problem - the keyboard layout was different than my actual keyboard: it was German. In order to overcome all the problems that this could bring, you will have to reconfigure your keyboard layout. In Debian, in order to do this, you will have to run the following command:

dpkg-reconfigure keyboard-configuration

This worked in my case, and an on-screen menu appeared, with very self explanatory options available to set a new layout, which in my case was US. After you do this, you will be good to go and confident that what you type is what you will see on the screen. If it doesn’t work in your case, which I doubt, you should run the command:

sudo apt install keyboard-configuration

Setting up and securing SSH

First, connect remotely to your server using ssh, like:

ssh root@IP_ADDRESS

You will use your credentials and once in, you should perhaps change the root password that was provided by Hetzner and use one that is secure enough and also easy to remember. For this you should use:

passwd

Now run a full update on the system with the commands:

apt update && apt upgrade

Create a new regular user using the command:

root@Debian-91-stretch-64-LAMP ~ # adduser alex
Adding user `ale' ...
Adding new group `alex' (1000) ...
Adding new user `alex' (1000) with group `alex' ...
Creating home directory `/home/alex' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for alex
Enter the new value, or press ENTER for the default
 Full Name []: Alexandru Calcatinge
 Room Number []:
 Work Phone []:
 Home Phone []:
 Other []:
Is the information correct? [Y/n] Y

Add the new user "alex" to the sudoers using the command, as root:

usermod -aG sudo alex

After that, check to see if alex is a member of sudo group using the command:

groups alex

By default, the Debian installation on the system does not have sudo installed, so you will have to install in (as root) using the command:

apt install sudo

For the changes to take effect and “alex” to be add to the sudo group, you will need to log out and than log back in into the server. For this, issue the command, as root:

shutdown -r now

Edit the SSH configuration file using:

Port 2222
PermitRootLogin no
X11Forwarding no
AllowUsers alex

vim /etc/ssh/sshd_config

and make the following changes:

Port 2222
PermitRootLogin no
X11Forwarding no
AllowUsers alex

Save the file and then restart the sshd service:

systemctl restart sshd

Now, in order for the new ssh port to be allowed by the Firewall, create a new iptables rule, as follows (as root):

iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 2222 -j ACCEPT

These rules just given, are resident in memory, have not been saved into the netfilter configuration file. For this, we could install netfilter-persistent package, with the command:

apt install netfilter-persistent

After installation, the new package asks if you would like to same the current iptables rules to a file called /etc/iptables/rules.v4, giving you some more info on how you could do this with the rules given after the installation of the package. Now, to test the configuration, exit the ssh connection and start a new one, using the new port and the new user name you just created. As a result, you will see that everything is just working fine:

alex@debian:~$ ssh -p 2222 alex@xx.xxx.xxx.xxx
alex@xx.xxx.xxx.xxx's password:
Linux Debian-91-stretch-64-LAMP 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u3 (2017-08-06) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

This server is running LAMP

Set up Apache virtual hosts

As LAMP is already installed on the system, I will not discuss the process of installing Apache or MariaDB or PHP, assuming that you already know how to do that, and did it already. Therefore, lets get right to setting up virtual hosts for Apache. I will create two virtual hosts, for two domains that I own: dummy1.ro and dummy2.ro (those are fictive names, I don’t really own these domains, as you could already guess).

Create directory structure

Connect to your server through ssh first, using the example from above. Once you are connected, explore the filesystem and go to the default document root which is in /var/www/html:

alexandru@Debian-91-stretch-64-LAMP:/var/www/html$ ls -l
total 12
-rw-r--r-- 1 root root 10701 Jul 3 08:43 index.html
alexandru@Debian-91-stretch-64-LAMP:/var/www/html$

Now go back to /var/www as your present working directory and in there, create two directories, for your two domains, as follows:

alexandru@Debian-91-stretch-64-LAMP:~$ pwd
/home/alexandru
alexandru@Debian-91-stretch-64-LAMP:~$ cd /var/www/
alexandru@Debian-91-stretch-64-LAMP:/var/www$ ls
html
alexandru@Debian-91-stretch-64-LAMP:/var/www$ sudo mkdir -p dummy1.ro/public_html
alexandru@Debian-91-stretch-64-LAMP:/var/www$ sudo mkdir -p dummy2.ro/public_html
alexandru@Debian-91-stretch-64-LAMP:/var/www$ ls
dummy1.ro html dummy2.ro
alexandru@Debian-91-stretch-64-LAMP:/var/www$

Grant permissions

The new directories created are owned by the root user, and thus we should change and grant permissions for the apache user, in order for it to be able to change content of those directories. For this we will use the following commands:

alexandru@Debian-91-stretch-64-LAMP:~$ sudo chown -R www-data:www-data /var/www/dummy2.ro/public_html/
alexandru@Debian-91-stretch-64-LAMP:~$ sudo chown -R www-data:www-data /var/www/dummy1.ro/public_html/
alexandru@Debian-91-stretch-64-LAMP:~$ sudo chmod -R 755 /var/www/

root@Debian-91-stretch-64-LAMP /home/alexandru # ls -l /var/www/
total 12
drwxr-xr-x 3 www-data www-data 4096 Sep 9 09:10 dummy1.ro
drwxr-xr-x 2 www-data www-data 4096 Jul 3 08:43 html
drwxr-xr-x 3 www-data www-data 4096 Sep 9 09:11 dummy2.ro

Create demo-pages

Right now, we can create demo pages for each virtual host, just until we will download and setup Drupal on each of them. For convenience, we will use the default index.html file in the /var/www/html directory. For this, we will copy that file in the respective directories.

alexandru@Debian-91-stretch-64-LAMP:~$ pwd
/home/alexandru
alexandru@Debian-91-stretch-64-LAMP:~$ ls
alexandru@Debian-91-stretch-64-LAMP:~$ cd /var/www/html/
alexandru@Debian-91-stretch-64-LAMP:/var/www/html$ ls
index.html
alexandru@Debian-91-stretch-64-LAMP:/var/www/html$ cp index.html /var/www/dummy1.ro/public_html/index.html
alexandru@Debian-91-stretch-64-LAMP:/var/www/html$ cp index.html /var/www/dummy2.ro/public_html/index.html

Those files will have to be removed after Drupal 8 is installed. Here are just for exemplification.

Create new virtual hosts files

We will use the Apache’s default virtual host file called 000-default.conf and edit that for our needs. First, lets start with the dummy1.ro domain.

First domain: dummy1.ro

1. Copy the file for the first domain

alexandru@Debian-91-stretch-64-LAMP:~$ sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/dummy1.ro.conf

2. Open the file in vim and start editing

alexandru@Debian-91-stretch-64-LAMP:~$ sudo vim /etc/apache2/sites-available/dummy1.ro.conf

3. Insert and edit the following in the file

<VirtualHost *:80>
 ServerAdmin openlark@gmail.com
 ServerName dummy1.ro
 ServerAlias www.dummy1.ro
 DocumentRoot /var/www/dummy1.ro/public_html
 ErrorLog ${APACHE_LOG_DIR}/error.log
 CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/dummy1.ro/public_html> 
Options FollowSymLinks 
AllowOverride All 
Order Allow,Deny 
Allow from All 
</Directory>
 #RewriteEngine On
 #RewriteCond %{HTTPS} !=on
 #RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
</VirtualHost>

Second domain: dummy2.ro

1. Copy the file used for the first domain and make it available for the second domain

alexandru@Debian-91-stretch-64-LAMP:~$ sudo cp /etc/apache2/sites-available/dummy1.ro.conf /etc/apache2/sites-available/dummy2.ro.conf

2. Open the file in vim and start editing

alexandru@Debian-91-stretch-64-LAMP:~$ sudo vim /etc/apache2/sites-available/dummy2.ro.conf

3. Edit the following text

<VirtualHost *:80>
 ServerAdmin openlark@gmail.com
 ServerName dummy2.ro
 ServerAlias www.dummy2.ro
 DocumentRoot /var/www/dummy2.ro/public_html
 ErrorLog ${APACHE_LOG_DIR}/error.log
 CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/dummy2.ro/public_html>
Options FollowSymLinks
AllowOverride All
Order Allow,Deny
Allow from All
</Directory>
 #RewriteEngine On
 #RewriteCond %{HTTPS} !=on
 #RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
</VirtualHost>

Enable the new virtual host files

Now that you created the virtual host files, all you have to do is to enable them, using apache commands.

alexandru@Debian-91-stretch-64-LAMP:~$ sudo a2ensite dummy1.ro.conf
Enabling site dummy1.ro.
To activate the new configuration, you need to run:
 systemctl reload apache2
alexandru@Debian-91-stretch-64-LAMP:~$ sudo a2ensite dummy2.ro.conf
Enabling site dummy2.ro.
To activate the new configuration, you need to run:
 systemctl reload apache2
alexandru@Debian-91-stretch-64-LAMP:~$ sudo systemctl reload apache2

Setting up mariaDB

As LAMP is already installed, it means that mariaDB is already installed on the system. Now, from the command line, I will run the script: mysql_secure_installation logged in as root. The only problem is that it asks for the root password. How can you find the root password for mysql? Well, remember first time you logged in via ssh, that you received a welcome message from the system? There is also a very short text that says:

This server is running LAMP

Thus, you can do a cat command on /passwords.txt to see the mysql root password. That password is quite difficult to remember, and thus you could change it. I did for mine.

In order to change the password, I first check the status of the service, then enter mysql as root user:

alexandru@Debian-91-stretch-64-LAMP:~$ systemctl status mariadb.service
● mariadb.service - MariaDB database server
 Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset:
 Active: active (running) since Sat 2017-09-09 09:07:20 CEST; 1h 14min ago
 Process: 1059 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_STAR
 Process: 1056 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUC
 Process: 932 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] && VAR=
 Process: 929 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_
 Process: 906 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run
 Main PID: 1024 (mysqld)
 Status: "Taking your SQL requests now..."
 Tasks: 27 (limit: 4915)
 CGroup: /system.slice/mariadb.service
 └─1024 /usr/sbin/mysqld
root@Debian-91-stretch-64-LAMP /home/alexandru # mysql -u root
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 28
Server version: 10.1.26-MariaDB-0+deb9u1 Debian 9.1

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

  Following, I will set a new password for the user root from within mysql, and then start the mysql_secure_installation script again.

MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> update user set password=PASSWORD("xxxxx") where User='root';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> exit;
Bye

root@Debian-91-stretch-64-LAMP /home/alexandru # systemctl restart mariadb.service
root@Debian-91-stretch-64-LAMP /home/alexandru # mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
 SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user. If you've just installed MariaDB, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.

You already have a root password set, so you can safely answer 'n'.

Change the root password? [Y/n] Y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
 ... Success!

By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] Y
 ... Success!

Normally, root should only be allowed to connect from 'localhost'. This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] Y
 ... Success!

By default, MariaDB comes with a database named 'test' that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] Y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] Y
 ... Success!

Cleaning up...

All done! If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

Create new mysql users and databases

The final purpose of this virtual sever setup is to host two websites that will be developed on Drupal 8, therefore, I will have to set up drupal databases for the two websites. Here is the output:

root@Debian-91-stretch-64-LAMP /home/alexandru # mysql -u root -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 10
Server version: 10.1.26-MariaDB-0+deb9u1 Debian 9.1

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE USER 'drupal_1'@'localhost' IDENTIFIED BY 'xxxxxx';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> CREATE DATABASE drupal_1;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> GRANT ALL PRIVILEGES on drupal_1.* TO 'drupal_1'@'localhost' IDENTIFIED BY 'xxxxxx';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> CREATE USER 'drupal_2'@'localhost' IDENTIFIED BY 'xxxxxx';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> CREATE DATABASE drupal_2;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> GRANT ALL PRIVILEGES on drupal_2.* TO 'drupal_2'@'localhost' IDENTIFIED BY 'xxxxxx';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> Bye

Download and setup Drupal 8

Now that all the important configurations have been made, it is time to download Drupal. We will use drupal to develop both websites, so I will show you for both cases.

For dummy1.ro

root@Debian-91-stretch-64-LAMP /var/www # cd /var/www/dummy1.ro/public_html/root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # ls
index.html
root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # wget https://ftp.drupal.org/files/projects/drupal-8.3.7.tar.gz
--2017-09-09 11:20:50-- https://ftp.drupal.org/files/projects/drupal-8.3.7.tar.gz
Resolving ftp.drupal.org (ftp.drupal.org)... 151.101.113.175
Connecting to ftp.drupal.org (ftp.drupal.org)|151.101.113.175|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12739663 (12M) [application/octet-stream]
Saving to: ‘drupal-8.3.7.tar.gz’

drupal-8.3.7.tar.gz 100%[===================>] 12.15M 18.4MB/s in 0.7s

2017-09-09 11:20:51 (18.4 MB/s) - ‘drupal-8.3.7.tar.gz’ saved [12739663/12739663]

root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # tar xzf drupal-8.3.7.tar.gz
root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # rm drupal-8.3.7.tar.gz
root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # ls
drupal-8.3.7 index.html
root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # ls -l
total 16
drwxr-xr-x 8 root root 4096 Aug 16 19:19 drupal-8.3.7
-rwxr-xr-x 1 www-data www-data 10701 Sep 9 09:33 index.html
root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # chown www-data:www-data -R drupal-8.3.7/
root@Debian-91-stretch-64-LAMP /var/www/dummy1.ro/public_html # chmod -R 755 drupal-8.3.7/

You will do the same for the second domain, just change the names accordingly.

Test your Drupal installation

To test the drupal installation, into your browser go to:

http://yourdomain.com/core/install.php

This will start the drupal installation process. Drupal installation page.

Harden your Apache with SSL

A good thing is to add SSL encryption to your webservers, and I will show you how. First of all, make sure that your firewall will allow requests through ports 80 and 443. For this, execute the following commands:

sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables-save

After that, review the virtual hosts files and make sure that they make no reference to port 443, which in our case, do not (just see above for this matter). Now, we will use the instructions on the official website, using certbot for the job.

sudo apt install python-certbot-apache

Then, run certbot as root:

root@debian ~ # certbot --apache
Saving debug log to /var/log/letsencrypt/letsencrypt.log

Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: dummy1.ro
2: www.dummy1.ro
3: dummy2.ro
4: www.dummy2.ro
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel):openlark@gmail.com

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf. You must agree
in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for dummy1.ro
tls-sni-01 challenge for www.dummy1.ro
tls-sni-01 challenge for dummy2.ro
tls-sni-01 challenge for www.dummy2.ro
Waiting for verification...
Cleaning up challenges

..........

Please choose whether HTTPS access is required or optional.
-------------------------------------------------------------------------------
1: Easy - Allow both HTTP and HTTPS access to these sites
2: Secure - Make all requests redirect to secure HTTPS access
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

............

-------------------------------------------------------------------------------
Congratulations! You have successfully enabled https://dummy1.ro,
https://www.dummy1.ro, https://dummy2.ro, and
https://www.dummy2.ro

Now, for information about renewal, see the following website.

Good Luck!