DEV Community

Stephen Afam-Osemene
Stephen Afam-Osemene

Posted on • Originally published at stephenafamo.com on

Docker, Nginx and Let’s Encrypt for a secure website

docker

Let me show you how I use Docker, Nginx and Let’s encrypt to host my websites and serve secure https content.

I use Docker to host a lot of my websites, and with Let’s Encrypt now issuing free SSL certificates, there is no excuse for not having one.

Things to note

My test server is running on Ubuntu 16.04. Though the process should be similar on most other servers.

You should have pointed your domain nameservers or A records to the server.

I’ll use _ example.com _ for this tutorial, you should replace that with your domain name.

I’ll use _ 12.34.56.78 _ as the IP address of the server. You should replace this with your own IP

Step 1: Install Docker

We will now install our beloved Docker using the command.

sudo apt install docker.io
Enter fullscreen mode Exit fullscreen mode

For more ways to install docker, visit the official docker installation page

Step 2: Set up our site with Docker

So, we’ll create a directory for our site, this will hold the code that we’ll mount put into the docker container.

sudo mkdir -p /var/www/example.com
Enter fullscreen mode Exit fullscreen mode

Next, we’ll pull the container.

docker pull creativitykills/nginx-php-server:1.1.1
Enter fullscreen mode Exit fullscreen mode

This is a container with nginx and php installed. The web root of the container is /var/www/public so we’ll have to mount our site there.

Next, we’ll start up our docker container.

docker run -d -p 1234:80 \
 --name example_site \
 -v /var/www/example.com:/var/www/public \
 creativitykills/nginx-php-server:1.1.1
Enter fullscreen mode Exit fullscreen mode

With that command,

  1. We ran the container creativitykills/nginx-php-server:1.1.1
  2. We told docker to run this container in detached mode [-d]
  3. We mapped the port 1234 on our server to port 80 of the container [-p 1234:80]
  4. We named the container example_site [–name example_site]
  5. We mounted our sites folder to the web root of the container. [-v /var/www/example.com:/var/www/public]

Lastly, we’ll add an index.html file in /var/www/example.com/index.html

<!-- /var/www/example.com/index.html -->
<!DOCTYPE html>
<html>
<head>
 <title>Example Site</title>
</head>
<body>
 <h2>This is my example site</h2>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

If you visit _ 12.34.56.78:1234 _ now, you should see the following.

example site
example site

Step 2: Point our domain to our docker container with Nginx

We’ll install Nginx on our server to use as a reverse proxy for our Docker containers. We can install it using the following command.

sudo apt install nginx
Enter fullscreen mode Exit fullscreen mode

If you visit _ example.com _ now, you should see the default nginx page.

ngins-default-page
Nginx default page

To point our domain to our docker container, we have to set up some custom nginx configuration.

Add an example.com.conf file to /etc/nginx/sites-available.

touch /etc/nginx/sites-available/example.com.conf
Enter fullscreen mode Exit fullscreen mode

use your favorite editor to add this to example.com.conf

server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location / {
   proxy_pass http://0.0.0.0:1234;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Proto $scheme;
 }

}
Enter fullscreen mode Exit fullscreen mode

Next, add example.com.conf to the sites-available list using this command.

ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf
Enter fullscreen mode Exit fullscreen mode

Now, reload nginx so that our new configuration takes effect

/etc/init.d/nginx reload
Enter fullscreen mode Exit fullscreen mode

If you visit _ example.com _ now, you should see the following.

example site
example site

 Step 4: Obtain an SSL certificate from Let’s Encrypt

For issuing and renewing certificates, we’ll use the letsencrypt package.

Simply install it by running the following command:

sudo apt install letsencrypt
Enter fullscreen mode Exit fullscreen mode

Next, generate a Diffie-Hellman group. Using this command

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Enter fullscreen mode Exit fullscreen mode

Now, we’ll adjust the nginx configuration to create the certificate.

Edit /etc/nginx/sites-available/example.com.conf and make it this.

server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Proto $scheme;
 }

}
Enter fullscreen mode Exit fullscreen mode

Reload nginx again for our configuration changes to take effect.

/etc/init.d/nginx reload
Enter fullscreen mode Exit fullscreen mode

Obtain the certificates from let’s encrypt.

letsencrypt certonly -a webroot \
--webroot-path=/var/www/example.com \
-d example.com -d www.example.com
Enter fullscreen mode Exit fullscreen mode

You will be asked to enter your email and agree to the terms and conditions.

After that, you should have your ssl certificates installed successfully.

Step 5: Serve your site over HTTPS

We edit our example.com.conf once more to serve https content. Change it to this.

server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Proto $scheme;
 }

}

server {
 listen 443 ssl;
 server_name example.com www.example.com;

 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 ssl_session_cache shared:SSL:10m;
 ssl_session_timeout 5m;
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_prefer_server_ciphers on;
 ssl_dhparam /etc/ssl/certs/dhparam.pem;
 ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
 ssl_stapling on;
 ssl_stapling_verify on;
 add_header Strict-Transport-Security max-age=15768000;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Proto $scheme;
 }
}
Enter fullscreen mode Exit fullscreen mode

Reload nginx one more time!

/etc/init.d/nginx reload
Enter fullscreen mode Exit fullscreen mode

You should now be able to access _ https://example.com _

Step 6: Setup Auto Renewal for your SSL certificates

Let’s encrypt certificates expire after 90 days. The command for renewal is

letsencrypt renew
Enter fullscreen mode Exit fullscreen mode

If you run it now, it’ll tell you that your certificates are not due for renewal.

What we’ll do is to set up a weekly cronjob that tries to renew the certificates, and then reloads nginx.

First, open the crontab as root

sudo crontab -e
Enter fullscreen mode Exit fullscreen mode

Next add the following lines to the crontab.

30 2 * * 1 letsencrypt renew
35 2 * * 1 /etc/init.d/nginx reload
Enter fullscreen mode Exit fullscreen mode

Step 7: Redirect all http requests to https (Optional)

On the quest to make the web safer, it is better to serve all requests over https except you have a good reason not to. To redirect all http traffic to https, modify example.com.conf to this.

server {
 listen 80;
 listen [::]:80;
 server_name example.com www.example.com;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 return 301 https://example.com$request_uri;
}

server {
 listen 443 ssl;
 server_name example.com www.example.com;

 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
 ssl_session_cache shared:SSL:10m;
 ssl_session_timeout 5m;
 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
 ssl_prefer_server_ciphers on;
 ssl_dhparam /etc/ssl/certs/dhparam.pem;
 ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
 ssl_stapling on;
 ssl_stapling_verify on;
 add_header Strict-Transport-Security max-age=15768000;

 location "/.well-known/acme-challenge" {
   default_type "text/plain";
   root /var/www/example.com;
   allow all;
 }

 location / {
   proxy_pass http://0.0.0.0:1234;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Proto $scheme;
 }
}
Enter fullscreen mode Exit fullscreen mode

Now, reload nginx.

/etc/init.d/nginx reload
Enter fullscreen mode Exit fullscreen mode

 

That’s it!

The post Docker, Nginx and Let’s Encrypt for a secure website appeared first on Stephen AfamO's Blog.

Top comments (1)

Collapse
 
dineshrathee12 profile image
Dinesh Rathee

LetsEncrypt have revoked around 3 million certs last night due to a bug that they found. Are you impacted by this, Check out ?

DevTo
[+] dev.to/dineshrathee12/letsencrypt-...

GitHub
[+] github.com/dineshrathee12/Let-s-En...

LetsEncryptCommunity
[+] community.letsencrypt.org/t/letsen...