Skip to content

Commit

Permalink
Merge branch 'master' into wp-network-#66
Browse files Browse the repository at this point in the history
  • Loading branch information
graphiclunarkid committed Sep 8, 2016
2 parents 51b8a4d + b8bdbfa commit d9479a8
Show file tree
Hide file tree
Showing 25 changed files with 285 additions and 123 deletions.
77 changes: 41 additions & 36 deletions doc/role-doc/letsencrypt.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,73 @@
## Description

This role configures nginx to use certificates issued by [Let's
Encrypt](https://letsencrypt.org/) instead of self-signed certificates
installed by Caislean's TLS role. The advantage is that Let's Encrypt
certificates are trusted by most browsers so visitors to your website won't see
an untrusted certificate warning.

## Notes

More information about Let's Encrypt can be found here:
https://letsencrypt.org/
Encrypt](https://letsencrypt.org/) instead of certificates signed by your own
authority as installed by Caislean's role `tls`. The advantage is that Let's
Encrypt certificates are trusted by most browsers so visitors to your website
won't see an untrusted certificate warning.

Use of this role implies acceptance of the Let's Encrypt Subscriber Agreement.
This is available here: https://letsencrypt.org/repository/
This is available here: <https://letsencrypt.org/repository/>

The Let's Encrypt role will only work on remote machines running Debian 8
(Jessie) or later. This is because the Let's Encrypt client is only available
in Debian Testing (stretch).

This role adds the "testing" repository to the remote machine. The role also
specifies apt preferences to make sure software is installed from the stable
repositories unless explicitly specified otherwise.
(Jessie). This is because the Let's Encrypt client is not available on Debian 7
(Wheezy) but is present in Jessie's backports repository. The role will
explicitly fail if you try running it on anything else than Jessie.

This role won't work unless every domain listed in `websites` resolves to the
IP address of the remote machine. This is because Let's Encrypt verifies that
you control the domains for which you're requesting certificates by placing
files in each virtual host's webroot and then checking that it can access those
files from the domains in question.
You can exclude some domains from using Let's Encrypt, either because you do not
want TLS at all or because you prefer using the `tls` role for those. See
configuration parameters below.

## Prerequired roles

- `base-packages`
- `base-config`
- `tls`
- `nginx`

# Manual steps

This role will fail unless every domain listed in `websites` for which you
did not disable Let's Encrypt resolves to the IP address of your server. This is
because Let's Encrypt verifies that you control the domains for which you are
requesting certificates by placing files in each virtual host's webroot and then
checking that it can access those files from the domains in question.

Make sure your DNS records are properly configured for each domain prior to
running this role: if you want a Let's Encrypt certificate for
`www.somedomain.tld`, this exact domain must have an `A` and/or `AAAA` record
pointing to your server.

# Configuration parameters (ansible variables)

## Mandatory parameters

### `websites`

A list of domain names for which Caislean should generate certificates. This is
the same list used by the `nginx` role when creating virtual hosts to serve.
the same list used by the `nginx` role when creating virtual hosts to serve. See
that role's documentation for more options of this configuration parameter.

Default:

websites:
- "{{ server_name }}.{{ domain_name }}"
websites:
- name: "{{ server_name }}.{{ domain_name }}"

Add or change lines to create new nginx virtual hosts and generate letsencrypt
certificates for them.
Add or change lines to create new nginx virtual hosts and generate Let's Encrypt
certificates for them. You can disable Let's Encrypt by setting explicitly the
`letsencrypt` parameter to `False` for a given domain.

Example:

websites:
- "{{ domain_name }}"
- "www.example.com"

### `webmaster_email`

The email address of the person responsible for administering the website (e.g.
[email protected])
websites:
- name: "{{server_name}}.{{domain_name}}"
- name: www.otherdomain.com
- name: cleartext.domain.com
letsencrypt: False

## Optional parameters

None.
### `tls_additional_domains`

Domains listed under this parameter will be excluded from Let's Encrypt
certificate requests. This is because we consider that their TLS is already
handled by the `tls` role. See documentation of that role.
4 changes: 3 additions & 1 deletion doc/role-doc/nginx.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,6 @@ These domains must be defined in the `websites` variable, or the role will fail
to execute. The default domain name must not be specified in this variable, as
TLS is enabled for it by default.

See the TLS role documentation for more information on this parameter.
See the `tls` role documentation for more information on this parameter. Do not
specify domains for which you want Let's Encrypt certificates in this paramter.
See the `letsencrypt` role documentation.
19 changes: 14 additions & 5 deletions doc/role-doc/openldap.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,18 @@ The LDAP administrator password.
Default: `[ domain: domain_name ]`

List of domain names managed in the LDAP directory. The role will create one
separate LDAP database for each of the domains. Optionally, use the parameter
`admin_pass` to set an administrator password specific of a given domain
(otherwise the password set in `ldap_admin_pass` will be used).
separate LDAP database for each of the domains.

For any given domain `example.com`, the administrator account to which to
identify is `cn=admin,dc=example,dc=com`.
Optionally, use the parameter `admin_pass` to set an administrator password
specific of a given domain (otherwise the password set in `ldap_admin_pass` will
be used). For any given domain `example.com`, the administrator account to which
to identify is `cn=admin,dc=example,dc=com`.

Optionally, use parameters `users_ou` and `groups_ou` to define custom
organizationalUnit (OU) entries meant to contain your users and groups. If
unset, the `mail` OU will contain users, and no OU is created for groups. For
now, changing the users OU will break all Caislean roles that query LDAP for
user authentication (`virtualmail`, `prosody`, etc.).

Example:

Expand All @@ -127,6 +133,9 @@ Example:
- domain: additionaldomain.com
- domain: some_other_domain.org
admin_pass: specificadminpass
- domain: somethingelse.com
users_ou: MyUsers
groups_ou: MyGroups

### `domain_name`

Expand Down
4 changes: 4 additions & 0 deletions doc/role-doc/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ called `<host name>.ca.crt.pem` (for the certification authority certificate
chain), `<host name>.cert.crt.pem` (your certificate) and `<host name>.key.pem`
(your private key).

Do not use this parameter if you are planning to use Let's Encrypt. The
`letsencrypt` role will ignore domains listed in `tls_additional_domains`. See
`letsencrypt` role documentation for more details on using Let's Encrypt.

Example:

tls_additional_domains:
Expand Down
2 changes: 1 addition & 1 deletion roles/base-hardening/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- restart ssh
tags: base

- sysctl: name={{ item.name }} value={{ item.value }} state=present
- include: sysctl.yml
with_items:
- { name: 'fs.suid.dumpable', value: '0' }
- { name: 'kernel.sysrq', value: '0' }
Expand Down
5 changes: 5 additions & 0 deletions roles/base-hardening/tasks/sysctl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- sysctl: name={{ item.name }} value={{ item.value }} state=present
register: sysctl_result
until: sysctl_result | success
retries: 2
tags: base
3 changes: 0 additions & 3 deletions roles/letsencrypt/files/etc/apt/preferences.d/security.pref

This file was deleted.

3 changes: 0 additions & 3 deletions roles/letsencrypt/files/etc/apt/preferences.d/stable.pref

This file was deleted.

3 changes: 0 additions & 3 deletions roles/letsencrypt/files/etc/apt/preferences.d/testing.pref

This file was deleted.

1 change: 0 additions & 1 deletion roles/letsencrypt/meta/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
dependencies:
- role: base-packages
- role: base-config
- role: tls
- role: nginx

59 changes: 13 additions & 46 deletions roles/letsencrypt/tasks/letsencrypt.yml
Original file line number Diff line number Diff line change
@@ -1,58 +1,25 @@
- name: Set apt preferences
copy:
src: "etc/apt/preferences.d/{{ item }}"
dest: "/etc/apt/preferences.d/{{ item }}"
group: root
owner: root
with_items:
- stable.pref
- security.pref
- testing.pref
tags: letsencrypt

- name: Add Debian testing repository
apt_repository:
repo: "deb http://http.debian.net/debian stretch main"
state: present
update_cache: yes
- set_fact:
current_letsencrypt_domain: "{{item.name}}"
tags: letsencrypt

- name: Install letsencrypt client
apt:
pkg: letsencrypt
state: installed
default_release: testing
tags: letsencrypt

- name: Generate certificates for websites
command: "letsencrypt certonly --webroot --webroot-path /var/www/{{ item.name }} --email {{ webmaster_email }} -d {{ item.name }} --agree-tos --keep"
with_items:
- "{{ websites }}"
tags: letsencrypt

- name: Configure nginx to use new certificates
- name: Configure nginx to use LE certificate
template:
src: letsencrypt.j2
dest: "/etc/nginx/includes/{{ item.name }}/letsencrypt"
dest: "/etc/nginx/includes/{{current_letsencrypt_domain}}/letsencrypt"
owner: root
group: root
mode: 0644
with_items:
- "{{ websites }}"
tags: letsencrypt
notify:
- restart nginx

# Try to renew the certificate daily, but keep the existing certificate unless it is due to be renewed.
# Certificate renewals seem to fall due 10 days before expiry by default.
- name: Schedule certificate renewals using cron
cron:
name: letsencrypt renew certificate
job: "letsencrypt certonly --webroot --webroot-path /var/www/{{ item.name }} --email {{ webmaster_email }} -d {{ item.name }} --agree-tos --keep && service nginx reload"
cron_file: "ansible_letsencrypt_{{ item.name }}_cert_renewal"
state: present
special_time: daily
user: root
with_items:
- "{{ websites }}"
- name: Make sure nginx does not use TLS configuration used from role tls
file: path=/etc/nginx/includes/{{current_letsencrypt_domain}}/tls-{{current_letsencrypt_domain}} state=absent
tags: letsencrypt
notify:
- restart nginx

- name: Generate certificate for current domain
command: certbot certonly --standalone --standalone-supported-challenges http-01 --email {{ admin_email }} -d {{ current_letsencrypt_domain }} --agree-tos --keep --pre-hook "service nginx stop" --post-hook "service nginx start" --non-interactive
tags: letsencrypt

29 changes: 28 additions & 1 deletion roles/letsencrypt/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,29 @@
- fail: msg="Only Debian Jessie is supported by this role."
when: ansible_distribution_release != "jessie"
tags: letsencrypt

- name: Install certbot letsencrypt client
apt: pkg=certbot state=installed default_release={{ansible_distribution_release}}-backports
tags: letsencrypt

- include: letsencrypt.yml
when: ansible_distribution_release == "jessie"
with_items: "{{websites}}"
when: (item.letsencrypt|default(True)) and (item.name not in (tls_additional_domains|default([])))

- name: Remove lets encrypt nginx configuration for domains where it is not wanted
file: path=/etc/nginx/includes/{{item.name}}/letsencrypt state=absent
with_items: "{{websites}}"
when: ((item.letsencrypt|default(True)) == False) or (item.name in (tls_additional_domains|default([])))
tags: letsencrypt
notify:
- restart nginx

- name: Schedule certificates renewal using cron
cron:
name: renew letsencrypt certificates
job: certbot renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"
cron_file: caislean_letsencrypt_cert_renewal
state: present
special_time: daily
user: root
tags: letsencrypt
4 changes: 2 additions & 2 deletions roles/letsencrypt/templates/letsencrypt.j2
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
add_header Strict-Transport-Security max-age=63072000;
ssl_certificate /etc/letsencrypt/live/{{ item }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ item }}/privkey.pem;
ssl_certificate /etc/letsencrypt/live/{{current_letsencrypt_domain}}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{current_letsencrypt_domain}}/privkey.pem;
2 changes: 1 addition & 1 deletion roles/nginx/templates/index.html.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<html>
<head>
<title>{{ item }}</title>
<title>{{ item.name }}</title>
<style>
.container {
font-family: sans-serif;
Expand Down
7 changes: 7 additions & 0 deletions roles/openldap/files/tmp/memberof.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
objectClass: top
olcModuleLoad: memberof
olcModulePath: /usr/lib/ldap

7 changes: 7 additions & 0 deletions roles/openldap/files/tmp/refint.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
objectClass: top
olcModuleLoad: refint
olcModulePath: /usr/lib/ldap

23 changes: 23 additions & 0 deletions roles/openldap/tasks/add-organizationalunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
- name: Check whether desired organizationalUnit LDAP entry exists
command: ldapsearch -x -b ou={{ou_name}},{{base_dn}} -s base
ignore_errors: true
register: ldapsearch_orgunit
tags:
- ldap

- name: Add organizationalUnit LDAP entry (1/2)
template: src=ou.ldif.j2 dest=/tmp/ou.ldif owner=root group=root mode=0644
when: ldapsearch_orgunit | failed
tags:
- ldap

- name: Add organizationalUnit LDAP entry (2/2)
command: ldapadd -D cn=admin,{{base_dn}} -w {{ domain_ldap_admin_pass }} -f /tmp/ou.ldif
when: ldapsearch_orgunit | failed
tags:
- ldap

- name: Remove LDIF temporary file for organizationalUnit entry
file: path=/tmp/ou.ldif state=absent
tags:
- ldap
43 changes: 43 additions & 0 deletions roles/openldap/tasks/ldap-db-overlays.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
- name: Check if database has memberOf overlay active
command: ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b {{ldap_db_config_dn}} '(&(objectClass=olcOverlayConfig)(olcOverlay=memberof))'
register: domain_ldapsearch_memberof
tags:
- ldap

- name: Upload LDIF file to configure memberOf overlay for current database
template: src=db_memberof_overlay.ldif.j2 dest=/tmp/db_memberof_overlay.ldif owner=root group=root mode=0644
when: domain_ldapsearch_memberof.stdout == ""
tags:
- ldap

- name: Configure memberOf overlay for current domain database
command: ldapadd -Y EXTERNAL -H ldapi:/// -f /tmp/db_memberof_overlay.ldif
when: domain_ldapsearch_memberof.stdout == ""
tags:
- ldap

- name: Check if database has refint overlay active
command: ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b {{ldap_db_config_dn}} '(&(objectClass=olcOverlayConfig)(olcOverlay=refint))'
register: domain_ldapsearch_refint
tags:
- ldap

- name: Upload LDIF file to configure refint overlay for current database
template: src=db_refint_overlay.ldif.j2 dest=/tmp/db_refint_overlay.ldif owner=root group=root mode=0644
when: domain_ldapsearch_refint.stdout == ""
tags:
- ldap

- name: Configure refint overlay for current domain database
command: ldapadd -Y EXTERNAL -H ldapi:/// -f /tmp/db_refint_overlay.ldif
when: domain_ldapsearch_refint.stdout == ""
tags:
- ldap

- name: Remove temporary LDIF files for overlays for current database
file: path=/tmp/{{item}} state=absent
with_items:
- db_refint_overlay.ldif
- db_memberof_overlay.ldif
tags:
- ldap
Loading

0 comments on commit d9479a8

Please sign in to comment.