diff --git a/README.md b/README.md index 33019bc..bff4737 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Features: - Manage dynamic modules (install and loading) - Deploy custom facts.d with sites config - Can listen with proxy protocol +- Generate certificates with acme.sh (let's encrypt) -- *EXPERIMENTAL* Requirements ------------ @@ -75,6 +76,7 @@ Fine configuration [FreeBSD](doc/freebsd.md) +[acme.sh](doc/acme.md) Note ---- diff --git a/defaults/main.yml b/defaults/main.yml index c6c153c..68c17bf 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -116,6 +116,14 @@ nginx_dh: null nginx_dh_path: '{{ nginx_ssl_dir }}/dhparam.pem' nginx_dh_length: 2048 +# +# acme.sh +# +nginx_acmesh: false +nginx_acmesh_dir: "/opt/acme.sh" +nginx_acmesh_git_dir: "/tmp/acme.sh" +nginx_acmesh_test: false + # Extra # Note: diff --git a/doc/acme.md b/doc/acme.md new file mode 100644 index 0000000..23fadfe --- /dev/null +++ b/doc/acme.md @@ -0,0 +1,15 @@ +acme.sh +======= + +Notes +----- + +This feature is experimental. + +Variables +--------- + +- `nginx_acmesh`: (bool) Enable/Disable acme.sh feature +- `nginx_acmesh_dir`: (string) Install directory +- `nginx_acmesh_git_dir`: (string) Git directory (removed after install) +- `nginx_acmesh_test`: (bool) If set to true (default false), uses test mode diff --git a/doc/ssl.md b/doc/ssl.md index 908ae14..dd7a065 100644 --- a/doc/ssl.md +++ b/doc/ssl.md @@ -52,6 +52,9 @@ nginx_sites; - name: 'test-ssl2.local' proto: ['http', 'https'] template: '_base' + - name: 'test-ssl3.local' + proto: ['http', 'https'] + template: '_base' nginx_ssl_pairs: - name: mysuperkey @@ -64,14 +67,6 @@ nginx_ssl_pairs: ....(snip).... -----END CERTIFICATE----- - name: test-ssl2.local - key: | - -----BEGIN RSA PRIVATE KEY----- - ....(snip).... - -----END RSA PRIVATE KEY----- - cert: | - -----BEGIN CERTIFICATE----- - ....(snip).... - -----END CERTIFICATE----- - + acme: true ``` diff --git a/tasks/main.yml b/tasks/main.yml index e709077..c951120 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -5,7 +5,7 @@ tags: ['nginx::site', 'nginx::ssl'] - name: INCLUDE | Install - include: install_{{ ansible_distribution }}.yml + include: "install_{{ ansible_distribution }}.yml" tags: ['nginx::site', 'nginx::ssl'] - name: INCLUDE | Prepare @@ -26,7 +26,7 @@ include: htpasswd.yml - name: INCLUDE | SSL configuration - include: ssl.yml + include: ssl/main.yml tags: ['nginx::ssl'] - name: INCLUDE | Sites configuration diff --git a/tasks/ssl/acme.yml b/tasks/ssl/acme.yml new file mode 100644 index 0000000..7efddfc --- /dev/null +++ b/tasks/ssl/acme.yml @@ -0,0 +1,82 @@ +--- + +- name: APT | Install git + apt: pkg=git + +- name: SET_FACT | Assign default.. + set_fact: + acme_create: [] + +- name: STAT | Check acme.sh is installed + stat: + path: "{{ nginx_acmesh_dir }}" + register: acme + +- block: + + - name: GIT | Get acme.sh + git: + repo: 'https://github.com/Neilpang/acme.sh.git' + dest: '{{ nginx_acmesh_git_dir }}' + update: no + + - name: SHELL | Install acme.sh + shell: ./acme.sh --install --home {{ nginx_acmesh_dir }} --cert-home {{ nginx_acmesh_dir }} + args: + chdir: "{{ nginx_acmesh_git_dir }}" + creates: "{{ nginx_acmesh_dir }}" + + when: not acme.stat.exists + +- name: STAT | Check if certificates are already installed + stat: + path: "{{ nginx_ssl_dir }}/{{ item | nginx_site_name }}/{{ item | nginx_site_name }}.crt" + with_items: "{{ nginx_ssl_pairs }}" + when: item.acme is defined and item.acme + register: acme_installed_certs + +- name: SET_FACT | Assign var with certificates to create + set_fact: + acme_create: "{{ acme_create | default([]) + [ (item.item | combine({'listen': ([item.item.acme_port]|default([])) }) ) ] }}" + with_items: "{{ acme_installed_certs.results }}" + when: item.skipped is not defined and not item.stat.exists + +- name: TEMPLATE | Create fake site + template: + src: "etc/nginx/sites-available/_base.j2" + dest: "{{ nginx_etc_dir }}/conf.d/FAKESITE_{{ item | nginx_site_name }}.conf" + with_items: "{{ acme_create }}" + register: fake_site + +- name: SERVICE | Reload nginx + service: + name: nginx + state: reloaded + when: fake_site.changed + +- name: SHELL | Get certificates + shell: '{{ nginx_acmesh_bin }} --issue{% if item.name is string %} -d {{ item.name }}{% else %}{% for name in item.name %} -d {{ name }}{% endfor %}{% endif %} --nginx {% if nginx_acmesh_test %}--test{% endif %}' + args: + creates: "/opt/acme.sh/{{ item | nginx_site_name }}/{{ item | nginx_site_name }}.key" + with_items: "{{ acme_create }}" + register: acme_get + failed_when: acme_get.rc != 0 and acme_get.rc != 2 + +- name: FILE | Create SSL dir per site + file: + path: "{{ nginx_ssl_dir }}/{{ item | nginx_site_name }}" + with_items: "{{ acme_create }}" + +- name: SHELL | Install certificates + shell: '{{ nginx_acmesh_bin }} --install-cert -d {{ item | nginx_site_name }} --fullchain-file {{ nginx_ssl_dir }}/{{ item | nginx_site_name }}/{{ item | nginx_site_name }}.crt --key-file {{ nginx_ssl_dir }}/{{ item | nginx_site_name }}/{{ item | nginx_site_name }}.key' + args: + creates: "{{ nginx_ssl_dir }}/{{ item | nginx_site_name }}/{{ item | nginx_site_name }}.key" + with_items: "{{ nginx_ssl_pairs }}" + when: item.acme is defined and item.acme + notify: restart nginx + +- name: FILE | Delete fake sites + file: + path: "{{ nginx_etc_dir }}/conf.d/FAKESITE_{{ item | nginx_site_name }}.conf" + state: absent + with_items: "{{ acme_create }}" diff --git a/tasks/ssl/main.yml b/tasks/ssl/main.yml new file mode 100644 index 0000000..8286ee2 --- /dev/null +++ b/tasks/ssl/main.yml @@ -0,0 +1,8 @@ +--- + +- name: INCLUDE | standard.yml + include: standard.yml + +- name: INCLUDE | acme.yml + include: acme.yml + when: nginx_acmesh diff --git a/tasks/ssl.yml b/tasks/ssl/standard.yml similarity index 100% rename from tasks/ssl.yml rename to tasks/ssl/standard.yml diff --git a/tests/includes/pre_Debian.yml b/tests/includes/pre_Debian.yml index 639d383..99eed08 100644 --- a/tests/includes/pre_Debian.yml +++ b/tests/includes/pre_Debian.yml @@ -50,11 +50,18 @@ cache_valid_time: 3600 state: present with_items: + - cron - curl - fcgiwrap + - jq - nghttp2 - strace - vim + - unzip + +- name: APT | Install daemonize from Stretch + apt: + deb: http://ftp.us.debian.org/debian/pool/main/d/daemonize/daemonize_1.7.7-1+b1_amd64.deb - name: APT | Install PHP apt: @@ -74,3 +81,37 @@ name: "{{ item.version | php_fpm_service }}" state: started with_items: "{{ nginx_php }}" + +- name: GET_URL | Download ngrok + get_url: + url: "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip" + dest: "/tmp/ngrok.zip" + +- name: UNARCHIVE | Uncompress ngrok + unarchive: + src: "/tmp/ngrok.zip" + dest: "/tmp" + remote_src: yes + +- name: SHELL | Check if ngrok is started + shell: ps aux | grep -q [n]grok + register: psngrok + changed_when: false + failed_when: false + +- block: + + - name: SHELL | Start ngrok + shell: daemonize /tmp/ngrok http 8888 -bind-tls=false + + - name: WAIT_FOR | ngrok started + wait_for: + delay: 2 + port: 4040 + + when: psngrok.rc > 0 + +- name: SHELL | Get ngrok public address + shell: curl 'http://127.0.0.1:4040/api/tunnels/command_line' | jq '.public_url' | grep -oE '[[:alnum:]]+\.ngrok\.io' + register: ngrok + changed_when: false diff --git a/tests/test.yml b/tests/test.yml index e75901c..6b3ce48 100644 --- a/tests/test.yml +++ b/tests/test.yml @@ -62,7 +62,12 @@ description: 'Please login!' users: [] state: 'absent' + nginx_acmesh: true + nginx_acmesh_test: true nginx_ssl_pairs: + - name: '{{ ngrok.stdout }}' + acme: true + acme_port: 8888 - name: 'test-ssl-predeployed.local' dest_key: "{{ int_ansible_ssl_dir }}/test.key" dest_cert: "{{ int_ansible_ssl_dir }}/test.crt" @@ -239,6 +244,12 @@ https_proxy_protocol_port: [20443] template: '_base' ssl_name: 'test-ssl.local' + - name: '{{ ngrok.stdout }}' + proto: ['http', 'https'] + template: '_base' + ssl_name: '{{ ngrok.stdout }}' + headers: + 'X-acme': '1' nginx_dh_length: 1024 roles: - ../../ @@ -272,6 +283,7 @@ - '{{ nginx_root }}/test-ssl.local/public' - '{{ nginx_root }}/test-ssl-predeployed.local/public' - '{{ nginx_root }}/test-ssl-proxy-protocol.local/public' + - '{{ nginx_root }}/{{ ngrok.stdout }}/public' - name: -- Create directory -- file: @@ -450,6 +462,7 @@ with_items: - 'test-ssl-predeployed.local' - 'test-ssl.local' + - '{{ ngrok.stdout }}' - name: -- VERIFY SSL REDIRECT -- uri: diff --git a/vars/main.yml b/vars/main.yml index 75923ad..dc7d2f0 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -43,3 +43,5 @@ nginx_servers_default_headers: 'X-Frame-Options': 'DENY always' 'X-Content-Type-Options': 'nosniff always' 'X-XSS-Protection': '1; mode=block' + +nginx_acmesh_bin: "{{ nginx_acmesh_dir }}/acme.sh"