diff --git a/README.md b/README.md index dee728c..962a69b 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,17 @@ The order of deployment is: - node.yaml - site.yaml - harden.yaml +- certbot.yaml + +The last line adds support for Let's Encrypt, which you can configure and enable (updating your Nginx setup) with: + +``` +sudo /opt/certbot/certbot-auto --nginx certonly +``` + +If you do **not** wish to use SSL, delete the last part of your nginx site configuration (/etc/nginx/sites-enabled/...). + +### Production releases For further deployment and system maintenance we have a `Makefile` which automates Docker Compose tasks. This should be converted to use [Ansible Container](http://docs.ansible.com/ansible-container/getting_started.html). In the meantime, start a release with Ansible, then complete it using `make`, i.e.: diff --git a/ansible/certbot.yaml b/ansible/certbot.yaml new file mode 100644 index 0000000..a99130c --- /dev/null +++ b/ansible/certbot.yaml @@ -0,0 +1,13 @@ +- hosts: webservers + become: true + become_method: 'sudo' + gather_facts: yes + vars: + certbot_auto_renew_user: ansible + certbot_auto_renew_minute: 20 + certbot_auto_renew_hour: 5 + certbot_dir: /opt/certbot + certbot_install_from_source: yes + certbot_version: v0.14.2 + roles: + - geerlingguy.certbot diff --git a/ansible/harden.yaml b/ansible/harden.yaml index 26fe918..134414c 100644 --- a/ansible/harden.yaml +++ b/ansible/harden.yaml @@ -3,8 +3,6 @@ gather_facts: True roles: - role: dev-sec.os-hardening - - role: dev-sec.ssh-hardening become: yes - - dev-sec.nginx-hardening diff --git a/ansible/inventories/production/webservers b/ansible/inventories/production/webservers index 33d2f8a..ec03451 100644 --- a/ansible/inventories/production/webservers +++ b/ansible/inventories/production/webservers @@ -1,13 +1,13 @@ $ANSIBLE_VAULT;1.1;AES256 -62616437613934626365626438656234636664643335663066646436613330663835396363346238 -3133326135666431313564623731656233633735393865650a373964373365663731653930376336 -61396433376463376164366665393731326665633762353134323265623736323337643131363866 -3334623134323365340a323964373565393066633637616433613933666262356661623733333461 -62366165393537333033363364393232356137353231323433323833326262643132393133313232 -66383232646263363532633037306137333830613062306261326539393462373937623132636436 -33306565353935663462643830363135396263373232396330633735633032353236306630303662 -62623966383163633230366139663463326661383065383435666561376239316537373333376434 -32353039623261313562373463633437383265633636373238636662643439366338613038623263 -39323062646261623961363537633062356531333230356634613835383334316532343863363131 -66363761353761653564303565313564613039393032343536313430666131616131363835663133 -38363331323462666237 +33346132656133323263366630316265383032356264303236306562366336346133303931366363 +6166623234393661383738316361653639353863323062660a333966363436333031363936396438 +31333262343961646537623534373165363735316566653133626435633538306636616162306535 +3336396134363466350a363839663636313862643531633762626563396333646136376430643634 +39383738613433643139323263613061353464383864636334313865313162316236343261333237 +36303836313661336534613630663161633163646131376238643335303663653338363261633165 +62623833626462633563386631643962393936646563663738623530666332353836303062376635 +64326237663737316438633063343935383463663937373634303236376635643062666664633132 +63633236373462376637363563363564643433356138636233363034306130306139306461373331 +39396564303963633135303536383862336562623663336636373435373366353934613964353863 +36313638623335356562626135383337656632333530333561623265323930323732333736343532 +31366239663161663562 diff --git a/ansible/roles/geerlingguy.certbot/.gitignore b/ansible/roles/geerlingguy.certbot/.gitignore new file mode 100644 index 0000000..c9b2377 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/.gitignore @@ -0,0 +1,2 @@ +*.retry +tests/test.sh diff --git a/ansible/roles/geerlingguy.certbot/.travis.yml b/ansible/roles/geerlingguy.certbot/.travis.yml new file mode 100644 index 0000000..47ab110 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/.travis.yml @@ -0,0 +1,23 @@ +--- +services: docker + +env: + - distro: centos7 + - distro: centos6 + playbook: test-source-install.yml + - distro: ubuntu1604 + - distro: ubuntu1404 + playbook: test-source-install.yml + - distro: debian8 + playbook: test-source-install.yml + +script: + # Download test shim. + - wget -O ${PWD}/tests/test.sh https://gist.githubusercontent.com/geerlingguy/73ef1e5ee45d8694570f334be385e181/raw/ + - chmod +x ${PWD}/tests/test.sh + + # Run tests. + - ${PWD}/tests/test.sh + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/ansible/roles/geerlingguy.certbot/LICENSE b/ansible/roles/geerlingguy.certbot/LICENSE new file mode 100644 index 0000000..4275cf3 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 Jeff Geerling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ansible/roles/geerlingguy.certbot/README.md b/ansible/roles/geerlingguy.certbot/README.md new file mode 100644 index 0000000..482c55b --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/README.md @@ -0,0 +1,75 @@ +# Ansible Role: Certbot (for Let's Encrypt) + +[![Build Status](https://travis-ci.org/geerlingguy/ansible-role-certbot.svg?branch=master)](https://travis-ci.org/geerlingguy/ansible-role-certbot) + +Installs and configures Certbot (for Let's Encrypt). + +## Requirements + +If installing from source, Git is required. You can install Git using the `geerlingguy.git` role. + +## Role Variables + +The variable `certbot_install_from_source` controls whether to install Certbot from Git or package management. The latter is the default, so the variable defaults to `no`. + + certbot_auto_renew: true + certbot_auto_renew_user: "{{ ansible_user }}" + certbot_auto_renew_hour: 3 + certbot_auto_renew_minute: 30 + +By default, this role configures a cron job to run under the provided user account at the given hour and minute, every day. The defaults run `certbot renew` (or `certbot-auto renew`) via cron every day at 03:30:00 by the user you use in your Ansible playbook. It's preferred that you set a custom user/hour/minute so the renewal is during a low-traffic period and done by a non-root user account. + +### Source Installation from Git + +You can install Certbot from it's Git source repository if desired. This might be useful in several cases, but especially when older distributions don't have Certbot packages available (e.g. CentOS < 7, Ubuntu < 16.10 and Debian < 8). + + certbot_install_from_source: no + certbot_repo: https://github.com/certbot/certbot.git + certbot_version: master + certbot_keep_updated: yes + +Certbot Git repository options. To install from source, set `certbot_install_from_source` to `yes`. This clones the configured `certbot_repo`, respecting the `certbot_version` setting. If `certbot_keep_updated` is set to `yes`, the repository is updated every time this role runs. + + certbot_dir: /opt/certbot + +The directory inside which Certbot will be cloned. + +## Dependencies + +None. + +## Example Playbook + + - hosts: servers + + vars: + certbot_auto_renew_user: your_username_here + certbot_auto_renew_minute: 20 + certbot_auto_renew_hour: 5 + + roles: + - geerlingguy.certbot + +After installation, you can create certificates using the `certbot` (or `certbot-auto`) script, which by default is installed inside the configured `certbot_dir` (when using Git). Here are some example commands to configure certificates with Certbot: + + # Automatically add certs for all Apache virtualhosts (use with caution!). + /opt/certbot/certbot-auto --apache + + # Generate certs, but don't modify Apache configuration (safer). + /opt/certbot/certbot-auto --apache certonly + +By default, this role adds a cron job that will renew all installed certificates once per day at the hour and minute of your choosing. + +You can test the auto-renewal (without actually renewing the cert) with the command: + + /opt/certbot/certbot-auto renew --dry-run + +See full documentation and options on the [Certbot website](https://certbot.eff.org/). + +## License + +MIT / BSD + +## Author Information + +This role was created in 2016 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). diff --git a/ansible/roles/geerlingguy.certbot/defaults/main.yml b/ansible/roles/geerlingguy.certbot/defaults/main.yml new file mode 100644 index 0000000..7f6f0d3 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/defaults/main.yml @@ -0,0 +1,16 @@ +--- +# Certbot auto-renew cron job configuration (for certificate renewals). +certbot_auto_renew: true +certbot_auto_renew_user: "{{ ansible_user }}" +certbot_auto_renew_hour: 3 +certbot_auto_renew_minute: 30 + +# To install from source (on older OSes or if you need a specific or newer +# version of Certbot), set this variable to `yes` and configure other options. +certbot_install_from_source: no +certbot_repo: https://github.com/certbot/certbot.git +certbot_version: master +certbot_keep_updated: yes + +# Where to put Certbot when installing from source. +certbot_dir: /opt/certbot diff --git a/ansible/roles/geerlingguy.certbot/meta/.galaxy_install_info b/ansible/roles/geerlingguy.certbot/meta/.galaxy_install_info new file mode 100644 index 0000000..55d1c6e --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Fri Jun 2 09:43:15 2017', version: 2.0.0} diff --git a/ansible/roles/geerlingguy.certbot/meta/main.yml b/ansible/roles/geerlingguy.certbot/meta/main.yml new file mode 100644 index 0000000..aa00a88 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/meta/main.yml @@ -0,0 +1,33 @@ +--- +dependencies: [] + +galaxy_info: + author: geerlingguy + description: "Installs and configures Certbot (for Let's Encrypt)." + company: "Midwestern Mac, LLC" + license: "license (BSD, MIT)" + min_ansible_version: 2.0 + platforms: + - name: EL + versions: + - 6 + - 7 + - name: Fedora + versions: + - all + - name: Ubuntu + versions: + - all + - name: Debian + versions: + - all + galaxy_tags: + - networking + - system + - web + - certbot + - letsencrypt + - encryption + - certificates + - ssl + - https diff --git a/ansible/roles/geerlingguy.certbot/tasks/include-vars.yml b/ansible/roles/geerlingguy.certbot/tasks/include-vars.yml new file mode 100644 index 0000000..0a70e50 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tasks/include-vars.yml @@ -0,0 +1,8 @@ +--- +- name: Load a variable file based on the OS type, or a default if not found. + include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml" + - "{{ ansible_distribution }}.yml" + - "{{ ansible_os_family }}.yml" + - "default.yml" diff --git a/ansible/roles/geerlingguy.certbot/tasks/install-from-source.yml b/ansible/roles/geerlingguy.certbot/tasks/install-from-source.yml new file mode 100644 index 0000000..7d97f9b --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tasks/install-from-source.yml @@ -0,0 +1,17 @@ +--- +- name: Clone Certbot into configured directory. + git: + repo: "{{ certbot_repo }}" + dest: "{{ certbot_dir }}" + version: "{{ certbot_version }}" + update: "{{ certbot_keep_updated }}" + force: yes + +- name: Set Certbot script variable. + set_fact: + certbot_script: "{{ certbot_dir }}/certbot-auto" + +- name: Ensure certbot-auto is executable. + file: + path: "{{ certbot_script }}" + mode: 0755 diff --git a/ansible/roles/geerlingguy.certbot/tasks/install-with-package.yml b/ansible/roles/geerlingguy.certbot/tasks/install-with-package.yml new file mode 100644 index 0000000..10490ff --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tasks/install-with-package.yml @@ -0,0 +1,7 @@ +--- +- name: Install Certbot. + package: "name={{ certbot_package }} state=present" + +- name: Set Certbot script variable. + set_fact: + certbot_script: "{{ certbot_package }}" diff --git a/ansible/roles/geerlingguy.certbot/tasks/main.yml b/ansible/roles/geerlingguy.certbot/tasks/main.yml new file mode 100644 index 0000000..5324ff9 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- include: include-vars.yml + +- include: install-with-package.yml + when: not certbot_install_from_source + +- include: install-from-source.yml + when: certbot_install_from_source + +- include: renew-cron.yml + when: certbot_auto_renew diff --git a/ansible/roles/geerlingguy.certbot/tasks/renew-cron.yml b/ansible/roles/geerlingguy.certbot/tasks/renew-cron.yml new file mode 100644 index 0000000..7678a7c --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tasks/renew-cron.yml @@ -0,0 +1,8 @@ +--- +- name: Add cron job for certbot renewal (if configured). + cron: + name: Certbot automatic renewal. + job: "{{ certbot_script }} renew --quiet --no-self-upgrade" + minute: "{{ certbot_auto_renew_minute }}" + hour: "{{ certbot_auto_renew_hour }}" + user: "{{ certbot_auto_renew_user }}" diff --git a/ansible/roles/geerlingguy.certbot/tests/README.md b/ansible/roles/geerlingguy.certbot/tests/README.md new file mode 100644 index 0000000..6fb2117 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tests/README.md @@ -0,0 +1,11 @@ +# Ansible Role tests + +To run the test playbook(s) in this directory: + + 1. Install and start Docker. + 1. Download the test shim (see .travis.yml file for the URL) into `tests/test.sh`: + - `wget -O tests/test.sh https://gist.githubusercontent.com/geerlingguy/73ef1e5ee45d8694570f334be385e181/raw/` + 1. Make the test shim executable: `chmod +x tests/test.sh`. + 1. Run (from the role root directory) `distro=[distro] playbook=[playbook] ./tests/test.sh` + +If you don't want the container to be automatically deleted after the test playbook is run, add the following environment variables: `cleanup=false container_id=$(date +%s)` diff --git a/ansible/roles/geerlingguy.certbot/tests/requirements.yml b/ansible/roles/geerlingguy.certbot/tests/requirements.yml new file mode 100644 index 0000000..6208520 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tests/requirements.yml @@ -0,0 +1,2 @@ +--- +- src: geerlingguy.git diff --git a/ansible/roles/geerlingguy.certbot/tests/test-source-install.yml b/ansible/roles/geerlingguy.certbot/tests/test-source-install.yml new file mode 100644 index 0000000..d2a3d73 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tests/test-source-install.yml @@ -0,0 +1,23 @@ +--- +- hosts: all + + vars: + certbot_install_from_source: yes + + pre_tasks: + - name: Update apt cache. + apt: update_cache=yes cache_valid_time=600 + when: ansible_os_family == 'Debian' + changed_when: false + + - name: Install cron (RedHat). + yum: name=cronie state=present + when: ansible_os_family == 'RedHat' + + - name: Install cron (Debian). + apt: name=cron state=present + when: ansible_os_family == 'Debian' + + roles: + - geerlingguy.git + - role_under_test diff --git a/ansible/roles/geerlingguy.certbot/tests/test.yml b/ansible/roles/geerlingguy.certbot/tests/test.yml new file mode 100644 index 0000000..217fc45 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/tests/test.yml @@ -0,0 +1,19 @@ +--- +- hosts: all + + pre_tasks: + - name: Update apt cache. + apt: update_cache=yes cache_valid_time=600 + when: ansible_os_family == 'Debian' + changed_when: false + + - name: Install cron (RedHat). + yum: name=cronie state=present + when: ansible_os_family == 'RedHat' + + - name: Install cron (Debian). + apt: name=cron state=present + when: ansible_os_family == 'Debian' + + roles: + - role_under_test diff --git a/ansible/roles/geerlingguy.certbot/vars/Ubuntu-16.04.yml b/ansible/roles/geerlingguy.certbot/vars/Ubuntu-16.04.yml new file mode 100644 index 0000000..90e9138 --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/vars/Ubuntu-16.04.yml @@ -0,0 +1 @@ +certbot_package: letsencrypt diff --git a/ansible/roles/geerlingguy.certbot/vars/default.yml b/ansible/roles/geerlingguy.certbot/vars/default.yml new file mode 100644 index 0000000..d88f2dc --- /dev/null +++ b/ansible/roles/geerlingguy.certbot/vars/default.yml @@ -0,0 +1,2 @@ +--- +certbot_package: certbot diff --git a/ansible/roles/web/tasks/nginx.yaml b/ansible/roles/web/tasks/nginx.yaml index c3d93dd..71c752c 100644 --- a/ansible/roles/web/tasks/nginx.yaml +++ b/ansible/roles/web/tasks/nginx.yaml @@ -7,6 +7,12 @@ src: nginx.conf.j2 dest: /etc/nginx/sites-available/{{ domain }} +- name: Copy extra Nginx site config + become: true + template: + src: ph-extra-nginx.conf.j2 + dest: /etc/nginx/sites-available/extra-{{ domain }} + - name: Activate Nginx site config become: true file: diff --git a/ansible/roles/web/templates/nginx.conf.j2 b/ansible/roles/web/templates/nginx.conf.j2 index aedb183..b351e07 100644 --- a/ansible/roles/web/templates/nginx.conf.j2 +++ b/ansible/roles/web/templates/nginx.conf.j2 @@ -50,41 +50,17 @@ server { proxy_redirect off; proxy_pass http://wagtail-site; } -} -# Web archive configuration -server { - listen 80; - server_name www-old.{{ domain }}; - index index.html index.htm; - root {{ archive_dir }}; - location / { - try_files $uri $uri/ =404; + # Enable secure site support + listen [::]:443; + listen 443 ssl; + ssl on; + ssl_certificate /etc/letsencrypt/live/{{ domain }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + + if ($scheme != "https") { + return 301 https://$host$request_uri; } -} -# Beta site configuration -server { - listen 80; - server_name beta.{{ domain }} www.{{ domain }}; - return 301 $scheme://{{ domain }}; -} - -# TODO: parameterize -server { - listen 80; listen 443 ssl; - server_name conference.{{ domain }}; - location /fr { - return 301 $scheme://sph17.organizers-congress.org/frontend/index.php?sub=89; - } - location / { - return 301 $scheme://sph17.organizers-congress.org; - } -} - -# TODO: parameterize -server { - listen 80; listen 443 ssl; - server_name sphc.ch; - return 301 $scheme://sph17.organizers-congress.org; } diff --git a/ansible/roles/web/templates/ph-extra-nginx.conf.j2 b/ansible/roles/web/templates/ph-extra-nginx.conf.j2 new file mode 100644 index 0000000..abe43b4 --- /dev/null +++ b/ansible/roles/web/templates/ph-extra-nginx.conf.j2 @@ -0,0 +1,32 @@ +#{{ ansible_managed }} + +# Web archive and other special configurations for public-health.ch + +server { + listen 80; + server_name www-old.{{ domain }}; + index index.html index.htm; + root {{ archive_dir }}; + location / { + try_files $uri $uri/ =404; + } +} + +# TODO: parameterize +server { + listen 80; + server_name conference.{{ domain }}; + location /fr { + return 301 $scheme://sph17.organizers-congress.org/frontend/index.php?sub=89; + } + location / { + return 301 $scheme://sph17.organizers-congress.org; + } +} + +# TODO: parameterize +server { + listen 80; + server_name sphc.ch; + return 301 $scheme://sph17.organizers-congress.org; +}