-
-
Save mattiaslundberg/ba214a35060d3c8603e9b1ec8627d349 to your computer and use it in GitHub Desktop.
Ansible playbook to setup HTTPS using Let's encrypt on nginx. | |
The Ansible playbook installs everything needed to serve static files from a nginx server over HTTPS. | |
The server pass A rating on [SSL Labs](https://www.ssllabs.com/). | |
To use: | |
1. Install [Ansible](https://www.ansible.com/) | |
2. Setup an Ubuntu 16.04 server accessible over ssh | |
3. Create `/etc/ansible/hosts` according to template below and change example.com to your domain | |
4. Copy the rest of the files to an empty directory (`playbook.yml` in the root of that folder and the rest in the `templates` subfolder) | |
5. Run `ansible-playbook playbook.yml` | |
6. Copy your (static HTML) code to `/var/www/example.com` (`example.com` replaced with your domain) | |
7. Restart nginx (`systemctl restart nginx`) |
[letsencrypt] | |
example.com ansible_user=root [email protected] domain_name=example.com |
--- | |
- hosts: letsencrypt | |
become: true | |
gather_facts: no | |
pre_tasks: | |
- raw: apt-get install -y python-simplejson | |
tasks: | |
- name: Upgrade system | |
apt: upgrade=dist update_cache=yes | |
- name: Install nginx | |
apt: name=nginx state=latest | |
- name: install letsencrypt | |
apt: name=letsencrypt state=latest | |
- name: create letsencrypt directory | |
file: name=/var/www/letsencrypt state=directory | |
- name: Remove default nginx config | |
file: name=/etc/nginx/sites-enabled/default state=absent | |
- name: Install system nginx config | |
template: | |
src: templates/nginx.conf.j2 | |
dest: /etc/nginx/nginx.conf | |
- name: Install nginx site for letsencrypt requests | |
template: | |
src: templates/nginx-http.j2 | |
dest: /etc/nginx/sites-enabled/http | |
- name: Reload nginx to activate letsencrypt site | |
service: name=nginx state=restarted | |
- name: Create letsencrypt certificate | |
shell: letsencrypt certonly -n --webroot -w /var/www/letsencrypt -m {{ letsencrypt_email }} --agree-tos -d {{ domain_name }} | |
args: | |
creates: /etc/letsencrypt/live/{{ domain_name }} | |
- name: Generate dhparams | |
shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048 | |
args: | |
creates: /etc/nginx/dhparams.pem | |
- name: Install nginx site for specified site | |
template: | |
src: templates/nginx-le.j2 | |
dest: /etc/nginx/sites-enabled/le | |
- name: Reload nginx to activate specified site | |
service: name=nginx state=restarted | |
- name: Add letsencrypt cronjob for cert renewal | |
cron: | |
name: letsencrypt_renewal | |
special_time: weekly | |
job: letsencrypt --renew certonly -n --webroot -w /var/www/letsencrypt -m {{ letsencrypt_email }} --agree-tos -d {{ domain_name }} && service nginx reload |
server_tokens off; | |
server { | |
listen 80 default_server; | |
server_name {{ domain_name }}; | |
location /.well-known/acme-challenge { | |
root /var/www/letsencrypt; | |
try_files $uri $uri/ =404; | |
} | |
location / { | |
rewrite ^ https://{{ domain_name }}$request_uri? permanent; | |
} | |
} | |
add_header X-Frame-Options SAMEORIGIN; | |
add_header X-Content-Type-Options nosniff; | |
add_header X-XSS-Protection "1; mode=block"; | |
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com; img-src 'self' data: https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-src 'none'; object-src 'none'"; | |
# HTTPS server | |
# | |
server { | |
listen 443 ssl default deferred; | |
server_name {{ domain_name }}; | |
ssl on; | |
ssl_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem; | |
ssl_certificate_key /etc/letsencrypt/live/{{ domain_name }}/privkey.pem; | |
ssl_trusted_certificate /etc/letsencrypt/live/{{ domain_name }}/fullchain.pem; | |
ssl_session_cache shared:SSL:50m; | |
ssl_session_timeout 5m; | |
ssl_stapling on; | |
ssl_stapling_verify on; | |
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; | |
ssl_dhparam /etc/nginx/dhparams.pem; | |
ssl_prefer_server_ciphers on; | |
root /var/www/{{ domain_name }}; | |
index index.html index.htm; | |
location / { | |
try_files $uri $uri/ =404; | |
} | |
} |
user www-data; | |
worker_processes 4; | |
pid /run/nginx.pid; | |
events { | |
worker_connections 768; | |
} | |
http { | |
sendfile on; | |
tcp_nopush on; | |
tcp_nodelay on; | |
keepalive_timeout 65; | |
types_hash_max_size 2048; | |
include /etc/nginx/mime.types; | |
default_type application/octet-stream; | |
access_log /var/log/nginx/access.log; | |
error_log /var/log/nginx/error.log; | |
gzip on; | |
gzip_disable "msie6"; | |
include /etc/nginx/conf.d/*.conf; | |
include /etc/nginx/sites-enabled/*; | |
} |
GREAT work, thanks a lot)
Just note for whom it failed:
On Ubuntu you need to use certbot instead letsencrypt cli
And no need to use 2 websites to generate a certificate, it worked for me always with one, even when it's proxy and doesn't have no working directory to pass acme validation.
Also: letsencrypt made legacy one of methods for domains verification so make sure you're using certbot v0.28 and above
Happy coding)
:-) sweet
Thank you so much !! You saved me so much time !!
GREAT work, thanks a lot)
Just note for whom it failed:
On Ubuntu you need to use certbot instead letsencrypt cli
And no need to use 2 websites to generate a certificate, it worked for me always with one, even when it's proxy and doesn't have no working directory to pass acme validation.Also: letsencrypt made legacy one of methods for domains verification so make sure you're using certbot v0.28 and above
Happy coding)
Can you share the command need to run in playbook for certbot (i'm using ubuntu)
Certbot instructions here:
https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx
This is amazing. Worked without any major modifications on ubuntu 19.10 on a raspberry pi.
I did the following changes
- Changed hosts to localhost
- Removed inventory file
- Renamed the template files to fix their names after the download
- Modified the SSL config to be a reverse proxy
- Moved variables from inventory file into vars in the playbook
- ran the playbook with ansible-playbook playbook.yml
My playbook:
---
- hosts: localhost
become: true
gather_facts: no
vars:
domain_name: www.example.com
letsencrypt_email: [email protected]
tasks:
- name: Install nginx
apt:
name: nginx
state: latest
- name: install letsencrypt
apt:
name: letsencrypt
state: latest
- name: create letsencrypt directory
file:
name: /var/www/letsencrypt
state: directory
- name: Remove default nginx config
file:
name: /etc/nginx/sites-enabled/default
state: absent
- name: Install system nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
- name: Install nginx site for letsencrypt requests
template:
src: nginx-http.j2
dest: /etc/nginx/sites-enabled/http
- name: Reload nginx to activate letsencrypt site
service:
name: nginx
state: restarted
- name: Create letsencrypt certificate
shell: letsencrypt certonly -n --webroot -w /var/www/letsencrypt -m {{ letsencrypt_email }} --agree-tos -d {{ domain_name }}
args:
creates: /etc/letsencrypt/live/{{ domain_name }}
- name: Generate dhparams
shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048
args:
creates: /etc/nginx/dhparams.pem
- name: Install nginx site for specified site
template:
src: nginx-le.j2
dest: /etc/nginx/sites-enabled/le
- name: Reload nginx to activate specified site
service: name=nginx state=restarted
- name: Add letsencrypt cronjob for cert renewal
cron:
name: letsencrypt_renewal
special_time: weekly
job: letsencrypt --renew certonly -n --webroot -w /var/www/letsencrypt -m {{ letsencrypt_email }} --agree-tos -d {{ domain_name }} && service nginx reload
@diablozzq this is great. what if need to handle 2 sub domains
example: domain_name: aaa.example.com
and domain_name_2: bbbb.example.com
@diablozzq this is great. what if need to handle 2 sub domains
example:
domain_name: aaa.example.com
anddomain_name_2: bbbb.example.com
you can do like this: -d www.aname.com -d api.aname.com -d api2.aname.com
also you can use wildcard way
Thanks for this!
FYI, on my Ubuntu 16.04.6 server with certbot 0.27.0
, I had to change the cronjob from
job: letsencrypt --renew certonly -n --webroot -w /var/www/letsencrypt -m {{ letsencrypt_email }} --agree-tos -d {{ domain_name }} && service nginx reload
to
job: letsencrypt --renew-by-default certonly -n --webroot -w /var/www/letsencrypt -m {{ letsencrypt_email }} --agree-tos -d {{ domain_name }} && service nginx reload
Thank you man! This saved me hours!
pre_tasks:
- raw: apt-get install -y python-simplejson
for Gentoo replace
pre_tasks:
- raw: emerge -v dev-python/simplejson
thanks man! amazing work.
Great stuff man, thanks a lot!
why u not using proxy nginx with buffer?
thanks for this! nowadays python-simplejson
is python3-simplejson
(tested on debian 11)
--renew-by-default
thanks for the tip
+1