Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple certs #1

Merged
merged 5 commits into from
Nov 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ First, read Let's Encrypt's TOS and EULA. Only proceed if you agree to them.

The following variables are available:

`letsencrypt_webroot_path` is the root path that gets served by your web server. Defaults to `/var/www`.
`letsencrypt_webroot_path` is the default root path that gets served by your web server. Defaults to `/var/www`.

`letsencrypt_certs` array of dicts with keys `domains` (array), `webroot` (string), `ssl_certificate` (string), `ssl_certificate_key` (string), one item matches one certificate.

`letsencrypt_email` needs to be set to your email address. Let's Encrypt wants it. Defaults to `webmaster@{{ ansible_fqdn }}`. If you _really_ want to register without providing an email address, define the variabe `letsencrypt_no_email`.

`letsencrypt_rsa_key_size` allows to specify a size for the generated key.

`letsencrypt_cert_domains` is a list of domains you wish to get a certificate for. It defaults to a single item with the value of `{{ ansible_fqdn }}`.

`letsencrypt_install_directory` should probably be left alone, but if you set it, it will change where the letsencrypt program is installed.

`letsencrypt_renewal_command_args` add arguments to the `letsencrypt renewal` command that gets run using cron. For example, use the renewal hooks to restart a web server.
Expand All @@ -37,6 +37,10 @@ The following variables are available:

`letsencrypt_server` sets the alternative auth server if needed. For example, during tests it's set to `https://acme-staging.api.letsencrypt.org/directory` to use the staging server (far higher rate limits, but certs are not trusted). It is not set by default.

Legacy variables for generate single certificate:

`letsencrypt_cert_domains` is a list of domains you wish to get a certificate for. It defaults to a single item with the value of `{{ ansible_fqdn }}`.

`ssl_certificate` and `ssl_certificate_key` symlinks the certificates to provided path if both are set.

The [Let's Encrypt client](https://github.com/letsencrypt/letsencrypt) will put the certificate and accessories in `/etc/letsencrypt/live/<first listed domain>/`. For more info, see the [Let's Encrypt documentation](https://letsencrypt.readthedocs.org/en/latest/using.html#where-are-my-certificates).
Expand All @@ -48,10 +52,16 @@ The [Let's Encrypt client](https://github.com/letsencrypt/letsencrypt) will put
user: root
roles:
- role: letsencrypt
letsencrypt_webroot_path: /var/www/html
letsencrypt_certs:
- domains:
- www.example.net
- example.net
ssl_certificate: /path/to/symlink/fullchain.pem
ssl_certificate_key: /path/to/key.pem
- webroot: /var/www/example
domains:
- www.example2.net
- example2.net
letsencrypt_email: [email protected]
letsencrypt_cert_domains:
- www.example.net
- example.net
letsencrypt_renewal_command_args: '--renew-hook "systemctl restart nginx"'
```
10 changes: 7 additions & 3 deletions defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
---
letsencrypt_src_directory: /usr/local/share/letsencrypt
letsencrypt_venv: "{{ letsencrypt_src_directory }}/env"
letsencrypt_cert_domains:
- "{{ ansible_fqdn }}"
letsencrypt_cert_domains: # legacy
letsencrypt_webroot_path: /var/www
letsencrypt_certs:
- webroot: /var/www
domains:
- "{{ ansible_fqdn }}"
ssl_certificate:
ssl_certificate_key:
letsencrypt_authenticator: webroot
letsencrypt_email: "webmaster@{{ ansible_domain }}"
letsencrypt_path: "{{ letsencrypt_venv }}/bin/letsencrypt"
letsencrypt_command: "{{ letsencrypt_path }} -n --agree-tos {% if letsencrypt_rsa_key_size is defined %}--rsa-key-size {{ letsencrypt_rsa_key_size }}{% endif %} --text {% for domain in letsencrypt_cert_domains %}-d {{ domain }} {% endfor %}{% if letsencrypt_no_email is defined %}--register-unsafely-without-email{% else %}--email {{ letsencrypt_email }}{% endif %} {% if letsencrypt_server is defined %}--server {{ letsencrypt_server }}{% endif %} --expand"
letsencrypt_renewal_frequency:
day: "*"
hour: 0
Expand Down
84 changes: 84 additions & 0 deletions tasks/cert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
- name: "{{ letsencrypt_cert.domains[0] }} : Define letsencrypt_command"
set_fact:
letsencrypt_command: "{{ letsencrypt_path }} -n --agree-tos {% if letsencrypt_rsa_key_size is defined %}--rsa-key-size {{ letsencrypt_rsa_key_size }}{% endif %} --text {% for domain in letsencrypt_cert.domains %}-d {{ domain }} {% endfor %}{% if letsencrypt_no_email is defined %}--register-unsafely-without-email{% else %}--email {{ letsencrypt_email }}{% endif %} {% if letsencrypt_server is defined %}--server {{ letsencrypt_server }}{% endif %} --expand"

- name: "{{ letsencrypt_cert.domains[0] }} : Define webroot"
set_fact:
webroot: "{% if letsencrypt_cert.webroot is defined %}{{ letsencrypt_cert.webroot }}{% else %}{{ letsencrypt_webroot_path }}{% endif %}"

- name: "{{ letsencrypt_cert.domains[0] }} : Ensure webroot {{ webroot }} exists"
file:
path: "{{ webroot }}"
state: directory
follow: yes
become: yes

- name: "{{ letsencrypt_cert.domains[0] }} : Attempt to get the certificate using the webroot authenticator"
command: "{{ letsencrypt_command }} -a webroot --webroot-path {{ webroot }} certonly"
become: yes
args:
creates: "/etc/letsencrypt/live/{{ letsencrypt_cert.domains[0] }}"
when: letsencrypt_authenticator == "webroot"
ignore_errors: True

- name: "{{ letsencrypt_cert.domains[0] }} : Attempt to get the certificate using the standalone authenticator (in case eg the webserver isn't running yet)"
command: "{{ letsencrypt_command }} -a standalone auth {{ letsencrypt_standalone_command_args }}"
become: yes
args:
creates: "/etc/letsencrypt/live/{{ letsencrypt_cert.domains[0] }}"

- name: "{{ letsencrypt_cert.domains[0] }} : Fix the renewal file"
ini_file:
section: renewalparams
option: "{{ item.key }}"
value: "{{ item.value }}"
dest: "/etc/letsencrypt/renewal/{{ letsencrypt_cert.domains[0] }}.conf"
become: yes
with_dict:
os_packages_only: False
verb: certonly
noninteractive_mode: False
uir: False
hsts: False
authenticator: '{{ letsencrypt_authenticator }}'

- name: "{{ letsencrypt_cert.domains[0] }} : Fix the webroot map in the renewal file"
ini_file:
section: "[webroot_map]"
option: "{{ item }}"
value: "{{ webroot }}"
dest: "/etc/letsencrypt/renewal/{{ letsencrypt_cert.domains[0] }}.conf"
become: yes
with_items: "{{ letsencrypt_cert.domains }}"

- name: "{{ letsencrypt_cert.domains[0] }} : Install renewal cron"
become: yes
cron:
name: "Let's Encrypt Renewal"
day: "{{ letsencrypt_renewal_frequency.day }}"
hour: "{{ letsencrypt_renewal_frequency.hour }}"
minute: "{{ letsencrypt_renewal_frequency.minute }}"
job: "{{letsencrypt_path}} renew --quiet {{ letsencrypt_renewal_command_args }}"

- name: "{{ letsencrypt_cert.domains[0] }} : Create directory for `ssl_certificate` and `ssl_certificate_key`"
file:
path: '{{ item }}'
state: directory
recurse: yes
when: letsencrypt_cert.ssl_certificate is defined and letsencrypt_cert.ssl_certificate_key is defined and letsencrypt_cert.ssl_certificate and letsencrypt_cert.ssl_certificate_key
with_items:
- "{{ letsencrypt_cert.ssl_certificate|dirname }}"
- "{{ letsencrypt_cert.ssl_certificate_key|dirname }}"

- name: "{{ letsencrypt_cert.domains[0] }} : Symlink certificates to `ssl_certificate` and `ssl_certificate_key`"
file:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
state: link
when: letsencrypt_cert.ssl_certificate is defined and letsencrypt_cert.ssl_certificate_key is defined and letsencrypt_cert.ssl_certificate and letsencrypt_cert.ssl_certificate_key
with_items:
- src: /etc/letsencrypt/live/{{ letsencrypt_cert.domains[0] }}/fullchain.pem
dest: "{{letsencrypt_cert.ssl_certificate}}"
- src: /etc/letsencrypt/live/{{ letsencrypt_cert.domains[0] }}/privkey.pem
dest: "{{letsencrypt_cert.ssl_certificate_key}}"
1 change: 1 addition & 0 deletions tasks/debian-jessie.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
- name: change the path to letsencrypt
set_fact:
letsencrypt_path: "/usr/bin/letsencrypt"
tags: configure
1 change: 1 addition & 0 deletions tasks/debian-stretch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
- name: change the path to letsencrypt
set_fact:
letsencrypt_path: "/usr/bin/letsencrypt"
tags: configure
96 changes: 21 additions & 75 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,87 +16,33 @@
- name: install certbot (Debian jessie)
include: debian-jessie.yml
when: ansible_distribution == "Debian" and ansible_distribution_release == "jessie"
tags: install

- name: install certbot (Debian stretch)
include: debian-stretch.yml
when: ansible_distribution == "Debian" and ansible_distribution_release == "stretch"
tags: install

- name: install certbot (using pip)
include: notdebian.yml
when: ansible_distribution != "Debian"
tags: install

- name: Ensure webroot exists
file:
path: "{{ letsencrypt_webroot_path }}"
state: directory
follow: yes
become: yes

- name: Attempt to get the certificate using the webroot authenticator
command: "{{ letsencrypt_command }} -a webroot --webroot-path {{ letsencrypt_webroot_path }} certonly"
become: yes
args:
creates: "/etc/letsencrypt/live/{{ letsencrypt_cert_domains[0] }}"
when: letsencrypt_authenticator == "webroot"
ignore_errors: True

- name: Attempt to get the certificate using the standalone authenticator (in case eg the webserver isn't running yet)
command: "{{ letsencrypt_command }} -a standalone auth {{ letsencrypt_standalone_command_args }}"
become: yes
args:
creates: "/etc/letsencrypt/live/{{ letsencrypt_cert_domains[0] }}"

- name: Fix the renewal file
ini_file:
section: renewalparams
option: "{{ item.key }}"
value: "{{ item.value }}"
dest: "/etc/letsencrypt/renewal/{{ letsencrypt_cert_domains[0] }}.conf"
become: yes
with_dict:
os_packages_only: False
verb: certonly
noninteractive_mode: False
uir: False
hsts: False
authenticator: '{{ letsencrypt_authenticator }}'

- name: Fix the webroot map in the renewal file
ini_file:
section: "[webroot_map]"
option: "{{ item }}"
value: "{{ letsencrypt_webroot_path }}"
dest: "/etc/letsencrypt/renewal/{{ letsencrypt_cert_domains[0] }}.conf"
become: yes
with_items: "{{ letsencrypt_cert_domains }}"

- name: Install renewal cron
become: yes
cron:
name: "Let's Encrypt Renewal"
day: "{{ letsencrypt_renewal_frequency.day }}"
hour: "{{ letsencrypt_renewal_frequency.hour }}"
minute: "{{ letsencrypt_renewal_frequency.minute }}"
job: "{{letsencrypt_path}} renew --quiet {{ letsencrypt_renewal_command_args }}"

- name: Create directory for `ssl_certificate` and `ssl_certificate_key`
file:
path: '{{ item }}'
state: directory
recurse: yes
when: ssl_certificate is defined and ssl_certificate_key is defined
with_items:
- "{{ ssl_certificate|dirname }}"
- "{{ ssl_certificate_key|dirname }}"

- name: Symlink certificates to `ssl_certificate` and `ssl_certificate_key`
file:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
state: link
when: ssl_certificate is defined and ssl_certificate_key is defined
with_items:
- src: /etc/letsencrypt/live/{{ letsencrypt_cert_domains[0] }}/fullchain.pem
dest: "{{ssl_certificate}}"
- src: /etc/letsencrypt/live/{{ letsencrypt_cert_domains[0] }}/privkey.pem
dest: "{{ssl_certificate_key}}"
- name: generate certificate (single)
include: cert.yml
vars:
letsencrypt_cert:
domains: "{{ letsencrypt_cert_domains }}"
webroot: "{{ letsencrypt_webroot_path }}"
ssl_certificate: "{{ ssl_certificate | default('') }}"
ssl_certificate_key: "{{ ssl_certificate_key | default('') }}"
when: letsencrypt_cert_domains and letsencrypt_webroot_path
tags: configure

- name: generate certificate (multiple)
include: cert.yml
with_items: "{{ letsencrypt_certs }}"
loop_control:
loop_var: letsencrypt_cert
when: letsencrypt_certs
tags: configure
5 changes: 0 additions & 5 deletions tasks/notdebian.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
package: name={{ item }} state=present
become: yes
with_items: "{{ letsencrypt_depends | default([]) }}"
tags: install

- name: Install virtualenv
package: name={{ item }} state=present
become: yes
with_items: "{{ virtualenv_package_name | default([]) }}"
tags: install

- name: Install python depends
pip:
Expand All @@ -26,13 +24,11 @@
with_items:
- setuptools
- pip
tags: install

- name: Install pycparser
# https://community.letsencrypt.org/t/certbot-auto-fails-while-setting-up-virtual-environment-complains-about-package-hashes/20529/22
pip: virtualenv="{{ letsencrypt_venv }}" virtualenv_site_packages=no name=pycparser version=2.13 state=present virtualenv_python=python2
become: yes
tags: install
when: ansible_os_family == "RedHat"

- name: More python depends
Expand All @@ -42,4 +38,3 @@
name: letsencrypt
state: latest
become: yes
tags: install
6 changes: 4 additions & 2 deletions tests/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

vars:
letsencrypt_email: [email protected]
letsencrypt_cert_domains:
- www.example.com
letsencrypt_certs:
webroot: /var/www
domains:
- www.example.com
letsencrypt_server: https://acme-staging.api.letsencrypt.org/directory

roles:
Expand Down