71 Commits

Author SHA1 Message Date
2b9f6567c1 feat: restore gitea proxy functionality with conditional support
Recupera la funcionalidad de proxy para repositorios git (gitea) que se perdió
al integrar el proxy SSH con stream.conf. Los servicios ahora pueden habilitar
opcionalmente el acceso a gitea agregando el atributo gitea_port.

Cambios:
- Agregado soporte condicional de upstream y servidor gitea en stream.conf
- El puerto gitea se agrega dinámicamente a matrix_ports cuando está definido
- Usa el mismo server_name que SSH, diferenciado solo por puerto
- Respeta la configuración root para dominios raíz

Uso: Agregar gitea_port: 2222 a cualquier servicio en abyayala.yml

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 18:19:49 -03:00
0487ffde12 elimino tarea de mas 2025-12-16 17:52:18 -03:00
a6c1bcaeff Merge branch 'master' into feat-cert-auto-renewal 2025-12-16 12:02:49 -03:00
090c397e24 Merge pull request 'Soporte para FQDN' (#76) from issue42 into master
Reviewed-on: #76
2025-12-16 15:00:07 +00:00
89866df0bf Soporte FQDN
closes #76
2025-12-16 00:45:50 -03:00
216c81df39 feat: use knsupdate only for abyayala subdomains, display DNS config for external domains
- knsupdate now only executes when is_abyayala_subdomain is true
- For external domains, display DNS configuration instructions in console
- Created dns_info.j2 template to show required DNS records for manual configuration
- External domains now show: A records, wildcard A records, and ACME challenge NS delegation

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-16 00:31:26 -03:00
a8b3a63807 se agrega expand en caso de certbot y se arregla condicion de Knsupdate (que sera solo para abyayala_subdomains).
en la amtriz testeando en prod
2025-12-16 00:30:09 -03:00
02b135f486 Merge branch 'master' into issue42 2025-12-15 19:55:15 -03:00
7e12b7b20a poner archivos de webroot 2025-12-15 19:49:25 -03:00
0a99c7f2d1 feat: implement webroot method for root domain SSL certificates
Replace dns-standalone with HTTP-01 webroot validation for root domains
(when root: yes flag is set). This change improves reliability by avoiding
DNS conflicts and simplifies the certificate obtention process.

Changes:
- Add acme_challenge.conf to serve .well-known/acme-challenge directory
- Update certbot.yml to use --webroot for root domains instead of dns-standalone
- Use official certbot/certbot:latest image for webroot (lighter, no DNS needed)
- Add certbot_webroot volume shared between nginx and certbot containers
- Configure vhost.conf to include ACME challenge location for root domains
- Add certbot_webroot variable (/var/www/certbot) to proxy vars

Benefits for root domains:
- No port 53 conflicts with Knot DNS
- Faster validation (HTTP vs DNS propagation)
- More reliable and simpler error handling
- Works with nginx already running on port 80

Wildcard domains continue using DNS-01 challenge as HTTP-01 does not
support wildcard certificates.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 19:48:44 -03:00
fc76a7fb91 refactor: rename no_wildcard to root and fix nginx vhost generation
Rename the flag from `no_wildcard` to `root` throughout the codebase
for better semantics, and fix nginx configuration generation for root
domains.

Changes:
1. Renamed `no_wildcard` → `root` flag in:
   - abyayala.yml (abyaya_root service)
   - roles/certbot/tasks/certbot.yml (uses is_root_domain internally)
   - README-root-domain.md (documentation)

2. Fixed nginx vhost generation in roles/proxy/templates/:
   - vhost.conf: Handle root domains without leading dot in server_name
     * root: yes → `server_name abyaya.la www.abyaya.la;` (exact match)
     * root: no  → `server_name .comun.abyaya.la;` (wildcard match)
   - stream.conf: Same logic for SSH proxy streams

Problem fixed:
- Previous: `.abyaya.la` matched all subdomains, conflicting with
  other vhosts (comun.abyaya.la, sutty.abyaya.la, etc.)
- Now: `abyaya.la www.abyaya.la` matches only root domain exactly

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 19:45:54 -03:00
7d5044f1f1 feat: add support for root domain www.abyaya.la
Add capability to route root domain (abyaya.la and www.abyaya.la)
to sutty.comun while maintaining all existing subdomain routes.

Changes:
- Add no_wildcard flag support in certbot certificate generation
- Split certificate obtention into two conditional paths:
  * Standard mode (with wildcard) for subdomains
  * No-wildcard mode for root domains
- Add abyaya_root service in matrix routing to sutty.comun
- Include implementation plan as README-root-domain.md

Technical details:
- Certificates for root domain will only include abyaya.la and
  www.abyaya.la (no *.abyaya.la wildcard)
- Prevents certificate confusion between root and subdomains
- Maintains clean separation of responsibilities
- All existing subdomain certificates remain unchanged

Generated with Claude Code
2025-12-15 19:45:20 -03:00
f1d37f21f7 tageo como instalacion esto 2025-12-15 19:45:20 -03:00
66e3c6fcaf fix: separar set_fact para compatibilidad con Ansible 2.15+
En Ansible 2.15+ las variables en el mismo set_fact se evalúan
simultáneamente, no secuencialmente. Separar needs_cert en su propio
set_fact antes de usarlo en needs_vhost y obtain_cert.
2025-12-15 19:45:20 -03:00
1fc58432dd fix: aplicar | bool en when clause de obtain_cert 2025-12-15 19:45:20 -03:00
d683f8bcd2 fix: envolver expresiones booleanas en templates Jinja2
Aplicar | bool al resultado final de cada expresión y envolver
en sintaxis {{ }} para forzar evaluación correcta como booleanos.
2025-12-15 19:45:20 -03:00
5c19e789c4 fix: forzar conversión a bool en condicionales de certbot
Ansible requiere que las condicionales resulten en booleanos.
Agregado filtro | bool para convertir explícitamente strings a booleanos
en las evaluaciones de needs_cert, needs_vhost y obtain_cert.
2025-12-15 19:45:20 -03:00
838cc5833c fix: mover limpieza de repos Docker fuera del bloque installation
La limpieza de repositorios Docker antiguos debe ejecutarse SIEMPRE,
incluso cuando se usa --skip-tags=installation, para evitar conflictos
APT antes de que knsupdate u otros roles intenten usar apt.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 19:45:20 -03:00
135edfe527 fix: limpiar repositorios Docker antiguos antes de configurar deb822
Soluciona conflicto APT causado por configuraciones de repositorio Docker
duplicadas con valores Signed-By contradictorios. Ahora se eliminan los
archivos de repositorio antiguos antes de agregar la configuración deb822.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 19:45:20 -03:00
f72983049d deabajo nodo 2025-12-15 19:45:20 -03:00
be5f8fc5e7 dns_extras en archivos con nombres de dominio principal, en
roles/knsupdate/files/dns_extras
2025-12-15 19:44:01 -03:00
acdf35e5a7 poner archivos de webroot 2025-12-15 17:06:28 -03:00
ee5513587e Merge branch 'webroot-certbot' 2025-12-15 14:26:29 -03:00
4ad59e5068 feat: implement webroot method for root domain SSL certificates
Replace dns-standalone with HTTP-01 webroot validation for root domains
(when root: yes flag is set). This change improves reliability by avoiding
DNS conflicts and simplifies the certificate obtention process.

Changes:
- Add acme_challenge.conf to serve .well-known/acme-challenge directory
- Update certbot.yml to use --webroot for root domains instead of dns-standalone
- Use official certbot/certbot:latest image for webroot (lighter, no DNS needed)
- Add certbot_webroot volume shared between nginx and certbot containers
- Configure vhost.conf to include ACME challenge location for root domains
- Add certbot_webroot variable (/var/www/certbot) to proxy vars

Benefits for root domains:
- No port 53 conflicts with Knot DNS
- Faster validation (HTTP vs DNS propagation)
- More reliable and simpler error handling
- Works with nginx already running on port 80

Wildcard domains continue using DNS-01 challenge as HTTP-01 does not
support wildcard certificates.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 14:03:22 -03:00
e17f872aba fix: properly handle DNS delegation for root domain and www subdomain
Updates knsupdate logic to correctly generate DNS records for:
- abyaya.la (zone apex @)
- www.abyaya.la (www subdomain)
- Existing subdomains (sutty, marmite, etc.)

This fixes the Let's Encrypt DNS-01 challenge failure for www.abyaya.la
by ensuring _acme-challenge records are created correctly.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Merge
2025-12-15 13:47:50 -03:00
e5a756525b fix: properly handle DNS delegation for root domain and www subdomain
Updates knsupdate logic to correctly generate DNS records for:
- abyaya.la (zone apex @)
- www.abyaya.la (www subdomain)
- Existing subdomains (sutty, marmite, etc.)

This fixes the Let's Encrypt DNS-01 challenge failure for www.abyaya.la
by ensuring _acme-challenge records are created correctly.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 13:34:17 -03:00
c085c99382 Merge branch 'root-domain' 2025-12-15 13:17:39 -03:00
890c496b4e refactor: rename no_wildcard to root and fix nginx vhost generation
Rename the flag from `no_wildcard` to `root` throughout the codebase
for better semantics, and fix nginx configuration generation for root
domains.

Changes:
1. Renamed `no_wildcard` → `root` flag in:
   - abyayala.yml (abyaya_root service)
   - roles/certbot/tasks/certbot.yml (uses is_root_domain internally)
   - README-root-domain.md (documentation)

2. Fixed nginx vhost generation in roles/proxy/templates/:
   - vhost.conf: Handle root domains without leading dot in server_name
     * root: yes → `server_name abyaya.la www.abyaya.la;` (exact match)
     * root: no  → `server_name .comun.abyaya.la;` (wildcard match)
   - stream.conf: Same logic for SSH proxy streams

Problem fixed:
- Previous: `.abyaya.la` matched all subdomains, conflicting with
  other vhosts (comun.abyaya.la, sutty.abyaya.la, etc.)
- Now: `abyaya.la www.abyaya.la` matches only root domain exactly

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 12:15:19 -03:00
c86059e319 Merge pull request 'Custom DNS' (#78) from customdnsfiles into master
Reviewed-on: #78
2025-12-12 18:50:15 +00:00
07dfd834ca feat: add support for root domain www.abyaya.la
Add capability to route root domain (abyaya.la and www.abyaya.la)
to sutty.comun while maintaining all existing subdomain routes.

Changes:
- Add no_wildcard flag support in certbot certificate generation
- Split certificate obtention into two conditional paths:
  * Standard mode (with wildcard) for subdomains
  * No-wildcard mode for root domains
- Add abyaya_root service in matrix routing to sutty.comun
- Include implementation plan as README-root-domain.md

Technical details:
- Certificates for root domain will only include abyaya.la and
  www.abyaya.la (no *.abyaya.la wildcard)
- Prevents certificate confusion between root and subdomains
- Maintains clean separation of responsibilities
- All existing subdomain certificates remain unchanged

Generated with Claude Code
2025-12-12 15:15:22 -03:00
554c51e94c Merge branch 'master' into customdnsfiles 2025-12-12 14:26:22 -03:00
7673ccd160 tageo como instalacion esto 2025-12-12 14:25:03 -03:00
ad118402f7 fix: separar set_fact para compatibilidad con Ansible 2.15+
En Ansible 2.15+ las variables en el mismo set_fact se evalúan
simultáneamente, no secuencialmente. Separar needs_cert en su propio
set_fact antes de usarlo en needs_vhost y obtain_cert.
2025-12-12 13:42:05 -03:00
54b24af0b5 fix: aplicar | bool en when clause de obtain_cert 2025-12-12 13:34:49 -03:00
08a3e563d4 fix: envolver expresiones booleanas en templates Jinja2
Aplicar | bool al resultado final de cada expresión y envolver
en sintaxis {{ }} para forzar evaluación correcta como booleanos.
2025-12-12 12:42:50 -03:00
b31a9abcad fix: forzar conversión a bool en condicionales de certbot
Ansible requiere que las condicionales resulten en booleanos.
Agregado filtro | bool para convertir explícitamente strings a booleanos
en las evaluaciones de needs_cert, needs_vhost y obtain_cert.
2025-12-12 11:35:23 -03:00
11ec613ae9 fix: mover limpieza de repos Docker fuera del bloque installation
La limpieza de repositorios Docker antiguos debe ejecutarse SIEMPRE,
incluso cuando se usa --skip-tags=installation, para evitar conflictos
APT antes de que knsupdate u otros roles intenten usar apt.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-12 11:09:48 -03:00
3fb144d02f fix: limpiar repositorios Docker antiguos antes de configurar deb822
Soluciona conflicto APT causado por configuraciones de repositorio Docker
duplicadas con valores Signed-By contradictorios. Ahora se eliminan los
archivos de repositorio antiguos antes de agregar la configuración deb822.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-12 10:58:59 -03:00
e5dff263d1 deabajo nodo 2025-12-11 18:40:38 -06:00
f
06654b9e41 Merge branch 'master' into issue42 2025-12-06 12:09:17 -03:00
d94cfd89d0 fix: asegurar que dominio .abyaya.la esté siempre en domains[0]
Modifica la lógica para garantizar que el dominio .abyaya.la siempre
sea el primero en la lista de dominios, independientemente del orden
definido en abyayala.yml. Esto es crítico para certificados SSL y
configuraciones vhost que dependen de domains[0].
2025-12-02 19:07:38 -03:00
1a00b8a60c expand en certbot para multiples dominios variables 2025-12-02 17:44:30 -03:00
127504cb62 fix logs del certbot 2025-12-02 17:37:18 -03:00
1b69f0d13f simplifico sintaxis con include 2025-12-01 15:08:07 -03:00
5e849ecee2 Merge branch 'master' into customdnsfiles 2025-12-01 14:44:51 -03:00
a973291506 Merge pull request 'WIP fix instalacion de Abyayala toolkit' (#79) from fix_installation into master
Reviewed-on: #79
Reviewed-by: fauno <fauno@sutty.coop.ar>
Testeado por Pirra
2025-12-01 17:31:18 +00:00
43ea3c9a58 Merge branch 'master' into fix_installation 2025-12-01 14:30:01 -03:00
4bec6e7fae agregue llavero 2025-11-28 13:16:06 -06:00
f180972d15 fix: revertir proxy_ssl_name a $ssl_server_name
El uso de $host en lugar de $ssl_server_name no es correcto ya que:
- proxy_ssl_verify está deshabilitado, por lo que el SNI no importa
- $ssl_server_name es el valor correcto para SNI en proxies SSL
- $host causaba confusión innecesaria

Revierte a la configuración estándar y correcta.
2025-11-27 16:28:55 -03:00
08a9a38fa5 Merge branch 'testing' into fix_installation 2025-11-26 18:20:29 -03:00
51bd9c9935 feat: agregar configuración group_vars para host testing
- Definir host_ip: 157.180.114.62
- Requerido por rol knsupdate y certbot
2025-11-26 18:13:55 -03:00
4f18275831 Merge branch 'fix-apt-modules-deprecated' into fix_installation 2025-11-26 18:07:23 -03:00
82f6c62803 fix: actualizar prerequisitos para compatibilidad Debian 12 y 13
- Eliminar software-properties-common (no existe en Debian, solo Ubuntu)
- Eliminar apt-transport-https (incluido por defecto en Debian moderno)
- Eliminar gnupg2 (no requerido explícitamente)
- Mantener solo paquetes esenciales: ca-certificates, curl, python3-pip

Cumple con requisitos oficiales de Docker para Debian:
https://docs.docker.com/engine/install/debian/

Compatible con Debian 12 (bookworm) y 13 (trixie)
2025-11-26 18:05:47 -03:00
dcc6fe2f48 Merge branch 'fix-local-action-deprecated' into fix_installation 2025-11-26 17:59:40 -03:00
7b16934a17 Merge branch 'testing' into fix_installation 2025-11-26 17:44:55 -03:00
0e2d64d39e actualizacion de deprecaciones en instalacion 2025-11-26 17:42:40 -03:00
b750293414 Merge branch 'fix-python-modules-deprecated' into merge_fixes 2025-11-26 17:40:42 -03:00
1ab755fb10 Merge branch 'fix-apt-modules-deprecated' into merge_fixes 2025-11-26 17:40:18 -03:00
7e04c03370 Merge branch 'fix-debian-version-detection' into merge_fixes 2025-11-26 17:37:37 -03:00
cdadee266e proxy nodo llavero 2025-11-26 16:32:29 -03:00
733c9930e2 fix: reemplazar local_action deprecado con delegate_to
- Reemplazar 9 usos de local_action con delegate_to: localhost
- 7 cambios en compose.yml (stat, blockinfile, lineinfile)
- 2 cambios en main.yml (file, template)
- Agregar ansible_connection=local en hosts.production para localhost

Beneficios:
- Cumple con mejores prácticas de Ansible
- Sintaxis moderna y no deprecada
- Evita intentos de conexión SSH a localhost
- Mismo comportamiento funcional que local_action

Refs:
- https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_delegation.html
2025-11-26 16:29:00 -03:00
71e6eb9429 fix: reemplazar apt_key y apt_repository deprecados con deb822
- Reemplazar apt_key con get_url + keyrings directory
- Reemplazar apt_repository con deb822_repository (formato moderno)
- Detección automática de Debian 12 (bookworm) y 13 (trixie)
- Llave GPG específica por repositorio (más seguro)
- Requiere Ansible 2.15+

Beneficios:
- Formato DEB822 moderno y no deprecado
- Mayor seguridad con llaves por repositorio
- Compatible con Debian 12 y 13
- Cumple con mejores prácticas actuales

Refs:
- https://docs.ansible.com/ansible/latest/collections/ansible/builtin/deb822_repository_module.html
- https://manpages.debian.org/bookworm/apt/sources.list.5.en.html
2025-11-26 16:10:51 -03:00
6ed17848cd fix: eliminar módulos Python deprecados y break_system_packages
Cambios realizados:
- Instalar Docker Compose v2 via docker-compose-plugin (apt) en lugar de pip
- Especificar paquetes Docker explícitamente: docker-ce, docker-ce-cli, containerd.io, docker-compose-plugin
- Reemplazar instalación de python-docker via pip por python3-docker desde apt
- Eliminar break_system_packages que rompe aislamiento PEP 668
- Eliminar instalación obsoleta de docker-compose via pip

Beneficios:
- Cumple con PEP 668 (externally managed environments)
- Docker Compose v2 más rápido y mejor integrado
- Gestión de paquetes más limpia y mantenible
- Compatible con Debian 12 y 13

Refs:
- https://peps.python.org/pep-0668/
- https://docs.docker.com/compose/install/linux/
- https://packages.debian.org/bookworm/python3-docker
2025-11-26 15:54:08 -03:00
fd57ecd546 fix: soporte automático para Debian 12 y 13 en repositorio Docker
- Reemplaza 'bookworm' hardcodeado con detección automática usando ansible_distribution_release
- Agrega validación explícita que solo permite Debian 12 (bookworm) o 13 (trixie)
- Mensaje de error claro si se intenta usar en versión no soportada
- Comentarios actualizados indicando versiones soportadas

Esto permite que el rol funcione automáticamente en Debian 12 y 13
sin necesidad de cambios manuales en el código.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 15:17:46 -03:00
dfbd04480c Merge branch 'master' into cutomdnsfile 2025-11-26 14:50:46 -03:00
7cdf7bb885 ej de test net 2025-11-26 14:41:57 -03:00
ecab24c02f VPS 4 testing 2025-11-26 13:11:59 -03:00
180a7f2ab6 recorto algunos tld innecesarios
y le saco el no a ssl para otra cosa
2025-11-26 13:11:09 -03:00
f106838dc1 dns_extras en archivos con nombres de dominio principal, en
roles/knsupdate/files/dns_extras
2025-11-25 14:04:05 -03:00
a3863b9465 Merge pull request 'issue-39-default' (#72) from issue-39-default into master
Reviewed-on: #72
closes #39 o no??
2025-11-20 22:34:44 +00:00
a27a86ce6b asi seria dejar todo esto default para todos los vhosts
pero especificarlos en roles/proxy/files/custom, los sobrescribiria a estos o no?
es deseable que si
cerraria PR #65
2025-10-23 18:30:20 -03:00
27 changed files with 403 additions and 103 deletions

99
README-root-domain.md Normal file
View File

@ -0,0 +1,99 @@
# Plan: Añadir Soporte para Dominio Principal www.abyaya.la
## Objetivo
Habilitar que el dominio raíz `abyaya.la` y `www.abyaya.la` apunten al servidor `sutty.comun`, mientras se mantienen funcionando todos los subdominios existentes (ej: `sutty.abyaya.la`, `marmite.abyaya.la`).
## Estrategia de Desarrollo
### Rama Tópica
Los cambios se implementarán en esta rama tópica:
- **Nombre de rama**: `root-domain`
- **Bifurcada desde**: `master`
## Contexto Técnico
### Arquitectura Actual
- **Certificados**: Cada servicio en `abyayala.yml` con `domains` + `nodo` genera un certificado con certbot: `-d DOMAIN -d *.DOMAIN`
- **Patron actual**: Todos los dominios siguen el patrón `subdomain.abyaya.la` (ej: `sutty.abyaya.la`)
### Decisión de Diseño
Crear un **servicio separado** para el dominio raíz con un nuevo flag `root: yes` que indica a certbot que NO solicite el certificado wildcard. Esto mantiene limpia la separación de responsabilidades:
- Servicio `sutty`: maneja `sutty.abyaya.la` y `*.sutty.abyaya.la`
- Servicio `abyaya_root`: maneja únicamente `abyaya.la` y `www.abyaya.la` (sin wildcard)
## Cambios Implementados
### 1. `roles/certbot/tasks/certbot.yml`
- Lee el flag `root` del servicio y lo mapea a variable `is_root_domain`
- Dividido el bloque de certificados en dos tareas condicionales:
- Modo estándar (con wildcard): para subdominios (`when: not is_root_domain`)
- Modo sin wildcard: para dominios raíz (`when: is_root_domain`)
### 2. `roles/proxy/templates/vhost.conf`
- Agregado condicional para manejar flag `root`
- Cuando `root: yes`: genera `server_name` sin punto prefijo (exacto)
- Cuando `root: no` (default): genera `server_name .domain` (con wildcard)
### 3. `abyayala.yml`
- Agregado servicio `abyaya_root` con:
- Dominios: `abyaya.la` y `www.abyaya.la`
- Enrutamiento a: `sutty.comun`
- Flag: `root: yes`
- HTTPS forzado y compresión habilitada
## Comandos de Despliegue
### Despliegue completo
```bash
ansible-playbook deploy.yml -e "alt=abyayala host=hetzner"
```
### Despliegue solo del servicio nuevo (para testing)
```bash
ansible-playbook deploy.yml -e "alt=abyayala host=hetzner service=abyaya_root"
```
## Verificación Post-Despliegue
### 1. Verificar certificado
```bash
ssh root@hetzner
ls -la /var/lib/docker/volumes/abyayala_certs_data/_data/live/abyaya.la/
docker run --rm -v abyayala_certs_data:/etc/letsencrypt certbot/certbot certificates | grep abyaya.la
```
Debe contener: `abyaya.la`, `www.abyaya.la` (SIN `*.abyaya.la`)
### 2. Verificar nginx
```bash
cat /opt/abyayala/proxy/vhosts/abyaya.la.conf
docker exec abyayala_proxy nginx -t
```
### 3. Verificar enrutamiento
```bash
curl -I http://abyaya.la # Debe redirigir a HTTPS
curl -I https://abyaya.la # Debe devolver 200 OK
curl -I https://www.abyaya.la # Debe devolver 200 OK
```
### 4. Verificar subdominios siguen funcionando
```bash
curl -I https://sutty.abyaya.la
curl -I https://marmite.abyaya.la
```
## Requisitos DNS
Antes del despliegue, configurar:
```
abyaya.la A <IP-del-proxy>
www.abyaya.la A <IP-del-proxy>
```
## Extensibilidad Futura
Este patrón `root: yes` puede reutilizarse para otros dominios raíz:
1. Agregar entrada en `abyayala.yml` con `root: yes`
2. Desplegar con ansible-playbook
3. Configurar DNS

View File

@ -14,7 +14,6 @@ matrix:
roles:
- rap
nodos:
- llavero
- marmite
- nodochasqui
- yanapak
@ -59,6 +58,8 @@ matrix:
- carabobolibre
- samatuun
- kaasavi
- llavero
- deabajo
- service_name: respaldos
domains:
@ -165,6 +166,15 @@ matrix:
force_https: yes
enable_compression: yes
- service_name: abyaya_root
domains:
- abyaya.la
- www.abyaya.la
nodo: sutty.comun
force_https: yes
enable_compression: yes
root: yes
- service_name: mexe
domains:
- mexe.abyaya.la
@ -204,11 +214,14 @@ matrix:
- service_name: kipu
domains:
# - abyaya.la
# - www.abyaya.la
- kipu.latina.red
nodo: kipu.comun
ssl: no
ports:
- 223
ssl: yes
# root: yes
- service_name: carabobolibre
domains:
@ -227,3 +240,15 @@ matrix:
- kaasavi.abyaya.la
nodo: kaasavi.comun
force_https: yes
- service_name: llavero
domains:
- llavero.abyaya.la
nodo: llavero.comun
force_https: yes
- service_name: deabajo
domains:
- deabajo.abyaya.la
nodo: deabajo.comun
force_https: yes

1
group_vars/testing/vars Normal file
View File

@ -0,0 +1 @@
host_ip: 157.180.114.62

View File

@ -1,5 +1,5 @@
[localhost]
127.0.0.1
127.0.0.1 ansible_connection=local
[hetzner]
5.161.236.18
@ -11,3 +11,9 @@ ansible_ssh_user=root
sutty.nl
[sutty:vars]
[testing]
157.180.114.62
[testing:vars]
ansible_ssh_user=root

View File

@ -1,13 +1,13 @@
- name: check if service volumes exists
local_action:
module: stat
stat:
path: "{{ playbook_dir }}/roles/{{ item.roles[0] | default('proxy') }}/templates/volumes.yml"
delegate_to: localhost
register: volumes_def
- name: check if service networks exists
local_action:
module: stat
stat:
path: "{{ playbook_dir }}/roles/{{ item.roles[0] | default('proxy') }}/templates/networks.yml"
delegate_to: localhost
register: networks_def
- set_fact:
@ -22,53 +22,53 @@
when: networks_def.stat.exists
- name: define services in local composition
local_action:
module: blockinfile
blockinfile:
path: "{{ local_compose_path }}/docker-compose.yml"
insertafter: "services:"
marker: "# {mark} {{ service_name|upper }}"
block: "{{ services_content }}"
delegate_to: localhost
changed_when: false
- name: define volumes in local composition
local_action:
module: lineinfile
lineinfile:
path: "{{ local_compose_path }}/docker-compose.yml"
insertafter: "# volumenes compartidos"
line: "volumes: #"
state: present
regexp: "volumes: #"
delegate_to: localhost
when: volumes_def.stat.exists
changed_when: false
- name: define volumes content in local composition
local_action:
module: lineinfile
lineinfile:
path: "{{ local_compose_path }}/docker-compose.yml"
insertafter: "volumes: #"
line: "{{ volumes_content }}"
state: present
regexp: "{{ volumes_content }}"
delegate_to: localhost
when: volumes_content is defined
changed_when: false
- name: define networks in local composition
local_action:
module: lineinfile
lineinfile:
path: "{{ local_compose_path }}/docker-compose.yml"
insertafter: "# redes compartidas"
line: "networks: #"
state: present
regexp: "networks: #"
delegate_to: localhost
when: networks_def.stat.exists
changed_when: false
- name: define networks content in local composition
local_action:
module: lineinfile
lineinfile:
path: "{{ local_compose_path }}/docker-compose.yml"
insertafter: "networks: #"
line: "{{ networks_content }}"
state: present
delegate_to: localhost
when: networks_content is defined
changed_when: false

View File

@ -1,28 +1,56 @@
# DOCKER CE this is specific for Debian
# https://docs.docker.com/install/linux/docker-ce/debian/
# Soporta Debian 12 (bookworm) y Debian 13 (trixie)
# Clean up conflicting Docker repositories first (always runs, even with --skip-tags=installation)
- name: remove old docker repository files to avoid APT conflicts
file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/docker.list
- /etc/apt/sources.list.d/download_docker_com_linux_debian.list
- block:
- name: "unattended upgrades"
apt:
name: "unattended-upgrades"
state: "present"
- name: required packages
apt:
name: ['apt-transport-https', 'ca-certificates', 'curl', 'gnupg2', 'software-properties-common', 'python3-pip']
name: ['ca-certificates', 'curl', 'python3-pip']
state: present
- name: docker signing key
apt_key:
- name: create keyrings directory
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: download docker gpg key
get_url:
url: https://download.docker.com/linux/debian/gpg
state: present
- name: docker apt repository
apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/debian bookworm stable
dest: /etc/apt/keyrings/docker.asc
mode: '0644'
- name: install docker community edition
- name: add docker repository with deb822 format
deb822_repository:
name: docker
types: [deb]
uris: https://download.docker.com/linux/debian
suites: ["{{ ansible_distribution_release }}"]
components: [stable]
architectures: [amd64]
signed_by: /etc/apt/keyrings/docker.asc
- name: install docker community edition and compose plugin
apt:
name: docker-ce
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
update_cache: yes
- name: is node already in swarm mode
@ -48,23 +76,11 @@
state: present
# ansible-docker requirements
- name: python package docker-py is deprecated
pip:
name: docker-py
state: absent
break_system_packages: true
- name: ensure python package docker is present
pip:
name: docker
# Use system packages instead of pip to avoid break_system_packages
- name: ensure python3-docker package is present
apt:
name: python3-docker
state: present
break_system_packages: true
- name: ensure python package docker-compose is present
pip:
name: docker-compose
state: present
break_system_packages: true
tags: installation
@ -74,16 +90,16 @@
file: path={{ compose_path }} state=directory
- name: make sure local compose path exists
local_action:
module: file
path: "{{ local_compose_path }}"
file:
path: "{{ local_compose_path }}"
state: directory
delegate_to: localhost
- name: clean docker-compose.yml
local_action:
module: template
template:
dest: "{{ local_compose_path }}/docker-compose.yml"
src: roles/althost/templates/docker-compose.yml
delegate_to: localhost
changed_when: false
- name: execute roles per domain mapping

View File

@ -10,33 +10,55 @@
register: vhost_stat
- set_fact:
needs_cert: (loop.ssl | default(domains_default_ssl) ) or (loop.force_https | default(domains_default_force_https))
needs_vhost: needs_cert and not vhost_stat.stat.exists
obtain_cert: needs_cert and not ssl_cert.stat.exists
needs_cert: "{{ ((loop.ssl | default(domains_default_ssl) | bool) or (loop.force_https | default(domains_default_force_https) | bool)) | bool }}"
- set_fact:
needs_vhost: "{{ (needs_cert | bool and not vhost_stat.stat.exists) | bool }}"
obtain_cert: "{{ (needs_cert | bool and not ssl_cert.stat.exists) | bool }}"
- name: certificate obtention
block:
- set_fact:
vhost: "{{ loop }}"
- name: fetch certificate with certbot container
- set_fact:
is_root_domain: "{{ loop.root | default(false) | bool }}"
- name: fetch certificate with certbot container (with wildcard)
docker_container:
name: chencriptemos
image: "{{ CERTBOT_image }}"
state: started
volumes:
- "{{ althost }}_certs_data:/etc/letsencrypt"
command: "--non-interactive --agree-tos --email {{ webmaster_email }} certonly --preferred-challenges dns --authenticator dns-standalone --dns-standalone-address={{ host_ip }} --dns-standalone-port=53 --dns-standalone-propagation-seconds=10 {% for domain in loop.domains %} -d {{ domain }} -d *.{{ domain }} {% endfor %}"
command: "--non-interactive --agree-tos --expand --email {{ webmaster_email }} certonly --preferred-challenges dns --authenticator dns-standalone --dns-standalone-address={{ host_ip }} --dns-standalone-port=53 --dns-standalone-propagation-seconds=10 {% for domain in loop.domains %} -d {{ domain }} -d *.{{ domain }} {% endfor %}"
detach: no
cleanup: yes
ports:
ports:
- "{{ host_ip }}:53:53/tcp"
- "{{ host_ip }}:53:53/udp"
notify:
- reload proxy
register: cert_result
when: not is_root_domain | bool
when: obtain_cert
- name: fetch certificate with certbot container (without wildcard, root domain - using webroot)
docker_container:
name: chencriptemos
image: certbot/certbot:latest
state: started
volumes:
- "{{ althost }}_certs_data:/etc/letsencrypt"
- "{{ althost }}_certbot_webroot:{{ certbot_webroot }}"
command: "--non-interactive --agree-tos --expand --email {{ webmaster_email }} certonly --webroot --webroot-path {{ certbot_webroot }} {% for domain in loop.domains %} -d {{ domain }} {% endfor %}"
detach: no
cleanup: yes
notify:
- reload proxy
register: cert_result
when: is_root_domain | bool
when: obtain_cert | bool
# RESET
- set_fact:

View File

@ -36,7 +36,7 @@
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"
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"
- name: proxy update, after certs renewal
cron:
@ -44,12 +44,4 @@
day: 4,18
hour: 6
minute: 10
job: "docker service update --force {{ althost }}_proxy"
- name: mail proxy update, after certs renewal
cron:
name: mail proxy update
day: 4,18
hour: 6
minute: 20
job: "docker service update {{ althost }}_correspondencia_front"
job: "docker service update --force {{ althost }}_proxy"

View File

@ -0,0 +1,8 @@
del pilmaiken mx
del pilmaiken txt
del pilmaiken spf
add pilmaiken mx 10 correspondencia.latina.red.
add pilmaiken txt "v=spf1 mx a:correspondencia.latina.red -all"
add pilmaiken spf "v=spf1 mx a:correspondencia.latina.red -all"
add dkim._domainkey.pilmaiken txt "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQ6JwPaawDzMXuscSgDpvipRFLGXSqgmvvI6jk18lcg0kK2lfxsvXGJ/6U7oCtKa35IEVzdigxD0o7DzklKxAsNIVbcExPJkFWzQuKuP6ATBESo7YUn7Z5qjfxBiNPS0FJp8XpbpUzN+zg/NTgmkggnwwC0tKgcEQ6HnI9AOa1LQIDAQAB"
add _dmarc.pilmaiken txt "v=DMARC1; p=reject; rua=mailto:postmaster@correspondencia.latina.red; ruf=mailto:postmaster@correspondencia.latina.red; adkim=s; aspf=s"

View File

@ -2,5 +2,6 @@
apt:
name: "knot-dnsutils"
state: "present"
tags: installation
- include_tasks: loop.yml

View File

@ -1,24 +1,31 @@
{% for dns_server in dns_servers %}
server {{ dns_server }}
zone {{ zone }}
origin {{ zone }}
ttl 60
del {{ hostname }} a
del {{ hostname }} ns
add {{ hostname }} a {{ host_ip }}
{% if is_abyayala_subdomain %}
{% if hostname != '@' and hostname != 'www' %}
add *.{{ hostname }} a {{ host_ip }}
{% else %}
add {{ domain }} a {{ host_ip }}
add *.{{ domain }} a {{ host_ip }}
{% endif %}
{% if hostname == '@' %}
add _acme-challenge a {{ host_ip }}
add _acme-challenge ns _acme-challenge
{% else %}
add _acme-challenge.{{ hostname }} a {{ host_ip }}
add _acme-challenge.{{ hostname }} ns _acme-challenge
{% if vhost.dns_extras is defined %}
{% for dns_extra in vhost.dns_extras %}
{{ dns_extra }}
{% endfor %}
{% endif %}
{% include "files/dns_extras/" ~ vhost.domains[0] ignore missing %}
send
{% endfor %}
quit

View File

@ -0,0 +1,21 @@
========================================
Configuracion de DNS requerida: {{ domain }}
========================================
Por favor configue los siguiente registros DNS en su proveedor:
{% if hostname == '@' %}
{{ zone | regex_replace('\\.$', '') }} IN A {{ host_ip }}
*.{{ zone | regex_replace('\\.$', '') }} IN A {{ host_ip }}
_acme-challenge.{{ zone | regex_replace('\\.$', '') }} IN NS ns-acme.{{ zone | regex_replace('\\.$', '') }}.
ns-acme.{{ zone | regex_replace('\\.$', '') }} IN A {{ host_ip }}
{% else %}
{{ hostname }}.{{ zone | regex_replace('\\.$', '') }} IN A {{ host_ip }}
*.{{ hostname }}.{{ zone | regex_replace('\\.$', '') }} IN A {{ host_ip }}
_acme-challenge.{{ hostname }}.{{ zone | regex_replace('\\.$', '') }} IN NS ns-acme.{{ hostname }}.{{ zone | regex_replace('\\.$', '') }}.
ns-acme.{{ hostname }}.{{ zone | regex_replace('\\.$', '') }} IN A {{ host_ip }}
{% endif %}
========================================

View File

@ -2,4 +2,4 @@
include_tasks: update_domain.yml
with_items: "{{ vhost.domains }}"
loop_control:
loop_var: domain
loop_var: domain

View File

@ -34,3 +34,9 @@
shell: knsupdate
args:
stdin: "{{ lookup('template', 'templates/commands.j2') }}"
when: is_abyayala_subdomain
- name: display DNS configuration instructions for external domains
debug:
msg: "{{ lookup('template', 'templates/dns_info.j2') }}"
when: not is_abyayala_subdomain

View File

@ -7,14 +7,6 @@ dns_servers:
compound_tlds:
- com.ar
- com.mx
- co.uk
- com.br
- gov.ar
- org.ar
- gob.ar
- net.ar
- mil.ar
- edu.ar
- co.nz
- net.nz
- org.nz
- edu.ar

View File

@ -1,11 +0,0 @@
proxy_buffering off;
proxy_request_buffering off;
proxy_redirect off;
proxy_connect_timeout 3m;
proxy_send_timeout 3m;
proxy_read_timeout 3m;
limit_conn connection_limit 50;
limit_req zone=request_limit nodelay burst=20;
add_header Retry-After $retry_after always;

View File

@ -1,4 +0,0 @@
add_header X-Frame-Options "sameorigin";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";

View File

@ -15,6 +15,7 @@
with_items:
- "{{ stream_path }}"
- "{{ conf_path }}"
- "{{ certbot_webroot }}"
loop_control:
loop_var: comun
@ -32,6 +33,7 @@
- common.conf
- common_ssl.conf
- nginx.conf
- acme_challenge.conf
loop_control:
loop_var: common
@ -45,13 +47,16 @@
loop_control:
loop_var: domino
- name: add default abyaya.la subdomain if not present
- name: ensure abyaya.la subdomain is always first in domains list
set_fact:
matrix_loop_with_defaults: "{{ matrix_loop_with_defaults | default([]) | union([ item_with_default ]) }}"
vars:
has_abyayala_domain: "{{ item.domains | select('match', '.*\\.abyaya\\.la$') | list | length > 0 }}"
existing_abyayala_domains: "{{ item.domains | select('match', '.*\\.abyaya\\.la$') | list }}"
has_abyayala_domain: "{{ existing_abyayala_domains | length > 0 }}"
default_domain: "{{ item.service_name }}.abyaya.la"
domains_with_default: "{{ item.domains + [default_domain] if not has_abyayala_domain else item.domains }}"
other_domains: "{{ item.domains | reject('match', '.*\\.abyaya\\.la$') | list }}"
abyayala_domain_to_use: "{{ existing_abyayala_domains[0] if has_abyayala_domain else default_domain }}"
domains_with_default: "{{ [abyayala_domain_to_use] + other_domains }}"
item_with_default: "{{ item | combine({'domains': domains_with_default}) }}"
with_items: "{{ matrix_loop | default([]) }}"
@ -89,6 +94,14 @@
when: (ma.ports is defined)
loop_control:
loop_var: ma
- name: add gitea port if any service has gitea_port defined
set_fact:
matrix_ports: "{{ matrix_ports | default([]) | union([ma.gitea_port]) }}"
with_items: "{{ matrix }}"
when: (ma.gitea_port is defined)
loop_control:
loop_var: ma
- include_tasks: ../../althost/tasks/compose.yml
vars: # forcing since this role is included statically

View File

@ -0,0 +1,25 @@
# Let's Encrypt ACME challenge configuration
# This configuration serves the .well-known/acme-challenge directory
# for HTTP-01 validation when using webroot method
location ^~ /.well-known/acme-challenge/ {
# Serve files from the certbot webroot
root {{ certbot_webroot }};
# Allow access to challenge files
allow all;
# Ensure plain text content type
default_type "text/plain";
# Disable any authentication
auth_basic off;
# Serve the challenge file directly
try_files $uri =404;
}
# Deny access to other .well-known paths for security
location ~ /\.well-known/ {
deny all;
}

View File

@ -0,0 +1,4 @@
add_header X-Frame-Options "sameorigin";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";

View File

@ -12,10 +12,12 @@
gzip_disable "msie6";
{% endif %}
client_max_body_size 1G;
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_ssl_name $host;
proxy_ssl_name $ssl_server_name;
proxy_pass https://$comun_{{ vhost.nodo | replace(".", "") }};
@ -27,6 +29,18 @@
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_buffering off;
proxy_request_buffering off;
proxy_redirect off;
proxy_connect_timeout 3m;
proxy_send_timeout 3m;
proxy_read_timeout 3m;
limit_conn connection_limit 50;
limit_req zone=request_limit nodelay burst=20;
add_header Retry-After $retry_after always;
{% include "files/custom_proxy_includes/" ~ vhost.domains[0] ignore missing %}
}
# END PROXY

View File

@ -19,3 +19,4 @@
- "certs_data:{{ nginx_certs_path }}:ro"
- "{{ conf_path }}/nginx.conf:/etc/nginx/nginx.conf:ro"
- "{{ stream_path }}:/etc/nginx/stream.d/"
- "certbot_webroot:{{ certbot_webroot }}"

View File

@ -2,10 +2,34 @@ upstream ssh_{{ vhost.nodo | replace(".", "") }} {
server {{ vhost.nodo }}:22;
}
{% if vhost.gitea_port is defined %}
upstream gitea_{{ vhost.nodo | replace(".", "") }} {
server {{ vhost.nodo }}:{{ vhost.gitea_port }};
}
{% endif %}
server {
listen {{ vhost.ports[0] }};
server_name {{ vhost.service_name }}.abyaya.la;
{% if vhost.root | default(false) %}
server_name {{ vhost.domains | join(' ') }};
{% else %}
server_name .{{ vhost.domains | join(' .') }};
{% endif %}
proxy_pass ssh_{{ vhost.nodo | replace(".", "") }};
}
}
{% if vhost.gitea_port is defined %}
server {
listen {{ vhost.gitea_port }};
{% if vhost.root | default(false) %}
server_name {{ vhost.domains | join(' ') }};
{% else %}
server_name .{{ vhost.domains | join(' .') }};
{% endif %}
proxy_pass gitea_{{ vhost.nodo | replace(".", "") }};
}
{% endif %}

View File

@ -1,18 +1,31 @@
map $http_host $comun_{{ vhost.nodo | replace(".", "") }} {
hostnames;
{% for domain in vhost.domains %}
{% if vhost.root | default(false) %}
{{ domain }} {{ vhost.nodo }};
{% else %}
.{{ domain }} {{ vhost.nodo }};
{% endif %}
{% endfor %}
}
server {
{% if vhost.root | default(false) %}
server_name {{ vhost.domains | join(' ') }};
{% else %}
server_name .{{ vhost.domains | join(' .') }};
{% endif %}
listen 80;
resolver 10.13.12.1 valid=300s;
resolver_timeout 5s;
{% if vhost.root | default(false) %}
# ACME challenge for HTTP-01 validation (webroot method for root domains)
include conf/acme_challenge.conf;
{% endif %}
{% if not needs_vhost and ((vhost.ssl | default(domains_default_ssl) ) or (vhost.force_https | default(domains_default_force_https))) %}
listen 443 ssl;

View File

@ -1 +1,2 @@
certs_data:
certbot_webroot:

View File

@ -15,3 +15,4 @@ default_stream: roles/proxy/templates/stream.conf
# certbot
webmaster_email: webmaster@numerica.cl
CERTBOT_image: numericalatina/certbot-wildcard
certbot_webroot: /var/www/certbot

23
testnet.yml Normal file
View File

@ -0,0 +1,23 @@
althost: testnet
matrix:
- service_name: comun
roles:
- kemal
domains:
- comun.abyayala.red
- service_name: dns
roles:
- knsupdate
- service_name: vpn
roles:
- rap
nodos:
- qi
- service_name: qi
domains:
- qi.abyayala.red
nodo: qi.comun
# force_https: yes