systemli.letsencrypt
Rol de Ansible para obtener certificados SSL de Let's Encrypt
Este rol está diseñado para solicitar certificados SSL de Let's Encrypt, utilizando el desafío HTTP o el desafío DNS para su API ACME.
Características:
- Instala y configura certbot y el script ayudante para el desafío DNS
- Soporta tanto el desafío HTTP como el desafío DNS
- Para el desafío HTTP, se soportan los plugins de autenticación
apache
,nginx
,standalone
ywebroot
- Para el desafío HTTP, se soportan los plugins de autenticación
- El desafío DNS utiliza una zona dedicada solo para los token de desafío AMCE, reduciendo los riesgos de seguridad de las actualizaciones dinámicas. El concepto se explica aquí
- Reinicia los servicios al renovar el certificado usando post-hooks o un comando post-hook personalizado
- Control de permisos a los certificados usando un grupo de sistema dedicado
Distribuciones soportadas:
- Debian 11 (Bullseye)
- Debian 12 (Bookworm)
Probado en:
- Debian 11 (Bullseye)
- Debian 12 (Bookworm)
- CentOS7
- Ubuntu 2204 (Jammy Jellyfish)
Realiza lo siguiente:
Cuando
letsencrypt_setup
es True (el valor predeterminado), este rol:- Instala certbot
- Registra una cuenta en Let's Encrypt
- Instala los archivos/claves necesarios para el desafío DNS
- Crea el grupo de sistema 'letsencrypt'
Cuando se invoca con la variable
letsencrypt_cert
llena:- Solicita un certificado SSL a través de la API ACME de Let's Encrypt, usando el desafío HTTP o el desafío DNS
- Opcionalmente establece el post-hook para renovaciones del certificado (para reiniciar los servicios necesarios después)
- Opcionalmente añade usuarios del sistema al grupo 'letsencrypt' para otorgarles acceso de lectura a los certificados SSL y sus claves privadas
Cómo funciona (ejemplos)
- Instalación de certbot
ansible-playbook site.yml -l localhost -t letsencrypt
- Creación de un certificado mediante desafío HTTP y con autenticador
webroot
(reiniciando el servicio 'apache2' al renovar):ansible-playbook site.yml -l localhost -t letsencrypt -e '{"letsencrypt_cert":{"name":"sub.example.org","domains":["sub.example.org"],"challenge":"http","http_auth":"webroot","webroot_path":"/var/www/sub.example.org","services":["apache2"]}}'
- Creación de un certificado mediante desafío DNS (otorgando acceso de lectura a los certificados al usuario 'Debian-exim', reiniciando los servicios 'exim4' y 'dovecot' al renovar):
ansible-playbook site.yml -l localhost -t letsencrypt -e '{"letsencrypt_cert":{"name":"sub2","domains":["sub2.example.org","sub2.another.example.org"],"challenge":"dns","services":["dovecot","exim4"],"users":["Debian-exim"]}}'
- Creación de un certificado mediante desafío HTTP y con autenticador
standalone
(reutilizando la misma clave privada al renovar y ejecutando un script post-hook personalizado al renovar):ansible-playbook site.yml -l localhost -t letsencrypt -e '{"letsencrypt_cert":{"name":"sub3","domains":["sub3.example.org"],"challenge":"http","http_auth":"standalone","reuse_key":True,"post_hook":"/usr/local/bin/cert-post-hook.sh"}}'
Estructura esperada de la variable letsencrypt_cert
La variable letsencrypt_cert
se espera que sea un diccionario:
letsencrypt_opts_extra: "--register-unsafely-without-email"
letsencrypt_cert:
name: sub.example.org
domains:
- sub.example.org
challenge: http
http_auth: webroot
webroot_path: /var/www/sub.example.org
services:
- apache2
o:
letsencrypt_cert:
name: sub2
domains:
- sub2.example.org
- sub2.another.example.org
challenge: dns
services:
- dovecot
- exim4
users:
- Debian-exim
o:
letsencrypt_cert:
name: sub3
domains:
- sub3.example.org
challenge: http
http_auth: standalone
reuse_key: True
post_hook: "/usr/local/bin/cert-post-hook.sh"
letsencrypt_cert:
name: sub3
domains:
- sub3.example.org
challenge: http
http_auth: standalone
reuse_key: True
deploy_hook: "/usr/local/bin/cert-post-hook.sh"
El diccionario soporta las siguientes claves:
name
: nombre del certificado [opcional]domains
: lista de dominios para el certificado [requerido]challenge
: 'http' o 'dns' [requerido]- para el desafío 'http':
http_auth
: 'webroot', 'apache' o 'nginx' [opcional, por defecto 'webroot']- para
http_auth
'webroot':webroot_path
[opcional, por defecto '/var/www']
- para
- para el desafío 'http':
services
: lista de servicios a reiniciar en el post-hook [opcional]reuse_key
: Reutilizar la misma clave privada al renovar el certificado. 'True' o 'False' (por defecto 'False')post_hook
: Post-hook personalizado a ejecutar después de intentar obtener/renovar un certificado [opcional]deploy_hook
: Deploy-hook personalizado a ejecutar después de un intento exitoso de obtener/renovar un certificado [opcional]renew_hook
: Renew-hook personalizado a ejecutar una vez por cada certificado renovado después de la renovación del certificado [opcional]users
: lista de usuarios a añadir al grupo de sistema 'letsencrypt' [opcional]
Preliminares generales
El rol se encarga de instalar certbot y solicitar certificados SSL usando ya sea el desafío HTTP o el desafío DNS. No instala o configura la infraestructura requerida (es decir, el servidor web Apache o un servidor DNS).
El rol se ha probado solo con Ansible 2.2. No se garantiza que funcione con versiones anteriores de Ansible.
El desafío HTTP
Requisitos:
- El nombre del dominio del certificado solicitado debe apuntar a el sistema
- Para
http_auth
'apache', Apache2 debe estar instalado (y configurado) en el sistema - Para
http_auth
'nginx', NGINX debe estar instalado (y configurado) en el sistema
El desafío DNS
Requisitos:
- Un servidor DNS con una zona dedicada, utilizada solo para el desafío DNS de ACME. Esta zona debe permitir actualizaciones dinámicas de DNS (NSUPDATE) para registros TXT (ver más abajo).
- Los registros CNAME para
_acme-challenge.sub.example.org
para todos los nombres de dominio del certificado solicitado deben apuntar asub.example.org._le.example.org
(dentro de la zona dedicada para el desafío DNS de ACME). - El contenido de la clave de actualización de DNS y las claves privadas de actualización de DNS deben estar disponibles en las variables de Ansible
letsencrypt_ddns_key
yletsencrypt_ddns_privkey
(preferiblemente dentro de un vault).
Este rol instala un script ayudante para el desafío DNS en
/usr/local/bin/certbot-dns-hook.sh
. Este script añadirá el token de validación al registro TXT en sub.example.org._le.example.org
durante el desafío DNS y lo eliminará después.
Soporte para comodines con el desafío DNS
La obtención de certificados comodín debería funcionar sin problemas a través del desafío DNS.
Configurando bind9 para el desafío DNS
(Otra opción sería usar el servidor acme-dns para esto)
Genera una clave para actualizaciones dinámicas:
cd /etc/bind/keys
dnssec-keygen -a HMAC-SHA512 -b 512 -n USER _le.example.org_ddns_update
chown -R bind:bind /etc/bind/keys
Añade la clave a tu configuración de bind (por ejemplo, en /etc/bind/named.conf.options
):
key "_le.example.org_ddns_update" {
algorithm hmac-sha512;
secret "...";
};
Crea la zona para actualizaciones dinámicas:
$ORIGIN .
$TTL 86400 ; 1 día
_le.example.org IN SOA ns1.example.org. postmaster.example.org. (
2017061501 ; serial
86400 ; refresco (1 día)
3600 ; reintento (1 hora)
2419200 ; caducidad (4 semanas)
86400 ; mínimo (1 día)
)
NS ns1.example.org.
NS ns2.example.org.
TXT "v=spf1 -all"
y configúralo en tu configuración de bind (por ejemplo, en /etc/bind/named.conf.local
):
zone "_le.example.org" {
type master;
file "/etc/bind/zones/db._le.example.org";
update-policy { grant _le.example.org_ddns_update wildcard *._le.example.org. TXT; };
};
Formato para /etc/letsencrypt/keys/ddns_update.key
(de bind)
key "<clave>" {
algorithm HMAC-SHA512;
secret "<clave>";
};
Formato para /etc/letsencrypt/keys/ddns_update.private
Private-key-format: v1.3
Algorithm: 165 (HMAC_SHA512)
Key: <clave>
Bits: AAA=
Created: 20181017144534
Publish: 20181017144534
Activate: 20181017144534
Valores predeterminados de las variables de Ansible
# Realizar paso de configuración; establecer falso para desactivar
letsencrypt_setup: True
# Proporcionar datos de cuenta existentes para copiar
letsencrypt_account: ""
# letsencrypt_account:
# hash: 1234567890abcdef1234567890abcdef
# id: 123456789
# creation_host: localhost
# creation_dt: 2020-12-13T13:12:00Z
# private_key:
# n: 1234
# e: 5678
# d: 90ab
# p: cdef
# q: 1234
# dp: 5678
# dq: 90ab
# qi: cdef
# kty: RSA
# Establecer la dirección de correo electrónico asociada con la cuenta de Let's Encrypt
letsencrypt_account_email: ""
# Autenticador predeterminado para el desafío HTTP ('webroot' o 'apache')
letsencrypt_http_auth: webroot
# Ruta predeterminada del webroot para el autenticador 'webroot'
letsencrypt_webroot_path: /var/www
# Instalar el script ayudante del desafío DNS y la clave de actualización DNS
letsencrypt_dns_challenge: yes
# Configuración para las actualizaciones dinámicas de la zona DNS
# letsencrypt_ddns_server: ""
# letsencrypt_ddns_zone: ""
# letsencrypt_ddns_key: ""
# letsencrypt_ddns_privkey: ""
# Crear grupo de sistema 'letsencrypt' para el acceso a los certificados
letsencrypt_group: yes
# Reutilizar clave privada en la renovación del certificado?
letsencrypt_reuse_key: False
# ¿Permitir subconjunto de nombres?
letsencrypt_subset_names: True
# Establecer opciones adicionales globales de línea de comando para certbot
letsencrypt_opts_extra: ""
# Establecer la ruta para el directorio letsencrypt (¡sin "/" al final!)
letsencrypt_directory: /etc/letsencrypt
Pruebas
Para fines de prueba, la variable letsencrypt_test
puede ser establecida. Si se establece en True, el rol utilizará servidores de prueba de Let's Encrypt para la creación de la cuenta y la obtención del certificado.
Para desarrollar y probar el rol utilizamos Molecule y Vagrant/Github Actions. En el entorno local, puedes probar fácilmente el rol con
molecule test
Licencia
Este rol de Ansible está licenciado bajo la GNU GPLv3.
Autor
Copyright 2017-2019 systemli.org (https://www.systemli.org/)
ansible-galaxy install systemli.letsencrypt