How to use Let’s Encrypt with nginx and docker

In my last blog post, I shared the story on how did I set my server up. I mentioned that I’d be writing about getting SSL certificates, so here you go.

When I started working on a remote server somewhere out there in the globe and letting that come in into my private space, (my home machine) I realised I needed to be much more careful, and secure.

The first step to attain security was to set up a firewall to control unwanted incoming intrusions.
The next step was to create a reverse proxy in nginx :

Let us assume we’re running a docker container, a CentOS 7 host, using the latest ghost image. So first, one has to install docker, nginx and start the docker service:

yum install docker nginx epel-release vim -y

Along with docker and nginx we are also installing epel-release from which we will later get Certbot, for the next part of our project and vim if you prefer to.

systemctl start docker

Next I started the docker container, I am using ghost as an example here.

docker run -d --name xyz -p 127.0.0.1:9786:2368 ghost:1.21.4

Running the docker container in background. I am exposing the container’s port 2368 to the port 9786 of the localhost, (using ghost as an example in this case.)


sudo vim /etc/nginx/conf.d/xyz.anweshadas.in.conf

Now we have to set up nginx for the server name xyz.anweshadas.in, in a configuration file named xyz.anweshadas.in.conf. The configuration looks like this


server {
        listen 80;

        server_name xyz.anweshadas.in;

        location / {
                # proxy commands go here as in your port 80 configuration

                proxy_pass http://127.0.0.1:9786/;
                proxy_redirect off;
                proxy_set_header HOST $http_host;
                proxy_set_header X-NginX-Proxy true;
                proxy_set_header X-Real-IP $remote_addr;
                    }
}

In the above mentioned configuration we are receiving the http requests
on port 80. We are forwarding all the requests for xyz.anweshadas.in to the port 9786 of our localhost.

Before we can start nginx, we have to set up a SELinux boolean so that the nginx server can connect to any port on localhost.

setsebool httpd_can_network_connect 1 -P

systemctl start nginx

Now you will be able to see the ghost running at http://xyz.anweshadas.in.

To protect one’s security and privacy in the web sphere it is very important to know that the people or objects one is communicating with, are actually who they claim to be.
In such circumstances, TLS certificates is what we rely on. Let’s Encrypt is one such certificate authority, that provides certificates.

It provides certificates for Transport Layer Security (TLS) encryption via an automated process. Certbot is the client side tool (from the EFF) to get a certificate from Let’s Encrypt.

So we need a https (secure) certificate for our server by installing certbot.
Let’s get started

yum install certbot
mkdir -p /var/www/xyz.anweshadas.in/.well-known

We now need to make a directory named .well-known, in /var/www/xyz.anweshadas.in, where we will get the certificate for validation by Let’s Encrypt certificate.

chcon -R -t httpd_sys_content_t /var/www/xyz.anweshadas.in

This SELinux context of the directory, xyz.anweshadas.in.

Now we need to enable the access of the .well-known directory under our domain, that Let’s Encrypt can verify. The configuration of nginx, is as follows

server {
        listen 80;

        server_name xyz.anweshadas.in;

        location /.well-known {
                alias /var/www/xyz.anweshadas.in/.well-known;
        }

        location / {
                  # proxy commands go here as in your port 80 configuration

                  proxy_pass http://127.0.0.1:9786/;
                  proxy_redirect off;
                  proxy_set_header HOST $http_host;
                  proxy_set_header X-NginX-Proxy true;
                  proxy_set_header X-Real-IP $remote_addr;
         }

}
certbot certonly --dry-run --webroot -w /var/www/xyz.anweshadas.in/ -d xyz.anweshadas.in

We are performing a test run of the client, by obtaining the test certificates, through placing files in a webroot, but not actually saving them in the hard drive. To have a dry-run is important because the number of time one can get certificates for a particular domain a limited number of time (20 times in a week). All the subdomains under a particular domain are counted separately. To know more, go to the manual page of Certbot.

certbot certonly --webroot -w /var/www/xyz.anweshadas.in/ -d xyz.anweshadas.in

After running the dry-run successfully, we will rerun the command agian without dry-run to get the actual certificates. In the command we are providing the webroot using -w pointing to /var/www/xyz.anweshadas.in/ directory for the particular domain(-d) named xyz.anweshadas.in.

Let us add some more configuration to nginx, so that we can access the https version of our website.

vim /etc/nginx/conf.d/xyz.anweshadas.in.conf

The configuration looks like:

server {
    listen 443 ssl;

    # if you wish, you can use the below line for listen instead
    # which enables HTTP/2
    # requires nginx version >= 1.9.5
    # listen 443 ssl http2;

    server_name xyz.anweshadas.in;

    ssl_certificate /etc/letsencrypt/live/xyz.anweshadas.in/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xyz.anweshadas.in/privkey.pem;

    # Turn on OCSP stapling as recommended at
    # https://community.letsencrypt.org/t/integration-guide/13123
    # requires nginx version >= 1.3.7
    ssl_stapling on;
    ssl_stapling_verify on;

    # modern configuration. tweak to your needs.
    ssl_protocols TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
    ssl_prefer_server_ciphers on;

    # Uncomment this line only after testing in browsers,
    # as it commits you to continuing to serve your site over HTTPS
    # in future
    # add_header Strict-Transport-Security "max-age=31536000";


    # maintain the .well-known directory alias for renewals
    location /.well-known {

        alias /var/www/xyz.anweshadas.in/.well-known;
    }

    location / {
        # proxy commands go here as in your port 80 configuration

        proxy_pass http://127.0.0.1:9786/;
        proxy_redirect off;
        proxy_set_header HOST $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

To view https://xyz.anweshadas.in, reload nginx.

systemctl reload nginx

In case of any error, go to the nginx logs.

If everything works fine, then follow the below configuration.

server {
        listen 80;

        server_name xyz.anweshadas.in;

        location /.well-known {
            alias /var/www/xyz.anweshadas.in/.well-known;
        }

        rewrite ^ https://$host$request_uri? ;

}
server {
    listen 443 ssl;

    # if you wish, you can use the below line for listen instead
    # which enables HTTP/2
    # requires nginx version >= 1.9.5
    # listen 443 ssl http2;

    server_name xyz.anweshadas.in;

    ssl_certificate /etc/letsencrypt/live/xyz.anweshadas.in/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xyz.anweshadas.in/privkey.pem;

    # Turn on OCSP stapling as recommended at
    # https://community.letsencrypt.org/t/integration-guide/13123
    # requires nginx version >= 1.3.7
    ssl_stapling on;
    ssl_stapling_verify on;

    # modern configuration. tweak to your needs.
    ssl_protocols TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
    ssl_prefer_server_ciphers on;


    # Uncomment this line only after testing in browsers,
    # as it commits you to continuing to serve your site over HTTPS
    # in future
    #add_header Strict-Transport-Security "max-age=31536000";


    # maintain the .well-known directory alias for renewals
    location /.well-known {

        alias /var/www/xyz.anweshadas.in/.well-known;
    }

    location / {
    # proxy commands go here as in your port 80 configuration

    proxy_pass http://127.0.0.1:9786/;
    proxy_redirect off;
    proxy_set_header HOST $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_set_header X-Real-IP $remote_addr;
    }
}

The final nginx configuration [i.e., the /etc/nginx/conf.d/xyz.anweshadas.in.conf] looks like the following, having the rewrite rule, forwarding all http requests to https. And uncommenting the “Strict-Transport-Security” header.

server {
        listen 80;

        server_name xyz.anweshadas.in;

        location /.well-known {
            alias /var/www/xyz.anweshadas.in/.well-known;
         }

        rewrite ^ https://$host$request_uri? ;

}

server {
        listen 443 ssl;

        # if you wish, you can use the below line for listen instead
        # which enables HTTP/2
        # requires nginx version >= 1.9.5
        # listen 443 ssl http2;

        server_name xyz.anweshadas.in;

        ssl_certificate /etc/letsencrypt/live/xyz.anweshadas.in/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/xyz.anweshadas.in/privkey.pem;

        # Turn on OCSP stapling as recommended at
        # https://community.letsencrypt.org/t/integration-guide/13123
        # requires nginx version >= 1.3.7
        ssl_stapling on;
        ssl_stapling_verify on;

        # modern configuration. tweak to your needs.
        ssl_protocols TLSv1.2;
        ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
        ssl_prefer_server_ciphers on;


        # Uncomment this line only after testing in browsers,
        # as it commits you to continuing to serve your site over HTTPS
        # in future
        add_header Strict-Transport-Security "max-age=31536000";


        # maintain the .well-known directory alias for renewals
        location /.well-known {

            alias /var/www/xyz.anweshadas.in/.well-known;
    }

        location / {
        # proxy commands go here as in your port 80 configuration

        proxy_pass http://127.0.0.1:9786/;
        proxy_redirect off;
        proxy_set_header HOST $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header X-Real-IP $remote_addr;
        }
}

So, now hopefully the website shows the desired content at the correct url now.

For this particular work, I am highly indebted to the Linux for You and Me, book, which actually introduced and made me comfortable with the Linux Command line.

Show Comments