Configuración de ACME#
El módulo ACME en Angie permite la obtención automática de certificados
usando el protocolo ACME. Este protocolo admite
varios métodos de verificación de dominio (también llamados "validación"); este módulo
implementa la validación HTTP, la validación DNS, la validación ALPN y la
validación mediante hooks
a través de un servicio externo personalizado. Pasos generales para habilitar solicitudes de certificados en la configuración: Configurar un cliente ACME en el bloque Especificar los dominios para los que se solicitan certificados: Se emitirá un único
certificado para todos los nombres de dominio listados en las directivas server_name
dentro de todos los bloques Las direcciones IP (IPv4 o IPv6) especificadas en server_name también son
identificadores de certificado válidos, excepto con la validación DNS. Tenga en cuenta que las autoridades de certificación públicas pueden emitir
certificados para direcciones IP solo bajo perfiles de certificado específicos;
por ejemplo, Let's Encrypt actualmente requiere un perfil que permita
identificadores IP (consulte su documentación de perfiles). Para solicitarlo, establezca el
parámetro Configurar el manejo de solicitudes y las devoluciones de llamada de ACME:
Esto es necesario para verificar la propiedad del dominio. La configuración depende
del método de validación elegido: Método Requisitos del Usuario Multidominio Dominios Wildcard Abrir el puerto 80 (o el especificado en acme_http_port)
para conexiones entrantes en el servidor Angie. ✔ Abrir el puerto 53 (o el especificado en acme_dns_port)
para conexiones entrantes en el servidor Angie. Configurar un registro NS para el subdominio ✔ ✔ Abrir el puerto 443 (o el puerto TLS usado por su servidor)
para conexiones entrantes en el servidor Angie. ✔ Crear un servicio externo (script o aplicación)
que pueda, a solicitud de Angie, actualizar registros DNS
o servir una respuesta especial mediante el servidor web. ✔ ✔ Configurar SSL usando el certificado y la clave obtenidos:
El módulo pone a disposición certificados y claves como variables integradas
que pueden usarse en la configuración para rellenar
ssl_certificate y ssl_certificate_key. Para instrucciones de configuración SSL, consulte Configuración SSL. Truco El proceso de obtención y renovación de certificados depende de muchos servicios
y puede llevar tiempo. Sea paciente y, si encuentra problemas o tiene dudas,
revise el log de depuración. Las claves y certificados de cliente se almacenan en codificación PEM dentro de subdirectorios del
directorio especificado por la Nota Estos archivos persisten en disco entre reinicios. Al arrancar, el cliente
reutiliza un certificado almacenado que aún es válido en lugar de solicitar
uno nuevo, evitando tanto el tiempo de espera de emisión como solicitudes
innecesarias a la CA, que pueden estar sujetas a límites de tasa. En un contenedor, coloque el
directorio de almacenamiento (por defecto Nota Recargar la configuración ( El cliente ACME requiere una cuenta en el servidor CA. Para crear y gestionar esta
cuenta, el cliente utiliza una clave privada ( Nota Si ya dispone de una clave de cuenta, colóquela en el subdirectorio del cliente
antes de iniciar, para reutilizar la cuenta. Alternativamente, especifique el archivo
de clave con el parámetro Algunas CA requieren que la cuenta ACME esté vinculada con una cuenta externa
existente mediante External Account Binding (EAB) antes de
emitir certificados. Para habilitarlo, configure el parámetro El cliente ACME también utiliza una clave separada ( Al arrancar, el cliente solicita un certificado si no existe, firmando y enviando
un CSR para todos los dominios bajo su gestión al servidor CA. El servidor
verifica la propiedad del dominio usando validación HTTP
o validación DNS y emite un certificado, que el cliente
guarda localmente ( Como se mencionó anteriormente, un único certificado cubre todos los nombres de
dominio gestionados por el mismo cliente ACME, lo que puede dar lugar a un
certificado multidominio. La lista de todos los nombres cubiertos por el
certificado puede encontrarse en la sección Subject Alternative Name (SAN) del
certificado obtenido. Para comprobarlo desde la línea de comandos: Cuando un certificado está a punto de expirar o la lista de dominios cambia,
el cliente firma y envía otro CSR al servidor CA. El servidor vuelve a verificar
la propiedad y emite un nuevo certificado, que el cliente instala localmente,
reemplazando el anterior. En la configuración, el certificado obtenido y su clave
correspondiente están disponibles mediante las variables prefijo
$acme_cert_<nombre> y $acme_cert_key_<nombre>. Sus valores son los
contenidos de los respectivos archivos, que deben usarse con las directivas
ssl_certificate y ssl_certificate_key: Nota El cliente ACME resuelve el nombre de host del servidor CA a través del
resolver del mismo contexto, que por defecto consulta los
servidores DNS listados en La directiva acme sirve únicamente para recopilar nombres de dominio
para las solicitudes de certificados.
No controla dónde puede usarse el certificado:
cualquier bloque Por ejemplo, si tiene un bloque Para controlar el conjunto exacto de nombres de dominio en un certificado
sin depender de la recopilación automática desde todos los bloques Otros bloques Cada acme_client gestiona un único certificado. Para obtener varios
certificados independientes (por ejemplo, para dominios no relacionados que no
deben compartir un mismo certificado), configure un acme_client separado
para cada uno en el bloque La validación es automática. Cuando Angie solicita un certificado, el servidor
ACME solicita un archivo token vía HTTP a Angie en
En este ejemplo, el cliente ACME llamado Como se mencionó anteriormente, el puerto 80 debe estar abierto para manejar las
llamadas HTTP de ACME. Si no hay ningún servidor configurado para escuchar en el
puerto de desafío HTTP, el módulo crea un listener dedicado en el puerto 80
(o el configurado en acme_http_port). No se requiere un bloque server separado. La validación es automática, pero requiere registros DNS de su parte. Cuando
Angie solicita un certificado, el servidor ACME envía una consulta DNS para un registro
TXT bajo el subdominio Para que la consulta llegue a Angie, su DNS debe delegar el subdominio
La validación DNS demuestra el control de un nombre de dominio, por lo que no
puede validar identificadores de dirección IP: Angie ignora una dirección IP en
server_name para un cliente configurado con Nota El servidor Angie debe ser accesible desde Internet
en el puerto UDP 53 (o el especificado en acme_dns_port).
Si el servidor está detrás de un firewall,
asegúrese de que este puerto esté abierto para conexiones entrantes. Por ejemplo, para verificar el dominio Esta configuración delega la resolución DNS de
Advertencia La propagación de registros NS puede tardar desde unos minutos hasta 48 horas
dependiendo del TTL y del proveedor de DNS. Se recomienda verificar que la
configuración sea correcta antes de solicitar un certificado. Para verificar que el DNS está configurado correctamente, puede usar los siguientes comandos: Este método permite solicitar certificados comodín, por ejemplo, un
certificado que incluya la entrada Advertencia La aplicabilidad de este escenario depende en gran medida de las capacidades
que ofrezca su proveedor de DNS; algunos proveedores no permiten este tipo de
configuraciones. En general, la configuración es similar al ejemplo de la sección anterior.
No se necesitan ajustes específicos de HTTP; basta con establecer
En este ejemplo, el cliente ACME llamado La validación se gestiona automáticamente. El servidor ACME se conecta usando TLS y
solicita el protocolo Para habilitar este método, configure La configuración es similar a las secciones anteriores; basta con establecer
En este ejemplo, el cliente ACME llamado A diferencia de los métodos anteriores, esta validación requiere un esfuerzo adicional.
El servidor ACME realiza la validación HTTP o la
validación DNS estándar, pero en lugar de interactuar
directamente con el servidor Angie, se comunica con un servicio externo
gestionado por el servidor Angie mediante llamadas hook (acme_hook).
Este servicio configura un servidor DNS o HTTP separado donde el servidor ACME
envía sus solicitudes. Una vez que el servidor ACME recibe la respuesta esperada desde el servidor DNS
o HTTP configurado, confirma la propiedad del dominio. Cuando la emisión o renovación de un certificado requiere la verificación de dominio,
Angie genera una solicitud interna
al El patrón general es el siguiente: Cree un Configure un manejador de solicitudes en el mismo Pase las variables ACME
al manejador utilizando el mecanismo que admita,
por ejemplo El manejador debe devolver un código de estado Independientemente del manejador utilizado,
el Para la validación DNS, el manejador debe utilizar En este ejemplo, el cliente ACME El bloque Un bloque El siguiente script en Perl demuestra un servicio FastCGI externo correspondiente: Aquí, Otro ejemplo, usando PHP-FPM: Los parámetros pasados pueden accederse en PHP a través de Aquí el hook es un script ejecutado por el módulo CGI; añade
y elimina el registro TXT El módulo se carga en el contexto octoDNS construye una zona a partir de sus fuentes en orden y las combina, por lo
que el hook mantiene el registro del desafío en un directorio de fuente propio,
El archivo de zona contiene los registros que usted mismo mantiene: Para comprobar manualmente la configuración de octoDNS, ejecute octodns-sync
sin El script lee las variables ACME de su entorno
y escribe el registro del desafío en la fuente En cuanto el hook Si el valor no aparece a tiempo, el script responde El límite de espera proviene de la variable de entorno Establecerlo en La variable $acme_hook_domain llega sin el prefijo El script se ejecuta como el usuario de los procesos de trabajo de Angie,
El error estándar del script va al registro de errores de Angie con el
nivel Para poner todo esto en marcha en un sistema basado en Debian: Instale el módulo CGI: Instale octoDNS y el proveedor de Cloudflare
en un entorno virtual propio: Cree los directorios; el paquete del módulo
no crea Cree Guarde el script mostrado arriba como Añada la configuración mostrada arriba, pruébela y
recargue Angie: El cliente solicita el certificado inmediatamente después de la recarga; su
estado y el estado del certificado aparecen en la sección de la API
/status/http/acme_clients/, y el certificado emitido se guarda en el
directorio de almacenamiento del cliente. El módulo Stream ACME permite la emisión y uso automatizado de
certificados para tráfico TCP.
Para que funcione correctamente, primero debe configurar su contraparte HTTP:
el cliente ACME debe declararse en el contexto Por defecto, se utiliza el modo de validación HTTP para obtener certificados.
Como se mencionó en la sección Validación HTTP,
esto requiere un servidor HTTP escuchando en el puerto 80: También puede usar la validación DNS
configurando Si anteriormente usaba certbot
para obtener y renovar certificados SSL de Let's Encrypt
antes de migrar de nginx a Angie,
siga estos pasos para pasar a usar nuestro módulo ACME. Supongamos que configuró certificados de la siguiente manera: La configuración creada automáticamente por este comando
suele encontrarse en En el ejemplo anterior, las líneas resaltadas deben modificarse.
Dependiendo de sus circunstancias y preferencias, configure
la validación HTTP
o la validación DNS usando el módulo ACME. La configuración de Angie resultante
podría verse así: Recuerde recargar la configuración
después de hacer cambios: Una vez haya verificado que esta configuración funciona,
puede eliminar los certificados de certbot
y deshabilitar o eliminar completamente certbot del servidor
si ya no se usa en otro lugar,
por ejemplo:Pasos de configuración#
http mediante la directiva
acme_client, que especifica un nombre único de cliente y otros
parámetros. Se pueden configurar varios clientes ACME.server que usen directivas acme que
apunten al mismo cliente ACME.profile= de acme_client._acme-challenge.
apuntando a su servidor Angie.Detalles de implementación#
--http-acme-client-path opción de compilación:$ ls /var/lib/angie/acme/example/
account.key certificate.pem private.key
/var/lib/angie/acme/) en un
volumen persistente para que los certificados emitidos sobrevivan a la
recreación del contenedor; consulte
ejecutar Angie en un contenedor.angie -s reload) vuelve a evaluar cada
cliente ACME frente a los certificados almacenados en disco. Un cliente cuyo
certificado no es válido en ese momento -- incluido uno cuya solicitud
anterior terminó en error -- se vuelve a solicitar de inmediato,
independientemente de retry_after_error. El retraso de
reintento (y retry_after_error=off) solo se aplica mientras Angie
sigue en ejecución; no persiste tras una recarga. Por lo tanto, recargar
repetidamente mientras una solicitud sigue fallando puede enviar solicitudes
frecuentes a la CA, sujetas a los límites de tasa mencionados anteriormente.account.key). Si no existe clave,
se genera al inicio. Luego, el cliente usa esta clave para registrar la cuenta
con el servidor.account_key en acme_client.eab de
acme_client con el identificador de clave y la clave MAC que proporciona
la CA; consulte la descripción de la directiva para conocer la sintaxis exacta.private.key) para
las solicitudes de firma de certificados (CSR). Esta clave de certificado se crea automáticamente
al inicio si es necesario.certificate.pem).$ openssl x509 -in certificate.pem -noout -text | grep -A5 "Subject Alternative Name"
server {
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
/etc/resolv.conf; configure la
directiva explícitamente solo para usar otros servidores o parámetros.
En un host sin conectividad IPv6, el resolver puede seguir emitiendo
consultas AAAA (IPv6) para la CA y no lograr conectarse; desactívelas
con el parámetro ipv6=off, por ejemplo
resolver conf ipv6=off;.Recopilación de dominios frente al uso del certificado#
server puede hacer referencia al certificado obtenido
mediante la variable $acme_cert_<name>,
independientemente de si el bloque contiene una directiva acme.server con comodín
que ya cubre todos los subdominios,
los bloques server adicionales para subdominios específicos
no necesitan la directiva acme:http {
acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=dns;
# This block lists the domains for the certificate request
server {
listen 443 ssl;
server_name example.com *.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
# This block uses the same certificate but does not
# add its server_name to the certificate request
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
Lista explícita de dominios#
server,
cree un bloque server dedicado
que contenga únicamente las directivas server_name y acme.
Para evitar que este bloque maneje tráfico real,
vincúlelo a un socket de dominio Unix:# Dedicated block that defines the certificate's domain list
server {
listen unix:/tmp/acme_example.sock;
server_name example.com www.example.com;
acme example;
}
server pueden entonces usar el certificado
mediante la variable $acme_cert_<name>
sin afectar a qué dominios se solicitan.Certificados independientes para diferentes dominios#
http, y apunte la directiva acme de
cada bloque server al cliente correspondiente por nombre:http {
# Dos clientes independientes, cada uno gestionando su propio certificado
acme_client shop https://acme-v02.api.letsencrypt.org/directory
challenge=http;
acme_client blog https://acme-v02.api.letsencrypt.org/directory
challenge=http;
server {
listen 443 ssl;
server_name shop.example.com www.shop.example.com;
acme shop;
ssl_certificate $acme_cert_shop;
ssl_certificate_key $acme_cert_key_shop;
}
server {
listen 443 ssl;
server_name blog.example.com;
acme blog;
ssl_certificate $acme_cert_blog;
ssl_certificate_key $acme_cert_key_blog;
}
}
Validación HTTP#
/.well-known/acme-challenge/<TOKEN>. Angie responde a estas solicitudes
por sí mismo, por lo que no es necesario servir esa ruta usted mismo. El
servidor ACME compara entonces el token recibido con el que emitió y, si
coinciden, acepta el dominio como suyo.Ejemplo de configuración#
example gestiona certificados para
example.com y www.example.com (tenga en cuenta que los certificados
wildcard no están soportados con validación HTTP):http {
acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80; # No es necesario: si nada más escucha en el puerto de
# desafío HTTP, el módulo lo abre por sí mismo
# (ver la directiva 'acme_http_port')
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
Validación DNS#
_acme-challenge. del dominio que se está
validando. Angie responde a estas consultas por sí mismo: actúa como servidor
de nombres autoritativo para ese subdominio y devuelve el registro TXT que
espera el servidor ACME. El servidor ACME compara el registro recibido con el
valor que emitió y, si coinciden, acepta el dominio como suyo._acme-challenge. al servidor Angie, como se muestra a continuación.
Angie responde con un TTL muy corto de forma predeterminada; algunos
proveedores de DNS filtran las respuestas con un TTL tan bajo, así que
auméntelo con acme_dns_ttl si la validación nunca se completa.challenge=dns, y
registra una advertencia al iniciar. Un bloque server que incluya una
dirección IP debe seguir indicando al menos un dominio que el cliente pueda
validar.example.com usando un servidor
Angie en la dirección IP 203.0.113.10, la configuración DNS de su
dominio debería incluir los siguientes registros:_acme-challenge.example.com. 60 IN NS ns.example.com.
ns.example.com. 60 IN A 203.0.113.10
_acme-challenge.example.com a ns.example.com, asegurando
que ns.example.com sea accesible al asignarlo a la dirección IP
(203.0.113.10).$ dig NS _acme-challenge.example.com +short # Verificar registro NS para el subdominio _acme-challenge
ns.example.com.
$ dig A ns.example.com +short # Verificar registro A para el servidor de nombres
203.0.113.10
$ nc -zv 203.0.113.10 53 # Verificar accesibilidad del servidor DNS en el puerto 53
*.example.com en la sección
Subject Alternative Name (SAN). Para solicitar explícitamente un certificado
para un subdominio, como www.example.com, debe verificar ese subdominio
por separado utilizando el método descrito anteriormente.Ejemplo de configuración#
challenge=dns en la directiva acme_client.example gestiona certificados
para example.com y *.example.com:http {
acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=dns;
server {
server_name example.com *.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
Validación ALPN#
acme-tls/1 a través de ALPN. El módulo sirve un
certificado temporal para la solicitud de validación.challenge=alpn en la
directiva acme_client y asegúrese de que su listener TLS sea accesible en
el puerto 443 (o el puerto usado para TLS).Ejemplo de configuración#
challenge=alpn en la directiva acme_client y asegurarse de que
el servidor TLS sea accesible en el puerto 443.example gestiona un certificado
para example.com y www.example.com:http {
acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=alpn;
server {
listen 443 ssl;
server_name example.com www.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
Validación basada en hooks#
location con nombre que contiene la directiva acme_hook.
La forma en que se gestiona esta solicitud depende enteramente de las demás directivas
configuradas en el mismo location.location con nombre con la directiva acme_hook.location
utilizando el módulo que mejor se adapte a su configuración:
fastcgi_pass para FastCGI,
proxy_pass para HTTP,
cgi_pass para scripts CGI, etc.fastcgi_param para FastCGI
o cgi_set_var para CGI.2xx,
que puede enviarse mediante la cabecera Status.
Cualquier otro código devuelto por el hook add interrumpe el intento
de renovación; en el caso del hook remove, el mismo código solo se
registra como advertencia y la renovación continúa.
La salida del manejador se ignora.Configuración mínima#
location del hook sigue esta estructura:location @acme_hook_location {
acme_hook example;
# Handler directive (fastcgi_pass, proxy_pass, cgi_pass, ...)
# Pass ACME variables using the handler's mechanism:
# ACME_HOOK — $acme_hook_name ("add" or "remove")
# ACME_CHALLENGE — $acme_hook_challenge ("dns" or "http")
# ACME_DOMAIN — $acme_hook_domain
# ACME_TOKEN — $acme_hook_token
# ACME_KEYAUTH — $acme_hook_keyauth
}
ACME_HOOK
para determinar la acción:
cuando es add,
crear un registro TXT para _acme-challenge.ACME_DOMAIN
con el valor de ACME_KEYAUTH;
cuando es remove, eliminar dicho registro.Ejemplo con FastCGI#
example está configurado para la
verificación de dominio usando callbacks DNS, indicado por el parámetro
challenge=dns en la directiva acme_client.server se aplica a todos los subdominios de example.com
(p. ej., *.example.com) y usa el cliente ACME example para
gestionar certificados, tal como especifica la directiva acme.location con nombre maneja las llamadas del hook.
La directiva acme_hook lo asocia
con el cliente ACME example.
Las solicitudes del hook se envían a un servidor FastCGI local
en el puerto 9000 mediante fastcgi_pass.
Las directivas fastcgi_param pasan
las variables ACME al servicio externo.acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=dns;
server {
listen 80;
server_name *.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
location @acme_hook_location {
acme_hook example;
fastcgi_pass localhost:9000;
fastcgi_param ACME_CLIENT $acme_hook_client;
fastcgi_param ACME_HOOK $acme_hook_name;
fastcgi_param ACME_CHALLENGE $acme_hook_challenge;
fastcgi_param ACME_DOMAIN $acme_hook_domain;
fastcgi_param ACME_TOKEN $acme_hook_token;
fastcgi_param ACME_KEYAUTH $acme_hook_keyauth;
include fastcgi.conf;
}
}
#!/usr/bin/perl
use strict; use warnings;
use FCGI;
my $socket = FCGI::OpenSocket(":9000", 5);
my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket);
while ($request->Accept() >= 0) {
print "\r\n";
my $client = $ENV{ACME_CLIENT};
my $hook = $ENV{ACME_HOOK};
my $challenge = $ENV{ACME_CHALLENGE};
my $domain = $ENV{ACME_DOMAIN};
my $token = $ENV{ACME_TOKEN};
my $keyauth = $ENV{ACME_KEYAUTH};
if ($hook eq 'add') {
DNS_set_TXT_record("_acme-challenge.$domain.", $keyauth);
} elsif ($hook eq 'remove') {
DNS_clear_TXT_record("_acme-challenge.$domain.");
}
};
FCGI::CloseSocket($socket);
DNS_set_TXT_record() y DNS_clear_TXT_record() son
funciones que se supone añaden y eliminan registros TXT en la configuración de
un servidor DNS externo que consulta el servidor ACME. Estos registros deben
contener los datos proporcionados por el servidor Angie para permitir que el
servidor DNS externo pase la validación con éxito, similar al proceso descrito
en Validación DNS. Los detalles de implementación de tales funciones
están fuera del alcance de esta guía; por ejemplo, los parámetros también
pueden pasarse a través de la URI de la solicitud:# ...
location @acme_hook_location {
acme_hook example uri=/acme_hook/$acme_hook_name?domain=$acme_hook_domain&key=$acme_hook_keyauth;
fastcgi_pass localhost:9000;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param ACME_CLIENT $acme_hook_client;
fastcgi_param ACME_CHALLENGE $acme_hook_challenge;
fastcgi_param ACME_TOKEN $acme_hook_token;
include fastcgi.conf;
}
Ejemplo con PHP-FPM#
location @acme_hook_location {
acme_hook example;
root /var/www/dns;
fastcgi_pass unix:/run/php-fpm/php-dns.sock;
fastcgi_index hook.php;
fastcgi_param SCRIPT_FILENAME /var/www/dns/hook.php;
include fastcgi_params;
fastcgi_param ACME_CLIENT $acme_hook_client;
fastcgi_param ACME_HOOK $acme_hook_name;
fastcgi_param ACME_CHALLENGE $acme_hook_challenge;
fastcgi_param ACME_DOMAIN $acme_hook_domain;
fastcgi_param ACME_TOKEN $acme_hook_token;
fastcgi_param ACME_KEYAUTH $acme_hook_keyauth;
}
[dns]
listen = /run/php-fpm/php-dns.sock
listen.mode = 0666
user = angie
group = angie
chdir = /var/www/dns
# ...
$_SERVER['...'].Ejemplo con CGI y octoDNS#
_acme-challenge mediante octoDNS, que mantiene el DNS como código: una zona se
describe en YAML y se sincroniza con un proveedor DNS. Aquí se muestra Cloudflare;
cualquier otro proveedor de octoDNS funciona de la misma manera una vez declarado bajo
providers y nombrado en targets. El token de la API necesita los
permisos Zone:Read, DNS:Read y DNS:Edit para la
zona.main. El cliente ACME
example solicita un único certificado para example.com y
*.example.com, verificado mediante callbacks DNS según indica el
parámetro challenge=dns en la directiva acme_client. Un bloque
location con nombre maneja las llamadas del hook: la directiva
acme_hook lo asocia con el cliente, las directivas cgi_set_var
pasan las variables ACME con los nombres
estándar, y cgi_pass ejecuta el script para cada llamada. De esos
nombres, este script usa ACME_CHALLENGE, ACME_HOOK,
ACME_DOMAIN y ACME_KEYAUTH.# in the main context
load_module modules/ngx_http_cgi_module.so;
acme_client example https://acme-v02.api.letsencrypt.org/directory
challenge=dns;
server {
listen 443 ssl;
server_name example.com *.example.com;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
location @acme_hook_location {
acme_hook example;
cgi_set_var ACME_CLIENT $acme_hook_client;
cgi_set_var ACME_HOOK $acme_hook_name;
cgi_set_var ACME_CHALLENGE $acme_hook_challenge;
cgi_set_var ACME_DOMAIN $acme_hook_domain;
cgi_set_var ACME_TOKEN $acme_hook_token;
cgi_set_var ACME_KEYAUTH $acme_hook_keyauth;
cgi_pass /usr/share/angie/cgi-bin/acme-octodns;
}
}
/etc/octodns/acme, y las renovaciones del certificado nunca reescriben el
archivo de zona en /etc/octodns/zones. La opción ignore_missing_zones
(octoDNS 1.16 y posteriores) permite que /etc/octodns/acme permanezca vacío
entre renovaciones. El propio archivo de zona no debe definir registros
_acme-challenge.providers:
config:
class: octodns.provider.yaml.YamlProvider
directory: /etc/octodns/zones
acme:
class: octodns.provider.yaml.YamlProvider
directory: /etc/octodns/acme
ignore_missing_zones: true
cloudflare:
class: octodns_cloudflare.CloudflareProvider
token: YOUR_API_TOKEN
zones:
example.com.:
sources:
- config
- acme
targets:
- cloudflare
'':
type: A
value: 203.0.113.10
www:
type: A
value: 203.0.113.10
--doit.acme:El script acme-octodns
#!/opt/octodns/bin/python3
# DNS-01 hook for the Angie ACME client: keeps the _acme-challenge TXT record
# in a separate octodns source directory and publishes it with octodns-sync.
import fcntl
import os
import subprocess
import sys
import time
import dns.message
import dns.query
import dns.resolver
import yaml
CONFIG = "/etc/octodns/octodns.yaml"
ACME_DIR = "/etc/octodns/acme"
SYNC = "/opt/octodns/bin/octodns-sync"
TTL = 120
WAIT = int(os.environ.get("ACME_WAIT", "300"))
def respond(code, message=""):
if message:
print(message, file=sys.stderr)
sys.stdout.write(f"Status: {code}\r\n\r\n")
sys.exit(0)
def find_zone(domain):
with open(CONFIG) as f:
zones = [z.rstrip(".") for z in yaml.safe_load(f)["zones"]]
match = [z for z in zones if domain == z or domain.endswith("." + z)]
if not match:
respond(500, f"no zone for {domain} in {CONFIG}")
return max(match, key=len)
def update(zone, name, value, add):
# One record may hold several values: a certificate for example.com and
# *.example.com is validated with two TXT values on the same name.
path = f"{ACME_DIR}/{zone}.yaml"
with open(f"{ACME_DIR}/.lock", "w") as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
data = {}
if os.path.exists(path):
with open(path) as f:
data = yaml.safe_load(f) or {}
values = set(data.get(name, {}).get("values", []))
(values.add if add else values.discard)(value)
if values:
data[name] = {"type": "TXT", "ttl": TTL, "values": sorted(values)}
else:
data.pop(name, None)
if data:
with open(path, "w") as f:
yaml.safe_dump(data, f)
elif os.path.exists(path):
os.remove(path)
cmd = [SYNC, f"--config-file={CONFIG}", "--doit", zone + "."]
run = subprocess.run(cmd, capture_output=True, text=True)
if run.returncode != 0:
respond(500, "octodns-sync failed:\n" + run.stdout + run.stderr)
def has_txt(server, fqdn, value):
try:
reply = dns.query.udp(dns.message.make_query(fqdn, "TXT"), server, timeout=3)
except Exception:
return False
return any(value == b"".join(getattr(r, "strings", ())).decode()
for rrset in reply.answer for r in rrset)
def wait_visible(zone, fqdn, value):
# The ACME server asks the zone's authoritative servers, so poll them
# directly: a resolver may have cached the record's absence.
deadline = time.monotonic() + WAIT
while time.monotonic() < deadline:
try:
servers = [str(a) for ns in dns.resolver.resolve(zone, "NS")
for a in dns.resolver.resolve(str(ns.target), "A")]
if servers and all(has_txt(s, fqdn, value) for s in servers):
return True
except Exception:
pass
time.sleep(5)
return False
def main():
hook = os.environ.get("ACME_HOOK")
domain = os.environ.get("ACME_DOMAIN", "")
value = os.environ.get("ACME_KEYAUTH", "")
if os.environ.get("ACME_CHALLENGE") != "dns" or hook not in ("add", "remove"):
respond(500, "the hook supports only DNS validation")
zone = find_zone(domain)
rel = domain[: -len(zone) - 1]
name = "_acme-challenge" + (f".{rel}" if rel else "")
update(zone, name, value, hook == "add")
if hook == "add" and WAIT and not wait_visible(zone, f"{name}.{zone}.", value):
update(zone, name, value, False)
respond(500, f"{name}.{zone} is not visible on the name servers after {WAIT}s")
respond(200)
try:
main()
except Exception as e:
respond(500, f"{type(e).__name__}: {e}")
add devuelve un código 2xx, Angie le pide al servidor
ACME que valide el desafío y consulta el resultado durante 60 segundos. Angie nunca
espera a que el registro se propague, así que el script se encarga de ello: consulta
directamente los servidores de nombres autoritativos de la zona, ya que un resolver
puede tener en caché la ausencia del registro, y deja de esperar en cuanto todos ellos
entregan el valor, esperando hasta 300 segundos de forma predeterminada.Status: 500, y Angie
interrumpe el intento y lo reintenta transcurrido el intervalo definido por el
parámetro retry_after_error. Angie no llama al hook
remove después de un add fallido, así que el script elimina su propio
valor antes de responder.ACME_WAIT. El
location del hook anterior no la define; para cambiar el límite, añádala ahí,
pasando el valor mediante una variable como las demás:set $acme_wait 600;
cgi_set_var ACME_WAIT $acme_wait;
0 desactiva la espera, lo que resulta adecuado para un
servidor autoritativo autoalojado donde una escritura de la API es visible de
inmediato.*., por lo que un
certificado para example.com y *.example.com genera dos llamadas
add para el mismo nombre _acme-challenge.example.com. El script
mantiene una lista de valores en un único registro y, en remove, elimina solo
el valor de esa llamada; cuando no queda ningún valor, elimina el registro y el
archivo que lo contenía en /etc/octodns/acme.angie. Necesita acceso de lectura a la configuración de octoDNS, que contiene
el token, y acceso de escritura a /etc/octodns/acme; nada más. El proceso CGI
no hereda el entorno de Angie, por lo que el token reside en el archivo de
configuración y el script usa rutas absolutas.warn, donde se diagnostican los fallos del hook; estos mensajes aparecen
solo cuando el nivel de registro es warn o menos severo.$ sudo apt-get install angie-module-cgi
$ sudo apt-get install python3-venv
$ sudo python3 -m venv /opt/octodns
$ sudo /opt/octodns/bin/pip install 'octodns>=1.16' octodns-cloudflare
/usr/share/angie/cgi-bin:$ sudo install -d /etc/octodns/zones /usr/share/angie/cgi-bin
$ sudo install -d -o angie -g angie -m 750 /etc/octodns/acme
/etc/octodns/octodns.yaml y
/etc/octodns/zones/example.com.yaml como se muestra arriba y, a
continuación, restrinja la configuración al grupo angie, ya que
contiene el token de la API:$ sudo chgrp angie /etc/octodns/octodns.yaml
$ sudo chmod 640 /etc/octodns/octodns.yaml
acme-octodns y, a
continuación, instálelo:$ sudo install -m 755 acme-octodns /usr/share/angie/cgi-bin/
$ sudo angie -t
$ sudo kill -HUP $(cat /run/angie.pid)
ACME en el módulo stream#
http,
y el bloque stream en sí debe colocarse después del bloque http
en la configuración.Ejemplo de configuración#
# Parte HTTP
http {
# Cliente ACME para la parte stream
acme_client example https://acme-v02.api.letsencrypt.org/directory;
# Servidor para validación HTTP
server {
listen 80;
return 444;
}
}
# Parte Stream
stream {
server {
listen 12345 ssl;
proxy_pass backend_upstream;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
server_name example.com www.example.com;
acme example; # referencia al cliente ACME definido en la parte HTTP
}
upstream backend_upstream {
server 127.0.0.1:54321;
}
}
challenge=dns en la directiva acme_client;
en ese caso, no se necesitará el servidor.Migración desde certbot#
$ sudo certbot --nginx -d example.com -d www.example.com
/etc/nginx/sites-available/example.conf
y se parece a lo siguiente:server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
http {
acme_client example https://acme-v02.api.letsencrypt.org/directory;
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
acme example;
ssl_certificate $acme_cert_example;
ssl_certificate_key $acme_cert_key_example;
}
}
$ sudo kill -HUP $(cat /run/angie.pid)
$ sudo rm -rf /etc/letsencrypt
$ sudo systemctl stop certbot.timer
$ sudo systemctl disable certbot.timer
$ # -- o --
$ sudo rm /etc/cron.d/certbot
$ sudo apt remove certbot
$ # -- o --
$ sudo dnf remove certbot