diff --git a/README.md b/README.md index 72ac0c5fb473f2b9adb08a7b1dd1edb2f9634722..dd5e859aac016d1171bb526a54173ede4f0a7dd8 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 38a51cdbd88251889864aa3eb21b01f0f06ad252..0d1e381bc2cf373c49851c51d4a41ed4f7e1c1b0 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 53da46e399e1f30b345a3c27463a5c92dd5760d0..9b1331bb97a3b4b8597d3e66eea658f4b78cbca8 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 0000000000000000000000000000000000000000..74b1841ca5ab30b4ba94917af9cf86842eb00f64 --- /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 0000000000000000000000000000000000000000..d5bebba697c0d9cc24353e62c21656beaa67cac9 --- /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 ae4f43131975ea8ad1ad92a0ac4bb37c150a98d9..920b7b0c2a7927fdea87783f2aa9ca79f4407fd8 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 0000000000000000000000000000000000000000..99dd6c628499f32bd2bc064968c3bff521cd1d24 --- /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 0000000000000000000000000000000000000000..27b7f30b67737d0fa085d663aca3c75afbb00724 --- /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 0000000000000000000000000000000000000000..6bb046372adbb3ec7e3e9b4f6a6a918e56c9630d --- /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 0000000000000000000000000000000000000000..c885d61ddd179f9cd5fa16090dc1754b83f222da --- /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; +}