********************************
Step 3: Extending a Docker Image
********************************
.. include:: 4-urls.rst
.. contents:: Table of Contents
**Objective**: Create an SSL version of WordPress
Migrating WordPress to an SSL version requires two steps in the correct
sequence. Making the changes in the wrong order could require database
modifications to regain access to the site.
4.3.1. Change the WordPress URL to HTTPS
==========================================
Let's do this first so that you do not lock yourself out of your site.
#. Open the admin panel of your WordPress site.
Did you forget your password? You can reset it using
:ref:`wp_reset_password`.
#. Go to ``Settings -> General Settings``
#. Change the values to read ``https``
.. image:: images/updatewpurls.png
.. image:: images/updatewpurls_ru.png
At this point, you cannot access your site until you configure
an SSL transport layer.
4.3.2. Build the new WordPress Image
==========================================
Extending an existing image is simple. We don't have to rebuild
the entire project. We could clone the WordPress code from GitHub and
manually build the project using the base |WordPress Dockerfile|
for Apache, but we will let someone else maintain that Dockerfile.
We only need to base our image on |an official WordPress image|
of our choice. For this lab, we will use the latest Apache version
(``wordpress:apache``).
To enable SSL in an image, we need to:
#. Base our project from an existing image (``wordpress:apache``)
#. Enable the Apache modules required for SSL
#. Copy self-signed certificates
#. Build our custom image
We'll start by creating our project directory and files.
#. Create the directory for our image and other files.
.. code-block:: bash
cd ~
mkdir wordpress-docker-image
cd wordpress-docker-image
touch Dockerfile
#. The image needs self-signed SSL certificates to work. We will create
them using package ``make-ssl-cert``.
.. code-block:: bash
apt-get -y install ssl-cert
make-ssl-cert generate-default-snakeoil
# Verify the new SSL certificates exist.
ls -l /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/ssl/private/ssl-cert-snakeoil.key
Then, we need to copy the files to our current directory.
.. code-block:: bash
cp -p /etc/ssl/certs/ssl-cert-snakeoil.pem .
cp -p /etc/ssl/private/ssl-cert-snakeoil.key .
.. code-block:: bash
:caption: Output
:linenos:
:emphasize-lines: 1,21-26,28,30-32
root@vps298933:~/wordpress-docker-image# apt-get -y install ssl-cert
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
openssl-blacklist
The following NEW packages will be installed:
ssl-cert
0 upgraded, 1 newly installed, 0 to remove and 4 not upgraded.
Need to get 17.0 kB of archives.
After this operation, 64.5 kB of additional disk space will be used.
Get:1 http://fr.archive.ubuntu.com/ubuntu bionic/main amd64 ssl-cert all 1.0.39 [17.0 kB]
Fetched 17.0 kB in 0s (442 kB/s)
Preconfiguring packages ...
Selecting previously unselected package ssl-cert.
(Reading database ... 128224 files and directories currently installed.)
Preparing to unpack .../ssl-cert_1.0.39_all.deb ...
Unpacking ssl-cert (1.0.39) ...
Setting up ssl-cert (1.0.39) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
root@vps298933:~/wordpress-docker-image# make-ssl-cert generate-default-snakeoil
root@vps298933:~/wordpress-docker-image# ls -l /etc/ssl/certs/ssl-cert-snakeoil.pem /etc/ssl/private/ssl-cert-snakeoil.key
-rw-r--r-- 1 root root 1034 Nov 13 22:21 /etc/ssl/certs/ssl-cert-snakeoil.pem
-rw-r----- 1 root ssl-cert 1708 Nov 13 22:21 /etc/ssl/private/ssl-cert-snakeoil.key
root@vps298933:~/wordpress-docker-image# cp -p /etc/ssl/certs/ssl-cert-snakeoil.pem .
root@vps298933:~/wordpress-docker-image# cp -p /etc/ssl/private/ssl-cert-snakeoil.key .
root@vps298933:~/wordpress-docker-image#
root@vps298933:~/wordpress-docker-image# ls -lh
total 8.0K
-rw-r--r-- 1 root root 0 Nov 13 22:21 Dockerfile
-rw-r----- 1 root ssl-cert 1.7K Nov 13 22:21 ssl-cert-snakeoil.key
-rw-r--r-- 1 root root 1.1K Nov 13 22:21 ssl-cert-snakeoil.pem
root@vps298933:~/wordpress-docker-image#
#. We can now create our Dockerfile.
.. code-block:: dockerfile
:caption: Dockerfile
:linenos:
# The base image
FROM wordpress:apache
# Enabling the SSL module
RUN a2enmod ssl
# Copy the SSL certs to the image
RUN mkdir /etc/apache2/certs
COPY ssl-cert-snakeoil.pem /etc/apache2/certs/ssl-cert.pem
COPY ssl-cert-snakeoil.key /etc/apache2/certs/ssl-cert.key
#. Lastly, build and the new image. You can give any name, but this
example uses ``ssl-wordpress``.
.. code-block:: bash
docker build -t ssl-wordpress:latest .
.. code-block:: bash
:caption: Output
:linenos:
:emphasize-lines: 3,8,16,22,26,28,31
root@vps298933:~/wordpress-docker-image# docker build -t ssl-wordpress:latest .
Sending build context to Docker daemon 6.656kB
Step 1/5 : FROM wordpress:apache
apache: Pulling from library/wordpress
Digest: sha256:20bffad04c9c3e696b3c6fbc48d769c5948718b57af8c9457d9a0f28b5066b4b
Status: Downloaded newer image for wordpress:apache
---> 6edecd0f5c75
Step 2/5 : RUN a2enmod ssl
---> Running in cfa4595ed571
Considering dependency setenvif for ssl:
Module setenvif already enabled
Considering dependency mime for ssl:
Module mime already enabled
Considering dependency socache_shmcb for ssl:
Enabling module socache_shmcb.
Enabling module ssl.
See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
To activate the new configuration, you need to run:
service apache2 restart
Removing intermediate container cfa4595ed571
---> 028793b3215f
Step 3/5 : RUN mkdir /etc/apache2/certs
---> Running in 66bf5da33f60
Removing intermediate container 66bf5da33f60
---> 8123001e5c7a
Step 4/5 : COPY ssl-cert-snakeoil.pem /etc/apache2/certs/ssl-cert.pem
---> 872b8b06ddc4
Step 5/5 : COPY ssl-cert-snakeoil.key /etc/apache2/certs/ssl-cert.key
---> 61532a6d1302
Successfully built 61532a6d1302
Successfully tagged ssl-wordpress:latest
root@vps298933:~/wordpress-docker-image#
.. code-block:: bash
:caption: Output
:linenos:
:emphasize-lines: 3
root@vps298933:~/wordpress-docker-image# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ssl-wordpress latest 61532a6d1302 23 minutes ago 546MB
pandoc latest 79ad25bc9c62 4 hours ago 1.03GB
pandoc default 7eb0798f4080 10 hours ago 720MB
node latest f1974cfde44f 40 hours ago 935MB
mariadb latest 2ab9d091310d 44 hours ago 414MB
nextcloud latest 3ed6ea445002 7 days ago 811MB
wordpress apache 6edecd0f5c75 7 days ago 546MB
wordpress latest 6edecd0f5c75 7 days ago 546MB
redis latest 62f1d3402b78 2 weeks ago 104MB
node 6.10 3f3928767182 3 years ago 661MB
root@vps298933:~/wordpress-docker-image#
4.4.3. Add SSL Configuration
==============================
The Apache webserver running in the new image called ``ssl-wordpress``
supports both HTTP and HTTPS. We need to add a configuration
file for the WordPress site to use SSL. We do the same thing when
we create an Nginx site.
#. First, we need to create an Apache SSL configuration for
WordPress to listen on port 433 (SSL). We will store these
files in a volume directory called ``apache``.
.. code-block:: bash
# Create the volume directory and file
cd ~/wordpress-docker
mkdir apache
touch apache/site.conf
Add this information to ``apache/site.conf``.
.. code-block:: bash
:caption: apache/site.conf
:linenos:
:emphasize-lines: 9
#ServerName www.example.com
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
#ServerName www.example.com
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile /etc/apache2/certs/ssl-cert.pem
SSLCertificateKeyFile /etc/apache2/certs/ssl-cert.key
SSLOptions +StdEnvVars
SSLOptions +StdEnvVars
#. Next, we need to modify the ``docker-compose.yml`` so that the
running container can use SSL.
a. Change the image to ``ssl-wordpress:latest``
#. Create a volume for our custom ``site.conf`` file.
#. Tell the container to listen on port 443 by adding a new port
map (20843:443).
.. tip:: We can the numbers 8 and 4 to differentiate between
HTTP and HTTPS maps.
* HTTP sites use port 80 or 8080. We use 208xx
* HTTPS sites use port 443 or 4443. We use 204xx
.. code-block:: bash
:caption: docker-compose.yml
:emphasize-lines: 6,9,12
. . .
wordpress:
depends_on:
- db
- redis
image: ssl-wordpress:latest
volumes:
- ./wordpress:/var/www/html
- ./apache/site.conf:/etc/apache2/sites-enabled/000-default.conf
ports:
- "20851:80"
- "20435:443"
restart: always
. . .
#. Restart and verify that the container responds to HTTPS requests.
.. code-block:: bash
docker-compose down && docker-compose up -d
curl -k --head https://localhost:20435
.. code-block:: bash
:caption: Output
:emphasize-lines: 1,16,19
root@vps298933:~/wordpress-docker# docker-compose down && docker-compose up -d
Stopping wordpressdocker_wordpress_1 ... done
Stopping wordpressdocker_redis_1 ... done
Stopping wordpressdocker_db_1 ... done
Removing wordpressdocker_wordpress_1 ... done
Removing wordpressdocker_redis_1 ... done
Removing wordpressdocker_db_1 ... done
Removing network wordpressdocker_default
Creating network "wordpressdocker_default" with the default driver
Creating wordpressdocker_redis_1 ...
Creating wordpressdocker_db_1 ...
Creating wordpressdocker_redis_1
Creating wordpressdocker_redis_1 ... done
Creating wordpressdocker_wordpress_1 ...
Creating wordpressdocker_wordpress_1 ... done
root@vps298933:~/wordpress-docker# curl -k --head https://localhost:20435
HTTP/1.1 301 Moved Permanently
Date: Fri, 13 Nov 2020 17:41:38 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.12
X-Redirect-By: WordPress
Location: https://localhost/
Content-Type: text/html; charset=UTF-8
root@vps298933:~/wordpress-docker#
#. The last step is to configure Nginx to operate on SSL.
Change ``proxy_pass`` to HTTPS.
Edit your blog Nginx site:
``nano /etc/nginx/sites-available/blog.example.com``
.. code-block:: bash
:caption: Output
:linenos:
:emphasize-lines: 7,8
server {
listen 80;
server_name blog.y.jj8i.com;
location / {
#proxy_pass http://localhost:20851;
proxy_pass https://localhost:20435;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Restart Nginx and obtain an SSL certificate.
.. code-block:: bash
nginx -t
systemctl restart nginx
certbot --nginx
View the page in your browser to verify that WordPress has SSL.