Compare commits
4 Commits
refactor-s
...
feat/param
| Author | SHA1 | Date | |
|---|---|---|---|
| 57c31fbd98 | |||
| ce103e4eda | |||
| 4979f6f8b8 | |||
| ad5e18bda2 |
@ -1,162 +0,0 @@
|
||||
# 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
|
||||
```
|
||||
@ -1,99 +0,0 @@
|
||||
# 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
|
||||
29
abyayala.yml
29
abyayala.yml
@ -59,8 +59,6 @@ matrix:
|
||||
- samatuun
|
||||
- kaasavi
|
||||
- llavero
|
||||
- deabajo
|
||||
- testnet
|
||||
|
||||
- service_name: respaldos
|
||||
domains:
|
||||
@ -68,9 +66,11 @@ matrix:
|
||||
nodo: respaldos.comun
|
||||
force_https: yes
|
||||
|
||||
- nodo: marmite
|
||||
- service_name: marmite
|
||||
domains:
|
||||
- marmite.abyaya.la
|
||||
nodo: marmite.comun
|
||||
force_https: yes
|
||||
# Auto-deduced: domains: [marmite.abyaya.la], rap_dn: marmite.comun
|
||||
|
||||
- service_name: yanapak
|
||||
domains:
|
||||
@ -165,15 +165,6 @@ 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
|
||||
@ -213,14 +204,10 @@ matrix:
|
||||
|
||||
- service_name: kipu
|
||||
domains:
|
||||
# - abyaya.la
|
||||
# - www.abyaya.la
|
||||
- kipu.latina.red
|
||||
nodo: kipu.comun
|
||||
ports:
|
||||
- 223
|
||||
ssl: yes
|
||||
# root: yes
|
||||
|
||||
- service_name: carabobolibre
|
||||
domains:
|
||||
@ -244,10 +231,4 @@ matrix:
|
||||
domains:
|
||||
- llavero.abyaya.la
|
||||
nodo: llavero.comun
|
||||
force_https: yes
|
||||
|
||||
- service_name: deabajo
|
||||
domains:
|
||||
- deabajo.abyaya.la
|
||||
nodo: deabajo.comun
|
||||
force_https: yes
|
||||
force_https: yes
|
||||
@ -1,6 +1,5 @@
|
||||
host_ip: 5.161.236.18
|
||||
main_zone: abyaya.la
|
||||
vpn_name: comun
|
||||
vpn_proxy: 10.13.12.1
|
||||
proxy_scale: 2
|
||||
domains_default_force_https: yes
|
||||
|
||||
@ -1,5 +1,2 @@
|
||||
host_ip: 157.180.114.62
|
||||
main_zone: abyayala.red
|
||||
vpn_name: comun
|
||||
vpn_proxy: 10.13.12.159
|
||||
proxy_scale: 1
|
||||
@ -1,16 +1,6 @@
|
||||
# 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:
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
- set_fact:
|
||||
current_service: "{{ item }}"
|
||||
# Deducir service_name: usar 'nodo' si existe, sino 'service_name' (retrocompatibilidad)
|
||||
service_name: "{{ item.nodo | default(item.service_name) }}"
|
||||
service_name: "{{ item.service_name }}"
|
||||
service_roles: "{{ item.roles | default([]) }}"
|
||||
|
||||
- include_role:
|
||||
- include_role:
|
||||
name: "{{ current_role_name }}"
|
||||
with_items: "{{ service_roles }}"
|
||||
loop_control:
|
||||
|
||||
@ -10,55 +10,33 @@
|
||||
register: vhost_stat
|
||||
|
||||
- set_fact:
|
||||
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 }}"
|
||||
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
|
||||
|
||||
- name: certificate obtention
|
||||
block:
|
||||
- set_fact:
|
||||
vhost: "{{ loop }}"
|
||||
|
||||
- set_fact:
|
||||
is_root_domain: "{{ loop.root | default(false) | bool }}"
|
||||
|
||||
- name: fetch certificate with certbot container (with wildcard)
|
||||
- name: fetch certificate with certbot container
|
||||
docker_container:
|
||||
name: chencriptemos
|
||||
image: "{{ CERTBOT_image }}"
|
||||
state: started
|
||||
volumes:
|
||||
- "{{ althost }}_certs_data:/etc/letsencrypt"
|
||||
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 %}"
|
||||
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 %}"
|
||||
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
|
||||
|
||||
- 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
|
||||
when: obtain_cert
|
||||
|
||||
# RESET
|
||||
- set_fact:
|
||||
|
||||
@ -30,13 +30,14 @@
|
||||
env: yes
|
||||
value: /bin/bash
|
||||
|
||||
- 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"
|
||||
# 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: proxy update, after certs renewal
|
||||
cron:
|
||||
@ -44,4 +45,12 @@
|
||||
day: 4,18
|
||||
hour: 6
|
||||
minute: 10
|
||||
job: "docker service update --force {{ althost }}_proxy"
|
||||
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"
|
||||
|
||||
@ -122,7 +122,7 @@ interface={{ vpn_name }}
|
||||
except-interface=eth0
|
||||
# Or which to listen on by address (remember to include 127.0.0.1 if
|
||||
# you use this.)
|
||||
listen-address={{ vpn_proxy }},127.0.0.1
|
||||
listen-address=10.13.12.1,127.0.0.1
|
||||
# If you want dnsmasq to provide only DNS service on an interface,
|
||||
# configure it as shown above, and then use the following line to
|
||||
# disable DHCP and TFTP on it.
|
||||
|
||||
@ -5,6 +5,6 @@ server {
|
||||
client_max_body_size 4k;
|
||||
|
||||
location / {
|
||||
proxy_pass http://{{ vpn_proxy }}:3000;
|
||||
proxy_pass http://10.13.12.1:3000;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
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"
|
||||
@ -2,6 +2,5 @@
|
||||
apt:
|
||||
name: "knot-dnsutils"
|
||||
state: "present"
|
||||
tags: installation
|
||||
|
||||
|
||||
- include_tasks: loop.yml
|
||||
|
||||
@ -1,31 +1,24 @@
|
||||
{% 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 hostname != '@' and hostname != 'www' %}
|
||||
{% if is_abyayala_subdomain %}
|
||||
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
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
|
||||
========================================
|
||||
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 %}
|
||||
|
||||
========================================
|
||||
@ -2,4 +2,4 @@
|
||||
include_tasks: update_domain.yml
|
||||
with_items: "{{ vhost.domains }}"
|
||||
loop_control:
|
||||
loop_var: domain
|
||||
loop_var: domain
|
||||
|
||||
@ -29,19 +29,10 @@
|
||||
zone: "{{ domain_parts[-2:] | join('.') }}."
|
||||
hostname: "{{ domain_parts[:-2] | join('.') if domain_parts | length > 2 else '@' }}"
|
||||
when: not is_abyayala_subdomain and not uses_compound_tld
|
||||
|
||||
- name: Debug knsupdate for this domain
|
||||
debug:
|
||||
- debug:
|
||||
msg: "{{ lookup('template', 'templates/commands.j2') }}"
|
||||
when: is_abyayala_subdomain
|
||||
|
||||
- name: knsupdate for this domain
|
||||
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
|
||||
|
||||
@ -10,16 +10,11 @@
|
||||
include_role: name=certbot
|
||||
tags: certbot
|
||||
|
||||
- name: set main_zone_regex from main_zone
|
||||
set_fact:
|
||||
main_zone_regex: "{{ '.' ~ main_zone | replace('.', '\\.') }}"
|
||||
|
||||
- name: configuration paths
|
||||
file: path={{ abc }} state=directory
|
||||
with_items:
|
||||
- "{{ stream_path }}"
|
||||
- "{{ conf_path }}"
|
||||
- "{{ certbot_webroot }}"
|
||||
loop_control:
|
||||
loop_var: abc
|
||||
|
||||
@ -37,19 +32,34 @@
|
||||
- common.conf
|
||||
- common_ssl.conf
|
||||
- nginx.conf
|
||||
- acme_challenge.conf
|
||||
loop_control:
|
||||
loop_var: common
|
||||
|
||||
- name: neo stuff
|
||||
- name: domains' stuff
|
||||
block:
|
||||
- name: normalize and build matrix_loop
|
||||
include_tasks: normalize_node.yml
|
||||
with_items: "{{ matrix }}"
|
||||
when: "{{ (domino.domains is defined or domino.nodo is defined or domino.service_name is defined) }}"
|
||||
- name: slice matrix with those having domains defined
|
||||
set_fact:
|
||||
matrix_loop: "{{ matrix_loop | default([]) | union([ domino ]) }}"
|
||||
with_items: "{{ matrix }}"
|
||||
when: "{{ domino.domains is defined and domino.nodo is defined }}"
|
||||
loop_control:
|
||||
loop_var: domino
|
||||
|
||||
- name: add default main zone subdomain if not present
|
||||
set_fact:
|
||||
matrix_loop_with_defaults: "{{ matrix_loop_with_defaults | default([]) | union([ item_with_default ]) }}"
|
||||
vars:
|
||||
has_abyayala_domain: "{{ item.domains | select('match', '.*\\.' ~ (main_zone | regex_escape) ~ '$') | list | length > 0 }}"
|
||||
default_domain: "{{ item.service_name ~ '.' ~ main_zone }}"
|
||||
domains_with_default: "{{ item.domains + [default_domain] if not has_abyayala_domain else item.domains }}"
|
||||
item_with_default: "{{ item | combine({'domains': domains_with_default}) }}"
|
||||
with_items: "{{ matrix_loop | default([]) }}"
|
||||
|
||||
- name: update matrix_loop with defaults
|
||||
set_fact:
|
||||
matrix_loop: "{{ matrix_loop_with_defaults }}"
|
||||
when: matrix_loop_with_defaults is defined
|
||||
|
||||
- name: certificates loop
|
||||
include_tasks: ../../certbot/tasks/certbot.yml
|
||||
with_items: "{{ matrix_loop | default([]) }}"
|
||||
@ -63,7 +73,7 @@
|
||||
with_items: "{{ matrix_loop }}"
|
||||
loop_control:
|
||||
loop_var: vhost
|
||||
when: ((service is undefined) or (service is defined and service == vhost.service_name)) and not vhost.skip_vhost
|
||||
when: (service is undefined) or (service is defined and service == vhost.service_name)
|
||||
|
||||
- name: streams loop
|
||||
include_tasks: stream.yml
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
- set_fact:
|
||||
_existing_abyayala_domains: "{{ (domino.domains | default([])) | select('match', '.*' ~ main_zone_regex ~ '$') | list }}"
|
||||
_other_domains: "{{ (domino.domains | default([])) | reject('match', '.*' ~ main_zone_regex ~ '$') | list }}"
|
||||
_node_name: "{{ domino.nodo | default(domino.service_name) }}"
|
||||
_default_domain: "{{ (domino.nodo | default(domino.service_name)) ~ '.' ~ main_zone }}"
|
||||
_default_rap_dn: "{{ (domino.nodo | default(domino.service_name)) ~ '.' ~ vpn_name }}"
|
||||
|
||||
- set_fact:
|
||||
domino_normalized:
|
||||
nodo: "{{ _node_name }}"
|
||||
rap_dn: "{{ domino.rap_dn | default(_default_rap_dn) }}"
|
||||
domains: "{{ [(_existing_abyayala_domains[0] if (_existing_abyayala_domains | length > 0) else _default_domain)] + _other_domains }}"
|
||||
force_https: "{{ domino.force_https | default(domains_default_force_https) }}"
|
||||
ports: "{{ domino.ports | default([]) }}"
|
||||
enable_compression: "{{ domino.enable_compression | default(domains_default_enable_compression) }}"
|
||||
roles: "{{ domino.roles | default([]) }}"
|
||||
service_name: "{{ domino.service_name | default(_node_name) }}"
|
||||
skip_vhost: "{{ (domino.roles is defined and (domino.roles | length > 0) and domino.domains is not defined) }}"
|
||||
|
||||
- set_fact:
|
||||
matrix_loop: "{{ matrix_loop | default([]) | union([ domino_normalized ]) }}"
|
||||
@ -1,10 +1,10 @@
|
||||
- set_fact:
|
||||
vhost_dest: "{{ stream_path }}/{{ vhost.service_name }}.conf"
|
||||
vhost_dest: "{{ stream_path }}/{{ vhost.domains[0] }}.conf"
|
||||
|
||||
- name: default stream for ssh
|
||||
template:
|
||||
src: "{{ default_stream }}"
|
||||
dest: "{{ vhost_dest }}"
|
||||
when: vhost.ports is defined and (vhost.ports | length > 0)
|
||||
when: vhost.ports is defined
|
||||
notify:
|
||||
- reload proxy
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
- set_fact:
|
||||
vhost_dest: "{{ vhosts_path }}/{{ vhost.domains[0] }}.conf"
|
||||
|
||||
- block:
|
||||
- block:
|
||||
- set_fact:
|
||||
custom_vhost: "roles/{{ vhost.roles[0] }}/templates/vhost.conf"
|
||||
- set_fact:
|
||||
proxy_conf: "roles/{{ vhost.roles[0] }}/templates/proxy.conf"
|
||||
when: vhost.roles is defined and (vhost.roles | length > 0)
|
||||
when: vhost.roles is defined
|
||||
|
||||
- set_fact:
|
||||
proxy_conf_look: "{{ lookup('template', proxy_conf) }}"
|
||||
@ -16,7 +16,7 @@
|
||||
template:
|
||||
src: "{{ default_vhost }}"
|
||||
dest: "{{ vhost_dest }}"
|
||||
when: (vhost.roles is undefined or (vhost.roles | length == 0) or vhost.roles[0] == 'proxy') or (custom_vhost is undefined or custom_vhost is not is_file)
|
||||
when: (vhost.roles is undefined or vhost.roles[0] == 'proxy') or (custom_vhost is undefined or custom_vhost is not is_file)
|
||||
notify:
|
||||
- reload proxy
|
||||
|
||||
@ -24,6 +24,6 @@
|
||||
template:
|
||||
src: "{{ custom_vhost }}"
|
||||
dest: "{{ vhost_dest }}"
|
||||
when: (vhost.roles is defined and (vhost.roles | length > 0) and vhost.roles[0] != 'proxy') and custom_vhost is defined and custom_vhost is is_file
|
||||
when: (vhost.roles is defined and vhost.roles[0] != 'proxy') and custom_vhost is defined and custom_vhost is is_file
|
||||
notify:
|
||||
- reload proxy
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
# 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;
|
||||
}
|
||||
@ -19,7 +19,7 @@
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name $ssl_server_name;
|
||||
|
||||
proxy_pass https://$comun_{{ vhost.rap_dn | replace(".", "") }};
|
||||
proxy_pass https://$comun_{{ vhost.nodo | replace(".", "") }};
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@ -19,4 +19,3 @@
|
||||
- "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 }}"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
upstream ssh_{{ vhost.rap_dn | replace(".", "") }} {
|
||||
server {{ vhost.rap_dn }}:22;
|
||||
upstream ssh_{{ vhost.nodo | replace(".", "") }} {
|
||||
server {{ vhost.nodo }}:22;
|
||||
}
|
||||
|
||||
server {
|
||||
@ -7,5 +7,5 @@ server {
|
||||
|
||||
server_name {{ vhost.service_name }}.{{ main_zone }};
|
||||
|
||||
proxy_pass ssh_{{ vhost.rap_dn | replace(".", "") }};
|
||||
proxy_pass ssh_{{ vhost.nodo | replace(".", "") }};
|
||||
}
|
||||
@ -1,31 +1,18 @@
|
||||
map $http_host $comun_{{ vhost.rap_dn | replace(".", "") }} {
|
||||
map $http_host $comun_{{ vhost.nodo | replace(".", "") }} {
|
||||
hostnames;
|
||||
{% for domain in vhost.domains %}
|
||||
{% if vhost.root | default(false) %}
|
||||
{{ domain }} {{ vhost.rap_dn }};
|
||||
{% else %}
|
||||
.{{ domain }} {{ vhost.rap_dn }};
|
||||
{% endif %}
|
||||
.{{ domain }} {{ vhost.nodo }};
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
server {
|
||||
{% if vhost.root | default(false) %}
|
||||
server_name {{ vhost.domains | join(' ') }};
|
||||
{% else %}
|
||||
server_name .{{ vhost.domains | join(' .') }};
|
||||
{% endif %}
|
||||
|
||||
listen 80;
|
||||
|
||||
resolver {{ vpn_proxy }} valid=300s;
|
||||
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;
|
||||
|
||||
|
||||
@ -1,2 +1 @@
|
||||
certs_data:
|
||||
certbot_webroot:
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
domains_default_ssl: no
|
||||
domains_default_force_https: no
|
||||
domains_default_enable_compression: no
|
||||
|
||||
# nginx
|
||||
vhosts_path: "{{ compose_path }}/proxy/vhosts"
|
||||
@ -16,4 +15,3 @@ default_stream: roles/proxy/templates/stream.conf
|
||||
# certbot
|
||||
webmaster_email: webmaster@numerica.cl
|
||||
CERTBOT_image: numericalatina/certbot-wildcard
|
||||
certbot_webroot: /var/www/certbot
|
||||
|
||||
Submodule roles/rap/code/rap updated: 05481cdbc3...ec4cd71e6e
@ -2,7 +2,7 @@
|
||||
# https://serverfault.com/questions/1108989/isc-dhcp-client-dhclient-alternative
|
||||
- name: instalar dependecias de la RAP
|
||||
apt:
|
||||
name: ['tinc', 'rsync', 'dhcpcd5']
|
||||
name: ['tinc', 'rsync', 'dhcp5']
|
||||
state: latest
|
||||
# update_cache: yes
|
||||
tags: installation
|
||||
@ -11,13 +11,13 @@
|
||||
|
||||
- name: copiar el codigo fuente
|
||||
synchronize:
|
||||
src: ../roles/rap/code/rap/
|
||||
src: ../roles/rap/code/rap
|
||||
dest: "{{ rap_path }}"
|
||||
perms: true
|
||||
|
||||
- name: hacer ejecutable el archivo rap
|
||||
file:
|
||||
path: "{{ rap_path }}/rap"
|
||||
path: "{{ rap_path }}/rap/rap"
|
||||
state: touch
|
||||
mode: '774'
|
||||
modification_time: preserve
|
||||
@ -26,13 +26,13 @@
|
||||
- name: inicializar el nodo
|
||||
shell:
|
||||
cmd: "./rap init -i {{ nodo }}"
|
||||
chdir: "{{ rap_path }}"
|
||||
chdir: "{{ rap_path }}/rap"
|
||||
environment:
|
||||
NETWORK: "{{ vpn_name }}"
|
||||
|
||||
- name: instalar el nodo
|
||||
shell:
|
||||
cmd: "./rap install -v {{ nodo }}"
|
||||
chdir: "{{ rap_path }}"
|
||||
chdir: "{{ rap_path }}/rap"
|
||||
environment:
|
||||
NETWORK: "{{ vpn_name }}"
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# Instalacion de la RAP en maquina local
|
||||
# ansible-playbook --become tasks/rap.yml -e "host=localhost nodo=chem" -i hosts.local
|
||||
# ansible-playbook tasks/rap.yml -e "host=testing nodo=testnet"
|
||||
---
|
||||
- hosts: "{{ host }}"
|
||||
tasks:
|
||||
|
||||
@ -16,7 +16,8 @@ matrix:
|
||||
nodos:
|
||||
- qi
|
||||
|
||||
- nodo: qi
|
||||
ports:
|
||||
- 531
|
||||
- service_name: qi
|
||||
domains:
|
||||
- qi.abyayala.red
|
||||
nodo: qi.comun
|
||||
# force_https: yes
|
||||
Reference in New Issue
Block a user