Objective
Learn how install Apache on Ubuntu 18.04, how to configure virtual hosts, setup the firewall and use ssl certificates for a secure connection
Requirements
- Root permissions
Conventions
- # – requires given linux commands to be executed with root privileges either
directly as a root user or by use ofsudo
command - $ – requires given linux commands to be executed as a regular non-privileged user
Introduction
The Apache web server doesn’t need big presentations: open source software, released by the Apache foundation, is one of the most used web servers in the world. In this tutorial, we will see how to install it, adjust the firewall configuration to allow http and https traffic, and setup virtual hosts on Ubuntu 18.04.
Installation
Installing the Apache web server on Ubuntu 18.04 Bionic Beaver is a really straightforward process:
$ sudo apt-get update && apt-get install apache2
Ubuntu installation scripts will take care of starting and enabling the apache2
service at boot.
Firewall setup
To access the default content served by Apache, on the same machine the server is running on, we’ll just have to fire up a web browser and navigate to localhost
in the address bar. If all is setup correctly, a page should welcome us with the “it works!” message:
If a firewall is enabled on our system (like it should), to make the content accessible from outside our machine, we need to allow incoming traffic on port 80
. The command to run depends on the firewall manager in use. For example, when using ufw
(Ubuntu’s default), we must run:
$ sudo ufw allow http
Similarly, if using firewalld
, we can run:
$ sudo firewall-cmd --permanent --add-service=http && firewall-cmd --reload
Notice that the above command will have its effect on the default firewalld zone. If we want to operate on another one, we must specify it with the --zone
option.
Configuring a virtual host
The apache web server has the ability to run more than one website on the same machine. Each site (a virtual host in the apache terminology) that should be served must have its own configuration. A virtual host can be ip or named based.
In this tutorial we will focus on the second type, since it is the easier to setup and doesn’t require multiple ip addresses (name-based virtual hosts allows many websites to share the same address).
The default virtual host
On Ubuntu, the default virtual host is defined into the /etc/apache2/sites-available
directory, inside the 000-default.conf
file. Let’s take a look at it:
<VirtualHost *:80>
[...]
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
[...]
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
[...]
</VirtualHost>
The <VirtualHost>
directive on Line 1 is used to group the settings used by apache for a specific virtual host. The first thing we saw defined in it, is the *:80
instruction. This indicates the ip address and port used by the virtual host.
Multiple virtual hosts can be defined in the same file or by following the “one virtual host definition per file” scheme. In both cases the first definition is considered to be the default, if no other virtual host is matched by the client request.
The ServerAdmin
directive on Line 3is optional, and it is used to specify the contact address that the web server will show in case of error messages. Normally we want to provide a valid email address as the argument of this directive, since the web server will use mailto:
on it, to make contacting the administrator easier.
DocumentRoot
on Line 4is mandatory and it’s essential for the virtual host configuration. The argument to this instruction must be a valid filesystem path. The directory provided will be considered the root directory of the virtual host, and must not contain a trailing ‘/’. In this case, the document root directory it’s /var/www/html
. If we take a look at its content, we see that it contains the index.html
page used as the server welcome page we saw before.
The last two instructions on Line 8 – 9provided in this virtualhost are ErrorLog
and CustomLog
. By using the first one, we set the file to which the server will log the occurring errors. The second, instead is used to log the requests sent to the server in the specified format (you can use this as a reference for an in-depth knowledge about log formats).
Create a new virtual host
We saw how the default virtualhost is defined; now suppose we want to serve another website using our webserver: we must define a new virtual host to be able to accomplish our goal.
As said above, virtual hosts files must be defined inside the /etc/apache2/sites-available
directory (at least in debian-based distributions): therefore we will create our file there. Before doing that, we want to create the directory to be used as our document root
, and create a basic page to be displayed when we reach the site:
$ sudo mkdir /var/www/example && echo "Welcome to example!" > /var/www/example/index.html
We can now proceed on configuring our virtual host:
<VirtualHost *:80> DocumentRoot /var/www/example ServerName www.example.local </VirtuaHost>
This is the minimal configuration needed to run a virtual host. Here we can see a new directive, ServerName
: This is what defines our virtual host. Let’s save this file as example.conf
. To activate our virtualhost we use the a2ensite
command: all this command does is create a symlink of the file into the /etc/apache2/sites-enabled
directory:
$ sudo a2ensite example.conf
After that, we must reload the server configuration:
$ sudo systemctl reload apache2.service
We defined our virtualhost, however since this is a test and we don’t have a dns entry
associated with it, to verify that the configuration works we must add an entry in the /etc/hosts
file of the machine we are trying to reach the site from.
$ sudo echo "192.168.122.241 www.example.local" >> /etc/hosts
Without this line, (and without a DNS entry) would be impossible to associate the address of the server to the name of our virtualhost, and using the server ip directly, would instead “trigger” the default virtual host.
From the client machine, if we now navigate to “www.example.local” we should see the minimal page we setup above:
Setting up ssl
Ssl, short for Secure Sockets Layer
it’s the technology which allows us to encrypt the data involved in the connection between the client and the server. When ssl certificates are used, https
(Hyper Text Transfer Protocol Secure) replaces http in the url.
Ssl certificates are issued by a certificate authority, which, as a trustful third party, assures that someone is really who claims to be on the internet. Ssl certificates can be very expensive, however there are two main alternatives to obtain a certificate: create a self signed certificate or get one from Let's encrypt
.
Generate a self-signed ssl certificate
Although generating a self-signed certificate it’s not a difficult task and can be useful when you just want to achieve encryption, it is not usable in contexts where the certificate itself must be signed by a trusted third party. We can generate a self-signed certificate by using the openssl
utility:
$ sudo openssl req -x509 \ -days 365 \ -sha256 \ -newkey rsa:2048 \ -nodes \ -keyout example.key \ -out example-cert.pem
Let’s see what this command does. The first option we encounter, -x509
, modifies the behavior of the command so that the it generates a self-signed certificate instead of a certificate request.
With -days
, we set the validity, in days, for the certificate. The next provided option is -newkey
: with it we create a new key, in this case an rsa
key, with the size of 2048 bits. For our test case, we don’t want to encrypt the private key file, so we used -nodes
. If this option is omitted, the file in which the key is stored will be protected by a password, which we will be prompted to insert each time the web server is restarted.
With -keyout
and -out
we specify the file to write the generated key and the certificate, respectively. When launching the command, we will be prompted to answer some questions, and then the key and the certificate will be generated.
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:IT State or Province Name (full name) [Some-State]: Locality Name (eg, city) []:Milan Organization Name (eg, company) [Internet Widgits Pty Ltd]:Damage Inc. Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:www.example.local Email Address []:
Next step is to copy our generated key and certificate into /etc/ssl/private
and /etc/ssl/ssl-certs
directories respectively:
$ sudo mv example-cert.pem /etc/ssl/certs
The certificate is public, so needs no special permission. Now, the key:
$ sudo mv example.key /etc/ssl/private
It’s important that we adjust the key file permissions. If we examine the /etc/ssl/private
folder, we can see that it belongs to the root
user and the ssl-cert
group, and it has 710
as permissions, meaning that while the owner has full privileges on it, the group owner can only access it and list its content, and no permission are allowed for others:
$ ls -ld /etc/ssl/private drwx--x--- 2 root ssl-cert 4096 Mar 16 11:57 /etc/ssl/private
Let’s change our key file permissions accordingly, giving the owner reading and writing permissions, and read-only privileges for the group:
$ sudo chown root:ssl-cert /etc/ssl/private/example.key $ sudo chmod 640 /etc/ssl/private/example.key
To use our certificate, we now have to enable the ssl apache module. We do it by using the a2enmod
command:
$ sudo a2enmod ssl
We are almost there. Now it’s time to modify our virtual host and set it this way:
<VirtualHost *:443>
DocumentRoot /var/www/example
ServerName www.example.local
# Enable ssl engine
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example-cert.pem
SSLCertificateKeyFile /etc/ssl/private/example.key
</VirtualHost>
The port 443
on Line 1is the port used for https (in place of port 80 used for http). We also added the SSLEngine on
instruction on Line 6, which is pretty self-explanatory.
Finally on Line 8 – 9 we have specified the paths for our certificate and key files, using the SSLCertificateFile
and SSLCertificateKeyFile
instructions.
Now, follow the instuctions to open firewall ports used at the beginning of the tutorial, but this time to allow the https
service:
$ sudo ufw allow https
Finally, reload apache configuration:
$ sudo systemctl reload apache2
All done. Now, if from the client, we navigate to https://www.example.local
address, we should see the web server alerting us that the used certificate is not secure (since it’s self trusted). This is, however the sign the our setup works and the traffic between the client and the server will be encrypted (you will need to add an exception for the certificate to use it).
Setting up Let’s encrypt
The alternative to commercial and self-signed certificates it’s represented by “Let’s encrypt”. Let’s encrypt is a free, automated and open certificate authority; its goal is to make possible to automatically obtain a certificate trusted by the browser without any human intervention.
This can be achieved by the use of the ACME
protocol and a certificate management agent
which runs on the server.
To obtain a certificate we must demonstrate we have control over the domain we want to use the certificate for. If we don’t have shell access on the server, we should contact our service provider to activate let’s encrypt on our behalf, but probably there is a dedicated section in the service configuration panel.
If, instead, we do have shell access to the server in question, first of all we must install the certbot
ACME client. Installing certbot on Ubuntu 18.04 it’s just matter of running:
$ sudo apt-get update && apt-get install certbot python-certbot-apache
The certbot package comes with a systemd timer
unit which will run certbot twice a day to keep the certificate up to date. Obtaining a certificate is quite simple:
$ sudo certbot --apache -m <administrator-email> -d <domain>
Obviously for this to work the domain must point correctly to our publicly accessible server ip. Certbot will prompt you for some questions to tweak the configuration, and if all goes well, the certificate and key should will saved into the /etc/letsencrypt/live/
directory. Just tweak your virtual host file to point to those and you are done!