Certbot standalone auto-renewal #77

Merged
Numerica merged 4 commits from feat-cert-auto-renewal into issue42 2025-12-16 20:48:59 +00:00
2 changed files with 169 additions and 8 deletions

162
DNS_ARCHITECTURE.md Normal file
View File

@ -0,0 +1,162 @@
# Arquitectura DNS y Validación ACME
Este documento explica cómo funciona el sistema DNS distribuido y la validación de certificados SSL para dominios FQDN.
## Componentes DNS
### 1. Servidores Knot (Autoritativos Remotos)
**Ubicación:** `anarres.sutty.nl`, `athshe.sutty.nl`, `gethen.sutty.nl`, `ganam.sutty.nl`
**Función:** DNS autoritativo real para:
- Zona `abyaya.la`
- Zonas FQDN (ej: `latina.red`, `example.com.ar`)
**Actualización:** Via `knsupdate` ejecutado desde el proxy
### 2. Proxy (Hetzner: 5.161.236.18)
**Servicios DNS en diferentes IPs:**
| Servicio | IP | Puerto | Función |
|----------|-------|--------|---------|
| **dnsmasq** | `10.13.12.1` + `127.0.0.1` | 53 | DNS cache/resolver para VPN interna |
| **certbot dns-standalone** | `5.161.236.18` | 53 | DNS temporal para validación ACME |
**No hay conflicto de puertos** porque usan IPs diferentes en la misma máquina.
## Flujo de Validación ACME para Dominios FQDN
### Ejemplo: Certificado para `kipu.latina.red`
#### Paso 1: knsupdate configura delegación en Knot
Cuando se despliega un servicio con FQDN, `knsupdate` crea estos registros en los servidores Knot:
```dns
; En zona latina.red
kipu.latina.red IN A 5.161.236.18
*.kipu.latina.red IN A 5.161.236.18
_acme-challenge.kipu.latina.red IN CNAME abyaya.la.
_acme-challenge.kipu.latina.red IN NS _acme-challenge.abyaya.la.
Review

esto es lo que decía que no hace falta dar de alta todos los fqdn en knot, porque estos 4 registros se pueden crear en su dns (salvo que no soporte wildcard)

esto es lo que decía que no hace falta dar de alta todos los fqdn en knot, porque estos 4 registros se pueden crear en su dns (salvo que no soporte wildcard)
Review

Es que no soportan wildcard, por ej el de Latina.red es Bind9, ¿no fue por eso levantamos Knsupdate de hecho el año pasado?
Lo que aqui estamos pensando hacer es que haya que delegarlos (registros NS) para utilizarlos, lo cual es bastante estándar en cada Hosting. Una vez delegados, como este que lo está, intentar obtener el standalone a través del mismo proxy del certbot.
Si los certificados los tenemos que tener nosotrxs, ¿como haríamos esta conexión entre el certbot y el NS externo si no?

Es que no soportan wildcard, por ej el de Latina.red es Bind9, ¿no fue por eso levantamos Knsupdate de hecho el año pasado? Lo que aqui estamos pensando hacer es que haya que delegarlos (registros NS) para utilizarlos, lo cual es bastante estándar en cada Hosting. Una vez delegados, como este que lo está, intentar obtener el standalone a través del mismo proxy del certbot. Si los certificados los tenemos que tener nosotrxs, ¿como haríamos esta conexión entre el certbot y el NS externo si no?
Review

mmm pensemos esto, a ver.

lo que estamos haciendo con *.HUERTA.abyaya.la es dar de alta en un dns dinamico porque no podemos tener ..abyaya.la. de hecho estoy pensando que nos podiamos evitar todo esto con un servidor dns que hice que sí lo permite hacer. quedará para otro momento.

si yo tengo huerta.sutty.coop.ar y lo quiero dar de alta en el proxy, en mi dns creo estos registros:

$ORIGIN sutty.coop.ar.
*.huerta CNAME huerta-sutty.abyayaya.la.

si mi servidor dns no soporta wildcards, tendré que dar de alta todos los subdominios que necesite.

si en cambio vamos por la delegación a los nameservers de la red (ahora gestionados por sutty):

$ORIGIN sutty.coop.ar.
huerta NS huerta-sutty.abyaya.la.

y el resto se gestiona por knsupdate. pero sutty tiene que dar de alta la delegación en sus knot, que no es lo que pasó (porque no me pediste).

mmm pensemos esto, a ver. lo que estamos haciendo con *.HUERTA.abyaya.la es dar de alta en un dns dinamico porque no podemos tener *.*.abyaya.la. de hecho estoy pensando que nos podiamos evitar todo esto con un servidor dns que hice que sí lo permite hacer. quedará para otro momento. si yo tengo huerta.sutty.coop.ar y lo quiero dar de alta en el proxy, en mi dns creo estos registros: ```zonefile $ORIGIN sutty.coop.ar. *.huerta CNAME huerta-sutty.abyayaya.la. ``` si mi servidor dns no soporta wildcards, tendré que dar de alta todos los subdominios que necesite. si en cambio vamos por la delegación a los nameservers de la red (ahora gestionados por sutty): ```zonefile $ORIGIN sutty.coop.ar. huerta NS huerta-sutty.abyaya.la. ``` y el resto se gestiona por knsupdate. pero sutty tiene que dar de alta la delegación en sus knot, que no es lo que pasó (porque no me pediste).
Review

y para que certbot funcione, tendría que crear este registro

$ORIGIN sutty.coop.ar.
_acme-challenge NS _acme-challenge.abyaya.la.
y para que certbot funcione, tendría que crear este registro ``` $ORIGIN sutty.coop.ar. _acme-challenge NS _acme-challenge.abyaya.la. ```
Review

Este último registro está, en el Knsupdate de más arriba
La delegación NS no, porque esa si va en el Registrar

Este último registro está, en el _Knsupdate_ de más arriba La delegación NS no, porque esa si va en el Registrar
; En zona abyaya.la
_acme-challenge.abyaya.la IN A 5.161.236.18
_acme-challenge IN NS _acme-challenge.abyaya.la.
```
**Propósito de la delegación:**
- El CNAME redirige la validación a `abyaya.la`
- El NS delega la autoridad a `_acme-challenge.abyaya.la`
- Que apunta al proxy donde certbot corre
#### Paso 2: Certbot obtiene/renueva certificado
```bash
docker run --rm \
-v abyayala_certs_data:/etc/letsencrypt \
--network host \
numericalatina/certbot-wildcard \
certonly --dns-standalone-address=5.161.236.18 --dns-standalone-port=53 \
Numerica marked this conversation as resolved
Review

está usando la ip del proxy real? sino estas hardcodeando el de producción

está usando la ip del proxy real? sino estas hardcodeando el de producción
Review

Sip esto está mal hardcodeado

Sip esto está mal hardcodeado
-d kipu.latina.red -d *.kipu.latina.red
```
**Certbot:**
1. Levanta un servidor DNS temporal en `5.161.236.18:53`
2. Crea el registro TXT con el token de validación
3. Notifica a Let's Encrypt que está listo
#### Paso 3: Let's Encrypt valida
```
Let's Encrypt consulta: _acme-challenge.kipu.latina.red
↓ Consulta a nameservers de latina.red (Knot)
↓ Knot responde con CNAME
Redirigido a: _acme-challenge.abyaya.la
↓ Consulta delegación NS
↓ Knot indica: usar nameserver _acme-challenge.abyaya.la
Consulta directa a: 5.161.236.18:53
↓ certbot dns-standalone responde
↓ Proporciona el token TXT
✓ Validación exitosa
```
## Configuración Requerida en DNS Externo
Para que un dominio FQDN externo (no `.abyaya.la`) pueda delegar la validación ACME al proxy, es necesario agregar estos registros en el DNS del dominio:
```dns
_acme-challenge IN CNAME abyaya.la.
_acme-challenge IN NS _acme-challenge.abyaya.la.
```
**Ejemplo para `kipu.latina.red`:**
```dns
; En el DNS de latina.red
_acme-challenge.kipu IN CNAME abyaya.la.
_acme-challenge.kipu IN NS _acme-challenge.abyaya.la.
```
Esto se hace **una sola vez por dominio** y permite que todos los subdominios deleguen automáticamente.
## Renovación Automática
La renovación de certificados funciona automáticamente porque:
1.**Delegación persistente**: Los registros `_acme-challenge` ya están en Knot
2.**Cron configurado**: Se ejecuta los días 4 y 18 de cada mes
3.**Sin conflictos**: dnsmasq y certbot usan IPs diferentes
4.**Mismo método**: `certbot renew` usa dns-standalone automáticamente
```yaml
# Configurado en roles/certbot/tasks/main.yml
- name: automatic letsencrypt certs renewal
cron:
name: certificate renewal
day: 4,18
hour: 0
minute: 0
job: "docker run --rm -v abyayala_certs_data:/etc/letsencrypt --network host numericalatina/certbot-wildcard renew --dns-standalone-address=5.161.236.18 --dns-standalone-port=53 >> /var/log/renewal.log 2>&1"
```
## Ventajas de esta Arquitectura
1. **Centralizada**: Un solo proxy maneja validación ACME para todos los dominios
2. **Segura**: No requiere credenciales API de proveedores DNS externos
3. **Flexible**: Soporta dominios `.abyaya.la` y FQDN externos
4. **Automatizada**: Renovación sin intervención manual
5. **Escalable**: Agregar nuevos dominios solo requiere actualizar `abyayala.yml`
## Troubleshooting
### Verificar delegación DNS
```bash
# Verificar CNAME
dig _acme-challenge.kipu.latina.red CNAME
# Verificar NS delegation
dig _acme-challenge.kipu.latina.red NS
# Probar resolución completa
dig @5.161.236.18 _acme-challenge.kipu.latina.red TXT
```
### Ver logs de renovación
```bash
tail -f /var/log/renewal.log
```
### Probar renovación manualmente
```bash
docker run --rm \
-v abyayala_certs_data:/etc/letsencrypt \
--network host \
numericalatina/certbot-wildcard \
renew --dns-standalone-address=5.161.236.18 --dns-standalone-port=53 --dry-run
```

View File

@ -30,14 +30,13 @@
env: yes
value: /bin/bash
# TODO
# - name: automatic letsencrypt certs renewal
# cron:
# name: certificate renewal
# day: 4,18
# hour: 0
# minute: 0
# job: "docker run --rm -v {{ althost }}_certs_data:/etc/letsencrypt -v {{ althost }}_certs_www:/var/www/letsencrypt certbot/certbot renew >> /var/log/renewal.log 2>&1"
- name: automatic letsencrypt certs renewal
cron:
name: certificate renewal
day: 4,18
hour: 0
minute: 0
job: "docker run --rm -v {{ althost }}_certs_data:/etc/letsencrypt --network host {{ CERTBOT_image }} renew --dns-standalone-address={{ host_ip }} --dns-standalone-port=53 --log-driver=journald"
Numerica marked this conversation as resolved Outdated
Outdated
Review

ah ok no está hardcodeada

ah ok no está hardcodeada
Outdated
Review

por que los logs van a un archivo separado en vez del journal?

por que los logs van a un archivo separado en vez del journal?

Esto está copiado de Numérica. Hay que adaptarlo a lo que resuelva este issue #45

Esto está copiado de Numérica. Hay que adaptarlo a lo que resuelva este issue #45
Outdated
Review

por ahora sacale el >> /var/log y agregale --log-driver=journald

por ahora sacale el `>> /var/log` y agregale `--log-driver=journald`
- name: proxy update, after certs renewal
cron: