Adding SSL certs to NGINX docker container
My solution:
I have mapped /etc/letsencrypt/live/mydomain.com/
to /etc/nginx/certs/
by:
volumes:
- /etc/letsencrypt/live/mydomain.com/:/etc/nginx/certs/
In this case, it has mapped the soft symbolic link.
lrwxrwxrwx 1 root root 38 Sep 15 00:21 chain.pem -> ../../archive/mydomain.com/chain1.pem
lrwxrwxrwx 1 root root 42 Sep 15 00:21 fullchain.pem -> ../../archive/mydomain.com/fullchain1.pem
lrwxrwxrwx 1 root root 40 Sep 15 00:21 privkey.pem -> ../../archive/mydomain.com/privkey1.pem
Finally, I changed the volume
section as the following:
volumes:
- /etc/letsencrypt/archive/mydomain.com/:/etc/nginx/certs/
Finally cracked this and was able to successfully repeat the process on my dev and production site to get SSL certs working!
Sorry for the length of the post!
In my setup I have docker docker-compose setup on an ubuntu 16 machine.
Anyone who's encountering this problem I'll detail the steps I did.
Go to the directory where your code lives
cd /opt/example_dir/
Make a directory for letsencrypt and it's site.
sudo mkdir -p /opt/example_dir/letsencrypt/letsencrypt-site
Create barebones docker-compose.yml file from the letsencrypt directory.
sudo nano /opt/example_dir/letsencrypt/docker-compose.yml
Add the following to it:
version: '2'
services:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./letsencrypt-site:/usr/share/nginx/html
networks:
- docker-network
networks:
docker-network:
driver: bridge
* This will pull down the latest nginx version
* Expose port 80
* Mount a config file (that i'll create later)
* Maps the site directory so that we can have a simple test index.html for when
we start the simple nginx container.
Create an nginx.conf file in
/opt/example_dir/letsencrypt
sudo nano /opt/example_dir/letsencrypt/nginx.conf
Put the following into it
server {
listen 80;
listen [::]:80;
server_name example_server.com;
location ~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
root /usr/share/nginx/html;
index index.html;
}
* This listens for requests on port 80 for the server with name example_server.com
* Gives the Certbot agent access to ./well-known/acme-challenge
* Sets the default root and file
- Next create an index.html file within
/opt/example_dir/letsencrypt/letsencrypt-site
sudo nano /opt/example_dir/letsencrypt/letsencrypt-site/index.html
Add the following to it
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>LetsEncrypt Setup</title>
</head>
<body>
<p>Test file for our http nginx server</p>
</body>
</html>
#### All parts in place for basic nginx container!
Now we start up the nginx container.
cd /opt/example_dir/letsencrypt sudo docker-compose up -d
The nginx container is up and running now, visit the url you've defined and you should get the test index.html page back. At this point we're ready to run the certbot command to generate some certs
Run the following to generate certs replacing
--email
with your emailsudo docker run -it --rm \ -v /docker-volumes/etc/letsencrypt:/etc/letsencrypt \ -v /docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt \ -v /opt/example_dir/letsencrypt/letsencrypt-site:/data/letsencrypt \ -v "/docker-volumes/var/log/letsencrypt:/var/log/letsencrypt" \ certbot/certbot \ certonly --webroot \ --email [email protected] --agree-tos --no-eff-email \ --webroot-path=/data/letsencrypt \ -d example.com
- Run docker in interactive mode so you can see the output.
- When its finished generating certs it will remove itself.
- It will mount 4 volumes:
- The letsencrypt folder where the certs are stored/
- A lib folder
- Maps our site folder
- Maps a logging path
- It agrees to ToS
- Specifies the root webpath
- Specify the server address you want to generate certs for.
If that command ran okay then we have generated certs for this web server. We can now use these in our production site and configure nginx to use the ssl and make use of these certs!
Shut down the nginx container
cd /opt/example_dir/letsencrypt/ sudo docker-compose down
Setup Production nginx container
Directory structure should look like this now. Where you have your code / web app project and then the letsencrypt folder that we created above.
/opt/example_dir
/ -> project_folder
/ -> letsencrypt
Create a folder call dh-param
sudo mkdir -p /opt/example_dir/project_folder/dh-param
Generate a dh key
sudo openssl dhparam -out /opt/example_dir/project_folder/dh-param/dhparam-2048.pem 2048
Update docker-compose.yml and nginx.conf files within
/opt/example_dir/project_folder
The project_folder is where my source code lives so I create a production config file here for nginx and update the docker-compose.yml to mount my nginx config, dh-pharam exchange key as well as the certs themselves we created earlier.
nginx service in the docker-compose
nginx:
image: nginx:1.11.3
restart: always
ports:
- "80:80"
- "443:443"
- "8000:8000"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./dh-param/dhparam-2048.pem:/etc/ssl/certs/dhparam-2048.pem
- /docker-volumes/etc/letsencrypt/live/exampleserver.com/fullchain.pem:/etc/letsencrypt/live/exampleserver.com/fullchain.pem
- /docker-volumes/etc/letsencrypt/live/exampleserver.com/privkey.pem:/etc/letsencrypt/live/exampleserver.com/privkey.pem
networks:
- docker-network
volumes_from:
- flask
depends_on:
- flask
- falcon
links:
- datastore
nginx.conf within project_folder
error_log /var/log/nginx/error.log warn;
server {
listen 80;
listen [::]:80;
server_name exampleserver.com
location / {
rewrite ^ https://$host$request_uri? permanent;
}
#for certbot challenges (renewal process)
location ~ /.well-known/acme-challenge {
allow all;
root /data/letsencrypt;
}
}
#https://exampleserver.com
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name exampleserver.com;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/exampleserver.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/exampleserver.com/privkey.pem;
ssl_buffer_size 8k;
ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
# Define the specified charset to the “Content-Type” response header field
charset utf-8;
}
At this point everything is setup! (finally)
Spin up the docker container.
cd /opt/example_dir/project_folder sudo docker-compose up -d # Check the docker log with: sudo docker logs -f -t
I know it's a lot of steps but this is what I have done, it's worked for me and hope it helps someone else.