Certbot certificate verification through nginx container

Quick Reminder: What is docker-compose?

docker-compose is a tool for defining and running multi-container Docker applications.

This guide requires docker-compose. If you don’t have it yet, take a look at the installation instructions and get it.

Clone the docker-symfony repository which has the docker configuration files.

git clone https://github.com/StaffNowa/docker-symfony.git
Cloning into 'docker-symfony'...
remote: Enumerating objects: 227, done.
remote: Counting objects: 100% (227/227), done.
remote: Compressing objects: 100% (157/157), done.
remote: Total 227 (delta 112), reused 170 (delta 59), pack-reused 0
Receiving objects: 100% (227/227), 39.78 KiB | 0 bytes/s, done.
Resolving deltas: 100% (112/112), done.

Setting up docker volume

What we need to do is setup a docker volume on the nginx container. This will allow us to later setup a route, through to this local directory. Within docker-compose, add the volume like so:

services:
  nginx:
    ...
    volumes:
      - /root/docker-symfony/certs-data/:/data/letsencrypt/

This will map the local directory, /root/docker-symfony/user/certs-data/, to a directory on the container under, /data/letsencrypt/.

Routing to the volume

Now the volume is setup to point to a local directory we need to create a route for it. This is done within nginx config.

server {
    listen 80 default_server;

    server_name docker.prado.lt;
    root /var/www/project/public;

    location ^~ /.well-known {
        allow all;
        root /data/letsencrypt/;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 http2 default_server;
    ...
}

Usually we are going to want to redirect traffic coming from any location on port 80 (http), to port 443 (https). This is done in the config using the location / property, within the server node listening on 80. However for requests to verify the domain, we need to allow http://docker.prado.lt/.well-known to serve up the local directory as /root/symfony-docker/user/certs-data, which is mounted on the container volume at /data/letsencrypt/. The ^~ prevents regular expression matching from occurring if it is determined to be the best non-regular expression match.

With this change in place we should be able to create a test file in the local directory and curl it to return the content.

cd ~/docker-symfony
mkdir -p user/certs-data/.well-known
cd user/certs-data/.well-known
echo test >> index.html
curl http://docker.prado.lt/.well-known/index.html

Creating the certificate

We can now run the certbox command above and not get the verification error:

certbot certonly --webroot -w /root/docker-symfony/user/certs-data/ -d docker.prado.lt -d www.docker-prado.lt

This will then save the certificates in the default location on the server:

ls -la /etc/letsencrypt/live/docker.prado.lt/
total 12
drwxr-xr-x 2 root root 4096 2019-03-31 22:01 .
drwx------ 3 root root 4096 2019-03-31 22:01 ..
lrwxrwxrwx 1 root root   39 2019-03-31 22:01 cert.pem -> ../../archive/docker.prado.lt/cert1.pem
lrwxrwxrwx 1 root root   40 2019-03-31 22:01 chain.pem -> ../../archive/docker.prado.lt/chain1.pem
lrwxrwxrwx 1 root root   44 2019-03-31 22:01 fullchain.pem -> ../../archive/docker.prado.lt/fullchain1.pem
lrwxrwxrwx 1 root root   42 2019-03-31 22:01 privkey.pem -> ../../archive/docker.prado.lt/privkey1.pem
-rw-r--r-- 1 root root  692 2019-03-31 22:01 README

Mapping the certs through docker

Now we have valid certificates on the server, we need to make them available to nginx through docker.

We create a volume on the nginx container in docker-compose, as we did before like so:

services:  
  nginx:
    ...
    volumes:
     - /etc/letsencrypt/:/etc/letsencrypt/

We should mount the location of the certs on the server to the same location as in the docker container.

We then need to apply the certs in nginx in the normal way, making sure we point to the /etc/letsenrypt/ directory:

server {
    listen 80 default_server;
    ...
}

server {
    listen 443 ssl http2 default_server;

    ssl_certificate /etc/letsencrypt/live/docker.prado.lt/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/docker.prado.lt/privkey.pem;

    server_name docker.prado.lt;
    root /var/www/project/public;

    access_log /var/log/nginx/project_access.log;
    error_log /var/log/nginx/project_error.log;

     # strip app.php/ prefix if it is present
    rewrite ^/app\.php/?(.*)$ /$1 permanent;

    location / {
        index app.php;
        try_files $uri @rewriteapp;
    }

    location @rewriteapp {
        rewrite ^(.*)$ /app.php/$1 last;
    }

    # expire
    location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff|woff2|eot|ttf)$ {
        try_files $uri /website.php/$1?$query_string;
        access_log off;
        expires 30d;
        add_header Pragma public;
        add_header Cache-Control "public";
    }

    # pass the PHP script to FastCGI server from upstream phpfcgi
    location ~ ^/(app|app_dev|config|_intellij_phpdebug_validator)\.php(/|$) {
        fastcgi_pass php-upstream;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SYMFONY_ENV dev;
        fastcgi_param HTTPS off;
    }
}

Certificates can be renewed with certbot using the following command:

certbot renew

or

certbot renew --dry-run //for testing

Leave a Reply

Your email address will not be published. Required fields are marked *