diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml new file mode 100644 index 0000000..f5c4aae --- /dev/null +++ b/.github/workflows/molecule.yml @@ -0,0 +1,47 @@ +--- +on: + # Trigger the workflow on push or pull request, + # but only for the main branch + push: + branches: + - master + pull_request: + branches: + - master +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + with: + path: "${{ github.repository }}" + - name: molecule + uses: robertdebock/molecule-action@2.6.8 + with: + command: lint + test: + needs: + - lint + runs-on: ubuntu-latest + strategy: + matrix: + image: + - geerlingguy/docker-ubuntu2004-ansible:latest + - geerlingguy/docker-ubuntu1804-ansible:latest + - geerlingguy/docker-ubuntu1604-ansible:latest + - geerlingguy/docker-centos8-ansible:latest + - geerlingguy/docker-centos7-ansible:latest + steps: + - name: checkout + uses: actions/checkout@v2 + with: + path: "${{ github.repository }}" + - name: molecule + uses: robertdebock/molecule-action@2.6.8 + with: + image: "${{ matrix.image }}" + options: parallel + env: + MOLECULE_DOCKER_IMAGE: "${{ matrix.image }}" + max_failures: 1 diff --git a/README.md b/README.md index a516ccb..55f23cf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Ansible role: letsencrypt +![.github/workflows/molecule.yml](https://github.com/AcroMedia/ansible-role-letsencrypt/workflows/.github/workflows/molecule.yml/badge.svg) + For use on shared hosting servers. The role: - Installs LetsEncrypt, - Makes a `/.well-known/acme-challenge` virtual directory available to all virtual hosts on the server (including the default site), so all sites can regsiter and renew LE SSL certificates, diff --git a/defaults/main.yml b/defaults/main.yml index 18a5a23..4e2316e 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -26,3 +26,9 @@ letsencrypt_renew_cron_day: "*" letsencrypt_webroot: /var/www/letsencrypt letsencrypt_install_certbot_from_ppa: false + +# Since molecule tests cannot actually run with valid DNS, or even listen for +# external connections on port 80, we need to be able to simulate some pieces +# of the letsencrypt process in order to check the rest of the moving parts of the role. +# This variable should only ever be used by molecule tests. +letsencrypt_molecule_mock_mode: false diff --git a/molecule/default/INSTALL.rst b/molecule/default/INSTALL.rst new file mode 100644 index 0000000..6a44bde --- /dev/null +++ b/molecule/default/INSTALL.rst @@ -0,0 +1,22 @@ +******* +Docker driver installation guide +******* + +Requirements +============ + +* Docker Engine + +Install +======= + +Please refer to the `Virtual environment`_ documentation for installation best +practices. If not using a virtual environment, please consider passing the +widely recommended `'--user' flag`_ when invoking ``pip``. + +.. _Virtual environment: https://virtualenv.pypa.io/en/latest/ +.. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site + +.. code-block:: bash + + $ pip install 'molecule[docker]' diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..b5d2c96 --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + become: true + gather_facts: true + roles: + - role: ansible-role-letsencrypt diff --git a/molecule/default/group_vars/all.yml b/molecule/default/group_vars/all.yml new file mode 100644 index 0000000..bcdeab1 --- /dev/null +++ b/molecule/default/group_vars/all.yml @@ -0,0 +1,4 @@ +--- +default_mail_recipient: webmaster@example.com +letsencrypt_create_default_server_cert: false +letsencrypt_molecule_mock_mode: true diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..7d4ff10 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,20 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: instance + image: ${MOLECULE_DOCKER_IMAGE:-'geerlingguy/docker-ubuntu1804-ansible:latest'} + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: true + pre_build_image: true +provisioner: + name: ansible + playbooks: + converge: ${MOLECULE_PLAYBOOK:-converge.yml} + config_options: + defaults: + verbosity: ${MOLECULE_VERBOSITY:-0} diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml new file mode 100644 index 0000000..edece61 --- /dev/null +++ b/molecule/default/prepare.yml @@ -0,0 +1,40 @@ +- name: Prepare + hosts: all + become: true + gather_facts: yes + tasks: + + - name: Update yum cache + yum: + update_cache: yes + when: ansible_os_family == 'RedHat' + + - name: Update cache + apt: + update_cache: yes + when: ansible_os_family == 'Debian' + + - name: Ensure python3 is installed + package: + name: + - python3 + state: present + + - name: Ensure dirmngr is installed (required to install software from PPAs) + apt: + name: dirmngr + state: present + when: ansible_os_family == 'Debian' + + - name: Ensure cron is installed + yum: + name: cronie + state: present + when: ansible_os_family == 'RedHat' + +- name: Install role dependencies that acromedia.letsencrypt will need + hosts: all + become: yes + gather_facts: yes + roles: + - contrib/acromedia.nginx diff --git a/molecule/default/requirements.yml b/molecule/default/requirements.yml new file mode 100644 index 0000000..edfbe62 --- /dev/null +++ b/molecule/default/requirements.yml @@ -0,0 +1,6 @@ +--- + +- name: contrib/acromedia.nginx + src: https://github.com/AcroMedia/ansible-role-nginx + version: origin/master + diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml new file mode 100644 index 0000000..b1bd039 --- /dev/null +++ b/molecule/default/verify.yml @@ -0,0 +1,17 @@ +--- +- name: Converge + hosts: all + become: true + gather_facts: true + tasks: + - name: Trivailly execute the certbot script, just to make sure it's present, + but only call it with the --help flag, so it does not try to do any work. + The certbot-auto script is no longer supported, and has been replaced + by snapd. This upgrade will be addressed in a new pull request. + shell: /usr/local/bin/certbot-auto --help + register: certbot_result + + - name: Ensure output looks as expected + assert: + that: + '"A self-updating wrapper script for the Certbot ACME client." in certbot_result.stdout' diff --git a/tasks/apache_default_ssl.yml b/tasks/apache_default_ssl.yml index 5279a24..96d8722 100644 --- a/tasks/apache_default_ssl.yml +++ b/tasks/apache_default_ssl.yml @@ -11,8 +11,8 @@ backup: true notify: restart apache when: default_cert_retest is defined - and default_cert_retest.rc is defined - and default_cert_retest.rc == 0 + and default_cert_retest.stat is defined + and default_cert_retest.stat.exists - name: Disable the factory default site on EL Apache 2.2 (we already set up in the welcome file) template: @@ -21,7 +21,7 @@ backup: true notify: restart apache when: default_cert_retest is defined - and default_cert_retest.rc is defined - and default_cert_retest.rc == 0 + and default_cert_retest.stat is defined + and default_cert_retest.stat.exists and ansible_os_family == 'RedHat' and apache_version is version('2.4.0', '<' ) diff --git a/tasks/main.yml b/tasks/main.yml index 50a1f9f..f86509c 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -111,10 +111,9 @@ creates: /usr/local/ssl/private/dhparams.pem - name: Stat the default site SSL cert - shell: "test -e /etc/letsencrypt/live/{{ default_site_fqdn }}" + stat: + path: "/etc/letsencrypt/live/{{ default_site_fqdn }}" register: default_cert_result - changed_when: "default_cert_result.rc != 0" - ignore_errors: true - name: Compose the certbot command string for the default site cert set_fact: @@ -132,16 +131,17 @@ {{ certbot_command_string }} --dry-run && {{ certbot_command_string }} register: certbot_result when: default_cert_result is defined - and default_cert_result.rc is defined - and default_cert_result.rc != 0 + and default_cert_result.exists is defined + and (not default_cert_result.exists) + and (not letsencrypt_molecule_mock_mode) environment: DEBIAN_FRONTEND: noninteractive - name: Re-stat default site SSL cert - shell: "test -e /etc/letsencrypt/live/{{ default_site_fqdn }}" + stat: + path: "/etc/letsencrypt/live/{{ default_site_fqdn }}" register: default_cert_retest - ignore_errors: true - changed_when: default_cert_retest.rc != default_cert_result.rc - name: Run {{ letsencrypt_webserver }}_default_ssl.yml playbook include: "{{ letsencrypt_webserver }}_default_ssl.yml" + when: not letsencrypt_molecule_mock_mode diff --git a/tasks/nginx_default_ssl.yml b/tasks/nginx_default_ssl.yml index 8657e4d..c1c1a55 100644 --- a/tasks/nginx_default_ssl.yml +++ b/tasks/nginx_default_ssl.yml @@ -5,4 +5,6 @@ dest: "{{ nginx_vhost_conf_dir }}/{{ nginx_default_vhost_filename }}" backup: yes notify: reload nginx - when: default_cert_retest.rc == 0 + when: default_cert_retest is defined + and default_cert_retest.stat is defined + and default_cert_retest.stat.exists