Add support for 2 more SSL certificate retrieval methods
Adds support for managing certificates manually and for having the playbook generate self-signed certificates for you. With this, Let's Encrypt usage is no longer required. Fixes Github issue #50.
This commit is contained in:
@ -387,17 +387,34 @@ matrix_nginx_proxy_reload_cron_time_definition: "20 4 */5 * *"
|
||||
# See: https://github.com/nginxinc/docker-nginx/issues/190
|
||||
matrix_nginx_proxy_ssl_protocols: "TLSv1.1 TLSv1.2"
|
||||
|
||||
# By default, this playbook automatically retrieves and auto-renews
|
||||
# free SSL certificates from Let's Encrypt.
|
||||
#
|
||||
# The following retrieval methods are supported:
|
||||
# - "lets-encrypt" - the playbook obtains free SSL certificates from Let's Encrypt
|
||||
# - "self-signed" - the playbook generates and self-signs certificates
|
||||
# - "manually-managed" - lets you manage certificates by yourself (manually; see below)
|
||||
#
|
||||
# If you decide to manage certificates by yourself (`matrix_ssl_retrieval_method: manually-managed`),
|
||||
# you'd need to drop them into the directory specified by `matrix_ssl_config_dir_path`
|
||||
# obeying the following hierarchy:
|
||||
# - <matrix_ssl_config_dir_path>/live/<domain>/fullchain.pem
|
||||
# - <matrix_ssl_config_dir_path>/live/<domain>/privkey.pem
|
||||
# where <domain> refers to the domains that you need (usually `hostname_matrix` and `hostname_riot`).
|
||||
matrix_ssl_retrieval_method: "lets-encrypt"
|
||||
|
||||
# Controls whether to obtain production or staging certificates from Let's Encrypt.
|
||||
matrix_ssl_lets_encrypt_staging: false
|
||||
matrix_ssl_lets_encrypt_certbot_docker_image: "certbot/certbot:v0.29.1"
|
||||
matrix_ssl_lets_encrypt_certbot_standalone_http_port: 2402
|
||||
matrix_ssl_lets_encrypt_support_email: "{{ host_specific_matrix_ssl_lets_encrypt_support_email }}"
|
||||
|
||||
# Specifies when to attempt to retrieve new SSL certificates from Let's Encrypt.
|
||||
matrix_ssl_lets_encrypt_renew_cron_time_definition: "15 4 */5 * *"
|
||||
|
||||
matrix_ssl_base_path: "{{ matrix_base_data_path }}/ssl"
|
||||
matrix_ssl_config_dir_path: "{{ matrix_ssl_base_path }}/config"
|
||||
matrix_ssl_log_dir_path: "{{ matrix_ssl_base_path }}/log"
|
||||
matrix_ssl_support_email: "{{ host_specific_matrix_ssl_support_email }}"
|
||||
matrix_ssl_certbot_docker_image: "certbot/certbot:v0.29.1"
|
||||
matrix_ssl_certbot_standalone_http_port: 2402
|
||||
matrix_ssl_use_staging: false
|
||||
|
||||
# Specifies when to attempt to retrieve new SSL certificates from Let's Encrypt.
|
||||
matrix_ssl_renew_cron_time_definition: "15 4 */5 * *"
|
||||
|
||||
# Variables to Control which parts of the role run.
|
||||
run_setup: true
|
||||
|
@ -8,7 +8,7 @@
|
||||
tags:
|
||||
- setup-all
|
||||
|
||||
- include: tasks/setup/setup_ssl.yml
|
||||
- include: tasks/setup/ssl/main.yml
|
||||
tags:
|
||||
- setup-all
|
||||
- setup-ssl
|
||||
|
@ -1,54 +0,0 @@
|
||||
---
|
||||
|
||||
- name: Determine domains to obtain certificates for (Matrix)
|
||||
set_fact:
|
||||
domains_to_obtain_certificate_for: "['{{ hostname_matrix }}']"
|
||||
|
||||
- name: Determine domains to obtain certificates for (Riot)
|
||||
set_fact:
|
||||
domains_to_obtain_certificate_for: "{{ domains_to_obtain_certificate_for + [hostname_riot] }}"
|
||||
when: matrix_riot_web_enabled
|
||||
|
||||
- name: Allow access to HTTP/HTTPS in firewalld
|
||||
firewalld:
|
||||
service: "{{ item }}"
|
||||
state: enabled
|
||||
immediate: yes
|
||||
permanent: yes
|
||||
with_items:
|
||||
- http
|
||||
- https
|
||||
when: ansible_os_family == 'RedHat'
|
||||
|
||||
- name: Ensure certbot Docker image is pulled
|
||||
docker_image:
|
||||
name: "{{ matrix_ssl_certbot_docker_image }}"
|
||||
|
||||
- name: Ensure SSL certificate paths exists
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: 0770
|
||||
owner: "{{ matrix_user_username }}"
|
||||
group: "{{ matrix_user_username }}"
|
||||
with_items:
|
||||
- "{{ matrix_ssl_log_dir_path }}"
|
||||
- "{{ matrix_ssl_config_dir_path }}"
|
||||
|
||||
- name: Obtain initial certificates
|
||||
include_tasks: "tasks/setup/setup_ssl_for_domain.yml"
|
||||
with_items: "{{ domains_to_obtain_certificate_for }}"
|
||||
loop_control:
|
||||
loop_var: domain_name
|
||||
|
||||
- name: Ensure SSL renewal script installed
|
||||
template:
|
||||
src: "{{ role_path }}/templates/usr-local-bin/matrix-ssl-certificates-renew.j2"
|
||||
dest: "/usr/local/bin/matrix-ssl-certificates-renew"
|
||||
mode: 0750
|
||||
|
||||
- name: Ensure periodic SSL renewal cronjob configured
|
||||
template:
|
||||
src: "{{ role_path }}/templates/cron.d/matrix-ssl-certificate-renewal.j2"
|
||||
dest: "/etc/cron.d/matrix-ssl-certificate-renewal"
|
||||
mode: 0600
|
38
roles/matrix-server/tasks/setup/ssl/main.yml
Normal file
38
roles/matrix-server/tasks/setup/ssl/main.yml
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
|
||||
- name: Fail if using unsupported SSL certificate retrieval method
|
||||
fail:
|
||||
msg: "The `matrix_ssl_retrieval_method` variable contains an unsupported value"
|
||||
when: "matrix_ssl_retrieval_method not in ['lets-encrypt', 'self-signed', 'manually-managed']"
|
||||
|
||||
|
||||
# Common tasks, required by any method below.
|
||||
|
||||
- name: Determine domains that we require certificates for (Matrix)
|
||||
set_fact:
|
||||
domains_requiring_certificates: "['{{ hostname_matrix }}']"
|
||||
|
||||
- name: Determine domains that we require certificates for (Riot)
|
||||
set_fact:
|
||||
domains_requiring_certificates: "{{ domains_requiring_certificates + [hostname_riot] }}"
|
||||
when: "matrix_riot_web_enabled"
|
||||
|
||||
- name: Ensure SSL certificate paths exists
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: 0770
|
||||
owner: "{{ matrix_user_username }}"
|
||||
group: "{{ matrix_user_username }}"
|
||||
with_items:
|
||||
- "{{ matrix_ssl_log_dir_path }}"
|
||||
- "{{ matrix_ssl_config_dir_path }}"
|
||||
|
||||
|
||||
# Method specific tasks follow
|
||||
|
||||
- include: tasks/setup/ssl/setup_ssl_lets_encrypt.yml
|
||||
|
||||
- include: tasks/setup/ssl/setup_ssl_self_signed.yml
|
||||
|
||||
- include: tasks/setup/ssl/setup_ssl_manually_managed.yml
|
@ -0,0 +1,61 @@
|
||||
---
|
||||
|
||||
#
|
||||
# Tasks related to setting up Let's Encrypt's management of certificates
|
||||
#
|
||||
|
||||
- name: (Deprecation) Fail if using outdated configuration
|
||||
fail:
|
||||
msg: "You're using the `host_specific_matrix_ssl_support_email` variable, which has been superseded by `host_specific_matrix_ssl_lets_encrypt_support_email`. Please change your configuration to use the new name!"
|
||||
when: "matrix_ssl_retrieval_method == 'lets-encrypt' and host_specific_matrix_ssl_support_email is defined"
|
||||
|
||||
- name: Allow access to HTTP/HTTPS in firewalld
|
||||
firewalld:
|
||||
service: "{{ item }}"
|
||||
state: enabled
|
||||
immediate: yes
|
||||
permanent: yes
|
||||
with_items:
|
||||
- http
|
||||
- https
|
||||
when: "matrix_ssl_retrieval_method == 'lets-encrypt' and ansible_os_family == 'RedHat'"
|
||||
|
||||
- name: Ensure certbot Docker image is pulled
|
||||
docker_image:
|
||||
name: "{{ matrix_ssl_lets_encrypt_certbot_docker_image }}"
|
||||
when: "matrix_ssl_retrieval_method == 'lets-encrypt'"
|
||||
|
||||
- name: Obtain certificates
|
||||
include_tasks: "tasks/setup/ssl/setup_ssl_lets_encrypt_obtain_for_domain.yml"
|
||||
with_items: "{{ domains_requiring_certificates }}"
|
||||
loop_control:
|
||||
loop_var: domain_name
|
||||
when: "matrix_ssl_retrieval_method == 'lets-encrypt'"
|
||||
|
||||
- name: Ensure SSL renewal script installed
|
||||
template:
|
||||
src: "{{ role_path }}/templates/usr-local-bin/matrix-ssl-certificates-renew.j2"
|
||||
dest: "/usr/local/bin/matrix-ssl-certificates-renew"
|
||||
mode: 0750
|
||||
when: "matrix_ssl_retrieval_method == 'lets-encrypt'"
|
||||
|
||||
- name: Ensure periodic SSL renewal cronjob configured
|
||||
template:
|
||||
src: "{{ role_path }}/templates/cron.d/matrix-ssl-certificate-renewal.j2"
|
||||
dest: "/etc/cron.d/matrix-ssl-certificate-renewal"
|
||||
mode: 0600
|
||||
when: "matrix_ssl_retrieval_method == 'lets-encrypt'"
|
||||
|
||||
|
||||
#
|
||||
# Tasks related to getting rid of Let's Encrypt's management of certificates
|
||||
#
|
||||
|
||||
- name: Ensure Let's Encrypt SSL certificate management files removed
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- /usr/local/bin/matrix-ssl-certificates-renew
|
||||
- /etc/cron.d/matrix-ssl-certificate-renewal
|
||||
when: "matrix_ssl_retrieval_method != 'lets-encrypt'"
|
@ -22,38 +22,38 @@
|
||||
--net=host
|
||||
-v {{ matrix_ssl_config_dir_path }}:/etc/letsencrypt
|
||||
-v {{ matrix_ssl_log_dir_path }}:/var/log/letsencrypt
|
||||
{{ matrix_ssl_certbot_docker_image }}
|
||||
{{ matrix_ssl_lets_encrypt_certbot_docker_image }}
|
||||
certonly
|
||||
--non-interactive
|
||||
{% if matrix_ssl_use_staging %}--staging{% endif %}
|
||||
{% if matrix_ssl_lets_encrypt_staging %}--staging{% endif %}
|
||||
--standalone
|
||||
--preferred-challenges http
|
||||
--agree-tos
|
||||
--email={{ matrix_ssl_support_email }}
|
||||
--email={{ matrix_ssl_lets_encrypt_support_email }}
|
||||
-d {{ domain_name }}
|
||||
when: "domain_name_needs_cert"
|
||||
register: result_certbot_direct
|
||||
ignore_errors: true
|
||||
|
||||
# If matrix-nginx-proxy is configured from a previous run of this playbook,
|
||||
# and it's running now, it may be able to proxy requests to `matrix_ssl_certbot_standalone_http_port`.
|
||||
# and it's running now, it may be able to proxy requests to `matrix_ssl_lets_encrypt_certbot_standalone_http_port`.
|
||||
- name: Attempt initial SSL certificate retrieval with standalone authenticator (via proxy)
|
||||
shell: >-
|
||||
/usr/bin/docker run
|
||||
--rm
|
||||
--name=matrix-certbot
|
||||
-p 127.0.0.1:{{ matrix_ssl_certbot_standalone_http_port }}:80
|
||||
-p 127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}:80
|
||||
--network={{ matrix_docker_network }}
|
||||
-v {{ matrix_ssl_config_dir_path }}:/etc/letsencrypt
|
||||
-v {{ matrix_ssl_log_dir_path }}:/var/log/letsencrypt
|
||||
{{ matrix_ssl_certbot_docker_image }}
|
||||
{{ matrix_ssl_lets_encrypt_certbot_docker_image }}
|
||||
certonly
|
||||
--non-interactive
|
||||
{% if matrix_ssl_use_staging %}--staging{% endif %}
|
||||
{% if matrix_ssl_lets_encrypt_staging %}--staging{% endif %}
|
||||
--standalone
|
||||
--preferred-challenges http
|
||||
--agree-tos
|
||||
--email={{ matrix_ssl_support_email }}
|
||||
--email={{ matrix_ssl_lets_encrypt_support_email }}
|
||||
-d {{ domain_name }}
|
||||
when: "domain_name_needs_cert and result_certbot_direct.failed"
|
||||
register: result_certbot_proxy
|
||||
@ -65,6 +65,6 @@
|
||||
Failed to obtain a certificate directly (by listening on port 80)
|
||||
and also failed to obtain by relying on the server at port 80 to proxy the request.
|
||||
See above for details.
|
||||
You may wish to set up proxying of /.well-known/acme-challenge to {{ matrix_ssl_certbot_standalone_http_port }} or,
|
||||
You may wish to set up proxying of /.well-known/acme-challenge to {{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }} or,
|
||||
more easily, stop the server on port 80 while this playbook runs.
|
||||
when: "domain_name_needs_cert and result_certbot_direct.failed and result_certbot_proxy.failed"
|
||||
when: "domain_name_needs_cert and result_certbot_direct.failed and result_certbot_proxy.failed"
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
|
||||
- name: Verify certificates
|
||||
include_tasks: "tasks/setup/ssl/setup_ssl_manually_managed_verify_for_domain.yml"
|
||||
with_items: "{{ domains_requiring_certificates }}"
|
||||
loop_control:
|
||||
loop_var: domain_name
|
||||
when: "matrix_ssl_retrieval_method == 'manually-managed'"
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
|
||||
- set_fact:
|
||||
matrix_ssl_certificate_verification_cert_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/fullchain.pem"
|
||||
matrix_ssl_certificate_verification_cert_key_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/privkey.pem"
|
||||
|
||||
- name: Check if SSL certificate file exists
|
||||
stat:
|
||||
path: "{{ matrix_ssl_certificate_verification_cert_path }}"
|
||||
register: matrix_ssl_certificate_verification_cert_path_stat_result
|
||||
|
||||
- fail:
|
||||
msg: "Failed finding a certificate file (for domain `{{ domain_name }}`) at `{{ matrix_ssl_certificate_verification_cert_path }}`"
|
||||
when: "not matrix_ssl_certificate_verification_cert_path_stat_result.stat.exists"
|
||||
|
||||
- name: Check if SSL certificate key file exists
|
||||
stat:
|
||||
path: "{{ matrix_ssl_certificate_verification_cert_key_path }}"
|
||||
register: matrix_ssl_certificate_verification_cert_key_path_stat_result
|
||||
|
||||
- fail:
|
||||
msg: "Failed finding a certificate key file (for domain `{{ domain_name }}`) at `{{ matrix_ssl_certificate_verification_cert_key_path }}`"
|
||||
when: "not matrix_ssl_certificate_verification_cert_key_path_stat_result.stat.exists"
|
@ -0,0 +1,24 @@
|
||||
---
|
||||
|
||||
- name: Ensure OpenSSL installed (RedHat)
|
||||
yum:
|
||||
name:
|
||||
- openssl
|
||||
state: present
|
||||
update_cache: no
|
||||
when: ansible_os_family == 'RedHat'
|
||||
|
||||
- name: Ensure APT usage dependencies are installed (Debian)
|
||||
apt:
|
||||
name:
|
||||
- openssl
|
||||
state: present
|
||||
update_cache: no
|
||||
when: ansible_os_family == 'Debian'
|
||||
|
||||
- name: Obtain certificates
|
||||
include_tasks: "tasks/setup/ssl/setup_ssl_self_signed_obtain_for_domain.yml"
|
||||
with_items: "{{ domains_requiring_certificates }}"
|
||||
loop_control:
|
||||
loop_var: domain_name
|
||||
when: "matrix_ssl_retrieval_method == 'self-signed'"
|
@ -0,0 +1,40 @@
|
||||
---
|
||||
|
||||
- set_fact:
|
||||
matrix_ssl_certificate_csr_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/csr.csr"
|
||||
matrix_ssl_certificate_cert_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/fullchain.pem"
|
||||
matrix_ssl_certificate_cert_key_path: "{{ matrix_ssl_config_dir_path }}/live/{{ domain_name }}/privkey.pem"
|
||||
|
||||
- name: Check if SSL certificate file exists
|
||||
stat:
|
||||
path: "{{ matrix_ssl_certificate_cert_path }}"
|
||||
register: matrix_ssl_certificate_cert_path_stat_result
|
||||
|
||||
# In order to do any sort of generation (below), we need to ensure the directory exists first
|
||||
- name: Ensure SSL certificate directory exists
|
||||
file:
|
||||
path: "{{ matrix_ssl_certificate_csr_path|dirname }}"
|
||||
state: directory
|
||||
mode: 0750
|
||||
owner: "{{ matrix_user_username }}"
|
||||
group: "{{ matrix_user_username }}"
|
||||
when: "not matrix_ssl_certificate_cert_path_stat_result.stat.exists"
|
||||
|
||||
# The proper way to do this is by using a sequence of
|
||||
# `openssl_privatekey`, `openssl_csr` and `openssl_certificate`.
|
||||
#
|
||||
# Unfortunately, `openssl_csr` and `openssl_certificate` require `PyOpenSSL>=0.15` to work,
|
||||
# which is not available on CentOS 7 (at least).
|
||||
#
|
||||
# We'll do it in a more manual way.
|
||||
- name: Generate SSL certificate
|
||||
command: |
|
||||
openssl req -x509 \
|
||||
-sha256 \
|
||||
-newkey rsa:4096 \
|
||||
-nodes \
|
||||
-subj "/CN={{ domain_name }}" \
|
||||
-keyout {{ matrix_ssl_certificate_cert_key_path }} \
|
||||
-out {{ matrix_ssl_certificate_cert_path }} \
|
||||
-days 3650
|
||||
when: "not matrix_ssl_certificate_cert_path_stat_result.stat.exists"
|
@ -1,4 +1,4 @@
|
||||
MAILTO="{{ matrix_ssl_support_email }}"
|
||||
MAILTO="{{ matrix_ssl_lets_encrypt_support_email }}"
|
||||
|
||||
# This periodically reloads the matrix-nginx-proxy service
|
||||
# to ensure it's using the latest SSL certificate
|
||||
|
@ -1,4 +1,4 @@
|
||||
MAILTO="{{ matrix_ssl_support_email }}"
|
||||
MAILTO="{{ matrix_ssl_lets_encrypt_support_email }}"
|
||||
|
||||
# The goal of this cronjob is to ask certbot to check
|
||||
# the current SSL certificates and to see if some need renewal.
|
||||
@ -8,4 +8,4 @@ MAILTO="{{ matrix_ssl_support_email }}"
|
||||
# This is not our concern here. We simply make sure the certificates are up to date.
|
||||
# Restarting of services happens on its own different schedule (other cronjobs).
|
||||
|
||||
{{ matrix_ssl_renew_cron_time_definition }} root /bin/bash /usr/local/bin/matrix-ssl-certificates-renew
|
||||
{{ matrix_ssl_lets_encrypt_renew_cron_time_definition }} root /bin/bash /usr/local/bin/matrix-ssl-certificates-renew
|
||||
|
@ -12,7 +12,7 @@ server {
|
||||
proxy_pass http://$backend;
|
||||
{% else %}
|
||||
{# Generic configuration for use outside of our container setup #}
|
||||
proxy_pass http://localhost:{{ matrix_ssl_certbot_standalone_http_port }};
|
||||
proxy_pass http://localhost:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }};
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ server {
|
||||
proxy_pass http://$backend;
|
||||
{% else %}
|
||||
{# Generic configuration for use outside of our container setup #}
|
||||
proxy_pass http://localhost:{{ matrix_ssl_certbot_standalone_http_port }};
|
||||
proxy_pass http://localhost:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }};
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
|
@ -4,23 +4,23 @@
|
||||
# need to forward requests for `/.well-known/acme-challenge` to the certbot container.
|
||||
#
|
||||
# This can happen inside the container network by proxying to `http://matrix-certbot:80`
|
||||
# or outside (on the host) by proxying to `http://localhost:{{ matrix_ssl_certbot_standalone_http_port }}`.
|
||||
# or outside (on the host) by proxying to `http://localhost:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}`.
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--name=matrix-certbot \
|
||||
--network="{{ matrix_docker_network }}" \
|
||||
-p 127.0.0.1:{{ matrix_ssl_certbot_standalone_http_port }}:80 \
|
||||
-p 127.0.0.1:{{ matrix_ssl_lets_encrypt_certbot_standalone_http_port }}:80 \
|
||||
-v {{ matrix_ssl_config_dir_path }}:/etc/letsencrypt \
|
||||
-v {{ matrix_ssl_log_dir_path }}:/var/log/letsencrypt \
|
||||
{{ matrix_ssl_certbot_docker_image }} \
|
||||
{{ matrix_ssl_lets_encrypt_certbot_docker_image }} \
|
||||
renew \
|
||||
--non-interactive \
|
||||
{% if matrix_ssl_use_staging %}
|
||||
{% if matrix_ssl_lets_encrypt_staging %}
|
||||
--staging \
|
||||
{% endif %}
|
||||
--quiet \
|
||||
--standalone \
|
||||
--preferred-challenges http \
|
||||
--agree-tos \
|
||||
--email={{ matrix_ssl_support_email }}
|
||||
--email={{ matrix_ssl_lets_encrypt_support_email }}
|
||||
|
Reference in New Issue
Block a user