2 Commits

Author SHA1 Message Date
2575026dc7 docs: agregar guía completa de testing para Ansible
Agrega TESTING.md con documentación exhaustiva sobre:
- Molecule (framework oficial de testing)
- Ansible Lint (análisis estático)
- Testinfra (testing de infraestructura con Python)
- YAML Lint (validación de sintaxis)
- Estrategias de CI/CD con GitHub Actions y GitLab CI
- Comandos útiles y mejores prácticas
- Estrategia de testing por fases para Abyaya.la

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 13:18:09 -03:00
edb3a22ec0 feat: renovación automática de certificados SSL con dns-standalone
- Activada renovación automática cada 4 y 18 de mes
- Usa dns-standalone sin conflictos con dnsmasq (IPs diferentes)
- Documentación completa de arquitectura DNS y validación ACME
2025-11-25 19:31:16 -03:00
3 changed files with 570 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.
; 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 \
-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
```

401
TESTING.md Normal file
View File

@ -0,0 +1,401 @@
# Testing en Abyaya.la
Esta guía documenta las estrategias y herramientas de testing para infraestructura como código (IaC) basada en Ansible.
## Índice
1. [Testing de Ansible](#testing-de-ansible)
2. [Validación y Sintaxis](#validación-y-sintaxis)
3. [Testing de Infraestructura](#testing-de-infraestructura)
4. [CI/CD para Ansible](#cicd-para-ansible)
5. [Recursos Adicionales](#recursos-adicionales)
---
## Testing de Ansible
### Molecule (Framework oficial - RECOMENDADO)
Molecule es el framework oficial para testing de roles de Ansible. Permite ejecutar tests unitarios, de integración y verificar idempotencia.
**Documentación Oficial:**
- Documentación principal: https://ansible.readthedocs.io/projects/molecule/
- Getting Started: https://ansible.readthedocs.io/projects/molecule/getting-started/
- Guía de escenarios: https://ansible.readthedocs.io/projects/molecule/configuration/
**Características:**
- Tests unitarios de roles individuales
- Tests de integración
- Tests de idempotencia (verificar que ejecutar dos veces da el mismo resultado)
- Soporte para Docker/Podman para simular entornos
**Instalación:**
```bash
pip install molecule molecule-docker
```
**Uso básico:**
```bash
cd roles/rap
molecule init scenario
molecule test
```
---
### Ansible Lint
Herramienta de análisis estático que verifica mejores prácticas y patrones comunes en playbooks y roles de Ansible.
**Documentación:**
- Documentación principal: https://ansible-lint.readthedocs.io/
- Catálogo de reglas: https://ansible-lint.readthedocs.io/rules/
**Instalación:**
```bash
pip install ansible-lint
```
**Uso:**
```bash
# Verificar un playbook específico
ansible-lint deploy.yml
# Verificar todos los archivos del proyecto
ansible-lint
# Verificar un rol específico
ansible-lint roles/proxy/
```
---
### Testinfra
Framework de testing de infraestructura escrito en Python. Permite verificar el estado real de los servidores después del despliegue.
**Documentación:**
- Documentación principal: https://testinfra.readthedocs.io/
- Ejemplos: https://testinfra.readthedocs.io/en/latest/examples.html
**Instalación:**
```bash
pip install testinfra
```
**Ejemplo de test:**
```python
# tests/test_vpn.py
def test_tinc_is_running(host):
"""Verificar que el servicio Tinc está corriendo"""
tinc = host.service("tinc@comun")
assert tinc.is_running
assert tinc.is_enabled
def test_vpn_interface_exists(host):
"""Verificar que la interfaz VPN existe"""
assert host.interface("comun").exists
def test_vpn_ip_assigned(host):
"""Verificar que la IP VPN está asignada"""
comun = host.interface("comun")
assert comun.addresses[0].startswith("10.13.12.")
def test_nginx_is_running(host):
"""Verificar que Nginx está corriendo"""
nginx = host.service("nginx")
assert nginx.is_running
def test_port_443_listening(host):
"""Verificar que el puerto 443 está escuchando"""
assert host.socket("tcp://0.0.0.0:443").is_listening
```
**Ejecución:**
```bash
# Ejecutar tests contra un host
testinfra --hosts=ssh://root@hetzner tests/test_vpn.py
# Ejecutar con Ansible inventory
testinfra --ansible-inventory=hosts.production tests/
```
---
## Validación y Sintaxis
### YAML Lint
Validador de sintaxis YAML con verificación de estilos y buenas prácticas.
**Documentación:**
- Repositorio: https://github.com/adrienverge/yamllint
- Configuración: https://yamllint.readthedocs.io/en/stable/configuration.html
**Instalación:**
```bash
pip install yamllint
```
**Uso:**
```bash
# Validar archivos específicos
yamllint deploy.yml abyayala.yml
# Validar todos los YAML del proyecto
yamllint *.yml roles/*/tasks/*.yml roles/*/templates/*.yml
```
**Configuración (.yamllint):**
```yaml
extends: default
rules:
line-length:
max: 120
indentation:
spaces: 2
```
---
### Ansible Syntax Check (Built-in)
Verificación de sintaxis integrada en Ansible.
**Documentación:**
- Testing strategies: https://docs.ansible.com/ansible/latest/reference_appendices/test_strategies.html
- Syntax check: https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html#cmdoption-ansible-playbook-syntax-check
**Uso:**
```bash
# Verificar sintaxis de un playbook
ansible-playbook deploy.yml --syntax-check
# Dry-run (simular sin ejecutar)
ansible-playbook deploy.yml --check
# Dry-run con diff de cambios
ansible-playbook deploy.yml --check --diff
```
---
## Testing de Infraestructura
### InSpec (Chef)
Framework de testing de infraestructura enterprise. Alternativa más robusta a Testinfra.
**Documentación:**
- Documentación principal: https://docs.chef.io/inspec/
**Características:**
- Lenguaje DSL específico para infraestructura
- Compliance as Code
- Perfiles de seguridad predefinidos
---
## CI/CD para Ansible
### GitHub Actions
**Documentación:**
- GitHub Actions para Ansible: https://github.com/marketplace/actions/run-ansible-playbook
**Ejemplo de workflow:**
```yaml
# .github/workflows/test.yml
name: Test Ansible Playbooks
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install ansible ansible-lint yamllint molecule molecule-docker
- name: Run YAML Lint
run: yamllint .
- name: Run Ansible Lint
run: ansible-lint
- name: Syntax check
run: ansible-playbook deploy.yml --syntax-check
- name: Run Molecule tests
run: |
cd roles/rap
molecule test
```
---
### GitLab CI
**Documentación:**
- GitLab CI para Ansible: https://docs.gitlab.com/ee/ci/examples/ansible/
**Ejemplo de pipeline:**
```yaml
# .gitlab-ci.yml
stages:
- lint
- test
lint:yaml:
stage: lint
image: python:3.9
script:
- pip install yamllint
- yamllint .
lint:ansible:
stage: lint
image: python:3.9
script:
- pip install ansible-lint
- ansible-lint
test:syntax:
stage: test
image: python:3.9
script:
- pip install ansible
- ansible-playbook deploy.yml --syntax-check
test:molecule:
stage: test
image: python:3.9
services:
- docker:dind
script:
- pip install molecule molecule-docker
- cd roles/rap && molecule test
```
---
## Recursos Adicionales
### Guías y Tutoriales
- **Testing Ansible Roles with Molecule**: https://www.digitalocean.com/community/tutorials/how-to-test-ansible-roles-with-molecule-on-ubuntu-20-04
- **Developing and Testing Ansible Roles with Molecule and Podman** (Serie de artículos): https://www.ansible.com/blog/developing-and-testing-ansible-roles-with-molecule-and-podman-part-1
### Best Practices
- **Ansible Best Practices**: https://docs.ansible.com/ansible/latest/tips_tricks/ansible_tips_tricks.html
- **Ansible Style Guide**: https://docs.ansible.com/ansible/latest/dev_guide/style_guide/
### Libros
- **Ansible for DevOps** (Jeff Geerling): https://www.ansiblefordevops.com/
- Capítulos relevantes sobre testing y CI/CD
---
## Estrategia de Testing Recomendada para Abyaya.la
### Fase 1: Inmediato (Quick Wins)
1. **Validación de sintaxis**
```bash
ansible-playbook deploy.yml --syntax-check
```
2. **YAML Lint**
```bash
pip install yamllint
yamllint .
```
3. **Ansible Lint**
```bash
pip install ansible-lint
ansible-lint
```
### Fase 2: Corto Plazo (1-2 semanas)
1. **Molecule para roles críticos**
- Empezar con `roles/rap` (VPN es crítico)
- Continuar con `roles/proxy` (punto de entrada público)
- Agregar `roles/certbot` (seguridad SSL)
2. **Pre-commit hooks**
```bash
pip install pre-commit
# Crear .pre-commit-config.yaml
pre-commit install
```
### Fase 3: Largo Plazo (1-2 meses)
1. **Testinfra para verificación post-despliegue**
- Tests de conectividad VPN
- Tests de disponibilidad de servicios
- Tests de configuración de firewall
- Tests de certificados SSL
2. **CI/CD Pipeline**
- GitHub Actions o GitLab CI
- Tests automáticos en cada push
- Tests de integración en staging
3. **Tests de integración end-to-end**
- Despliegue completo en entorno de prueba
- Verificación de flujo completo: VPN → Proxy → Servicio
---
## Comandos Útiles de Testing
```bash
# Testing rápido de sintaxis
ansible-playbook deploy.yml --syntax-check
# Dry-run (ver qué cambiaría sin aplicar)
ansible-playbook deploy.yml --check --diff -e "host=hetzner alt=abyayala"
# Verificar sintaxis YAML
yamllint *.yml roles/
# Análisis estático con ansible-lint
ansible-lint deploy.yml
# Test de un rol específico con Molecule
cd roles/rap && molecule test
# Verificación post-despliegue con Testinfra
testinfra --hosts=ssh://root@hetzner tests/
# Ver qué tareas se ejecutarían
ansible-playbook deploy.yml --list-tasks -e "host=hetzner alt=abyayala"
# Ejecutar solo con tags específicos en check mode
ansible-playbook deploy.yml --check --tags=vpn -e "host=hetzner alt=abyayala"
```
---
## Contribuir
Al agregar nuevos roles o modificar existentes:
1. Ejecutar ansible-lint antes de commit
2. Verificar sintaxis con --syntax-check
3. Si es un rol crítico, agregar tests de Molecule
4. Documentar casos de prueba en este archivo

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 >> /var/log/renewal.log 2>&1"
- name: proxy update, after certs renewal
cron: