From d7d4a083e2d429d678121c471f6cf0d165e48d43 Mon Sep 17 00:00:00 2001
From: Sven-Hendrik Haase <svenstaro@gmail.com>
Date: Mon, 20 Jun 2016 03:00:53 +0200
Subject: [PATCH] Add letsencrypt/certbot stuff

---
 README.md                                 | 10 ++++++-
 playbooks/orion.yml                       |  3 ++-
 roles/dbscripts/templates/nginx.d.conf.j2 |  6 +++--
 roles/nginx/files/certbot-renewal.service |  7 +++++
 roles/nginx/files/certbot-renewal.timer   |  9 +++++++
 roles/nginx/tasks/main.yml                | 28 +++++++++++++++++++-
 roles/nginx/templates/letsencrypt.conf    |  5 ++++
 roles/nginx/templates/sslsettings.conf    |  9 +++++++
 roles/sources/tasks/main.yml              |  9 +++++++
 roles/sources/templates/nginx.d.conf.j2   | 32 +++++++++++++++++++++++
 10 files changed, 113 insertions(+), 5 deletions(-)
 create mode 100644 roles/nginx/files/certbot-renewal.service
 create mode 100644 roles/nginx/files/certbot-renewal.timer
 create mode 100644 roles/nginx/templates/letsencrypt.conf
 create mode 100644 roles/nginx/templates/sslsettings.conf
 create mode 100644 roles/sources/tasks/main.yml
 create mode 100644 roles/sources/templates/nginx.d.conf.j2

diff --git a/README.md b/README.md
index 72ac0c5fb..dd5e859aa 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,14 @@ After the provisioning script has run, it is safe to reboot.
 Once in the new system, run the regular playbook: `ansible-playbook playbooks/$hostname.yml`. This
 playbook is the one regularily used for adminstrating the server and is entirely idempotent.
 
+##### Note about first time certificates
+
+The first time a certificate is issued, you'll have to do this manually by yourself. First, configure the DNS to
+point to the new server and then run a playbook onto the server which includes the nginx role. Then on the server,
+it is necessary to run the following once:
+
+certbot certonly --email webmaster@archlinux.org --agree-tos --rsa-key-size 4096 --renew-by-default --webroot -w /var/lib/letsencrypt/ <domain-name>
+
 ## Servers
 
 ### vostok
@@ -22,7 +30,7 @@ playbook is the one regularily used for adminstrating the server and is entirely
 
 #### Services
 - repos/sync (repos.archlinux.org)
-- sources
+- sources (sources.archlinux.org)
 - archive (archive.archlinux.org)
 
 ### apollo
diff --git a/playbooks/orion.yml b/playbooks/orion.yml
index 38a51cdbd..0d1e381bc 100644
--- a/playbooks/orion.yml
+++ b/playbooks/orion.yml
@@ -12,7 +12,8 @@
     - { role: opendkim, dkim_selector: orion, tags: ['mail'] }
     - { role: postfix, tags: ['mail'] }
     - { role: archusers, tags: ['archusers'] }
-    - { role: nginx, tags: ["nginx"] }
+    - { role: nginx, letsencrypt_validation_dir: "/var/lib/letsencrypt", tags: ["nginx"] }
     - { role: dbscripts, repos_domain: "repos.archlinux.org", svntogit_repos: "/srv/svntogit/repos", tags: ['dbscripts', 'archusers'] }
     - { role: sudo, tags: ['sudo', 'archusers'] }
     - { role: archweb, archweb_home: "/srv/http/archweb", tags: ['archweb'] }
+    - { role: sources, sources_domain: "sources.archlinux.org", tags: ['sources'] }
diff --git a/roles/dbscripts/templates/nginx.d.conf.j2 b/roles/dbscripts/templates/nginx.d.conf.j2
index 53da46e39..9b1331bb9 100644
--- a/roles/dbscripts/templates/nginx.d.conf.j2
+++ b/roles/dbscripts/templates/nginx.d.conf.j2
@@ -1,6 +1,6 @@
 server {
-    listen       80 default_server;
-    listen       [::];
+    listen       80;
+    listen       [::]:80;
     server_name  {{ repos_domain }};
     root         /srv/ftp;
 
@@ -10,6 +10,8 @@ server {
         allow all;
     }
 
+    include snippets/letsencrypt.conf;
+
     # Server at velocitynet
     allow  66.211.214.130; # dom0.archlinux.org.
     allow  66.211.214.131; # gudrun.archlinux.org.
diff --git a/roles/nginx/files/certbot-renewal.service b/roles/nginx/files/certbot-renewal.service
new file mode 100644
index 000000000..74b1841ca
--- /dev/null
+++ b/roles/nginx/files/certbot-renewal.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Let's Encrypt renewal
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/certbot renew --rsa-key-size 4096
+ExecStartPost=/bin/systemctl reload nginx.service
diff --git a/roles/nginx/files/certbot-renewal.timer b/roles/nginx/files/certbot-renewal.timer
new file mode 100644
index 000000000..d5bebba69
--- /dev/null
+++ b/roles/nginx/files/certbot-renewal.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Daily renewal of Let's Encrypt's certificates
+
+[Timer]
+OnCalendar=daily
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml
index ae4f43131..920b7b0c2 100644
--- a/roles/nginx/tasks/main.yml
+++ b/roles/nginx/tasks/main.yml
@@ -1,13 +1,22 @@
 ---
 
 - name: install nginx
-  pacman: name=nginx-mainline state=present
+  pacman: name=nginx-mainline,certbot state=present
 
 - name: configure nginx
   template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644
   notify:
     - restart nginx
 
+- name: snippets directory
+  file: state=directory path=/etc/nginx/snippets owner=root group=root mode=755
+
+- name: copy snippets
+  template: src={{ item }} dest=/etc/nginx/snippets owner=root group=root mode=644
+  with_items:
+    - letsencrypt.conf
+    - sslsettings.conf
+
 - name: create nginx.d directory
   file: state=directory path=/etc/nginx/nginx.d owner=root group=root mode=755
 
@@ -17,5 +26,22 @@
 - name: create default nginx log directory
   file: state=directory path=/var/log/nginx/default owner=http group=log mode=750
 
+- name: create unique DH group
+  command: openssl dhparam -out /etc/ssl/dhparams.pem 2048 creates=/etc/ssl/dhparams.pem
+
+- name: create directory to store validation stuff in
+  file: owner=root group=http mode=750 path={{ letsencrypt_validation_dir }} state=directory
+
+- name: install letsencrypt renewal service
+  copy: src={{ item }} dest=/etc/systemd/system/{{ item }} owner=root group=root mode=644
+  with_items:
+    - certbot-renewal.service
+    - certbot-renewal.timer
+  notify:
+    - daemon reload
+
+- name: activate letsencrypt renewal service
+  service: name=certbot-renewal.timer enabled=yes state=started
+
 - name: enable nginx
   service: name=nginx enabled=yes
diff --git a/roles/nginx/templates/letsencrypt.conf b/roles/nginx/templates/letsencrypt.conf
new file mode 100644
index 000000000..99dd6c628
--- /dev/null
+++ b/roles/nginx/templates/letsencrypt.conf
@@ -0,0 +1,5 @@
+location /.well-known/acme-challenge {
+    root {{ letsencrypt_validation_dir }};
+    default_type "text/plain";
+    try_files $uri =404;
+}
diff --git a/roles/nginx/templates/sslsettings.conf b/roles/nginx/templates/sslsettings.conf
new file mode 100644
index 000000000..27b7f30b6
--- /dev/null
+++ b/roles/nginx/templates/sslsettings.conf
@@ -0,0 +1,9 @@
+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_protocols TLSv1.2;
+ssl_prefer_server_ciphers on;
+ssl_dhparam /etc/ssl/dhparams.pem;
+ssl_session_timeout  5m;
+ssl_session_cache    shared:SSL:10m;
+ssl_stapling        on;
+ssl_stapling_verify on;
+add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
diff --git a/roles/sources/tasks/main.yml b/roles/sources/tasks/main.yml
new file mode 100644
index 000000000..6bb046372
--- /dev/null
+++ b/roles/sources/tasks/main.yml
@@ -0,0 +1,9 @@
+---
+
+- name: set up nginx
+  template: src=nginx.d.conf.j2 dest=/etc/nginx/nginx.d/sources.conf owner=root group=root mode=644
+  notify:
+    - restart nginx
+
+- name: make nginx log dir
+  file: path=/var/log/nginx/{{ sources_domain }} state=directory owner=http group=log mode=755
diff --git a/roles/sources/templates/nginx.d.conf.j2 b/roles/sources/templates/nginx.d.conf.j2
new file mode 100644
index 000000000..c885d61dd
--- /dev/null
+++ b/roles/sources/templates/nginx.d.conf.j2
@@ -0,0 +1,32 @@
+server {
+    listen       80;
+    listen       [::]:80;
+    server_name  {{ sources_domain }};
+
+    access_log   /var/log/nginx/{{ sources_domain }}/access.log;
+    error_log    /var/log/nginx/{{ sources_domain }}/error.log;
+
+    include snippets/letsencrypt.conf;
+
+    location / {
+        rewrite ^(.*) https://$server_name$1 permanent;
+    }
+}
+
+server {
+    listen       443 ssl http2;
+    listen       [::]:443 ssl http2;
+    server_name  {{ sources_domain }};
+
+    access_log   /var/log/nginx/{{ sources_domain }}/access.log;
+    error_log    /var/log/nginx/{{ sources_domain }}/error.log;
+
+    ssl_certificate      /etc/letsencrypt/live/{{ sources_domain }}/fullchain.pem;
+    ssl_certificate_key  /etc/letsencrypt/live/{{ sources_domain }}/privkey.pem;
+    ssl_trusted_certificate /etc/letsencrypt/live/{{ sources_domain }}/chain.pem;
+    include snippets/sslsettings.conf;
+
+    root         /srv/ftp/sources;
+
+    autoindex on;
+}
-- 
GitLab