diff --git a/FQDN_AUTHORITATIVE.md b/FQDN_AUTHORITATIVE.md new file mode 100644 index 0000000..7801bfb --- /dev/null +++ b/FQDN_AUTHORITATIVE.md @@ -0,0 +1,133 @@ +# Soporte para Dominios FQDN Autoritativos + +Esta feature añade soporte para usar dominios FQDN externos (ejemplo.com, kipu.latina.red, etc.) además de subdominios .abyaya.la. + +## Cambios Implementados + +### 1. Generación Automática de Subdominio Default + +Cuando se define un dominio FQDN, el sistema genera automáticamente un subdominio `.abyaya.la` basado en el `service_name` que funciona como alias. + +**Ejemplo:** +```yaml +- service_name: kipu + domains: + - kipu.latina.red + nodo: kipu.comun + force_https: yes +``` + +El sistema automáticamente añade `kipu.abyaya.la` a la lista de dominios, por lo que ambos dominios funcionarán y redirigirán al primero de la lista. + +### 2. Soporte para TLDs Compuestos + +El sistema detecta automáticamente TLDs compuestos como `.com.ar`, `.co.uk`, `.com.br`, etc., y extrae correctamente la zona DNS. + +**TLDs soportados:** +- com.ar, gov.ar, org.ar, gob.ar, net.ar, mil.ar, edu.ar +- com.mx +- co.uk +- com.br +- co.nz, net.nz, org.nz + +Para añadir más TLDs, editar `roles/knsupdate/vars/main.yml`. + +### 3. Actualización DNS Multi-Zona + +El sistema ahora actualiza correctamente el DNS en Knot para cada dominio según su tipo: + +- **Subdominios .abyaya.la**: Se actualizan en la zona `abyaya.la.` +- **FQDN autoritativos**: Se actualizan en su zona correspondiente (ej: `latina.red.`, `example.com.ar.`) + +La detección es **completamente automática** basada en el sufijo del dominio. + +## Uso + +### Caso Básico: Solo FQDN + +```yaml +- service_name: ejemplo + domains: + - ejemplo.latina.red + nodo: ejemplo.comun + force_https: yes +``` + +**Resultado:** +- `ejemplo.latina.red` → Dominio principal (detectado como FQDN) +- `ejemplo.abyaya.la` → Generado automáticamente como alias +- Ambos tienen certificados SSL wildcard +- Ambos redirigen al primero (ejemplo.latina.red) + +### Caso Avanzado: Múltiples Dominios + +```yaml +- service_name: miapp + domains: + - miapp.com.ar + - miapp.latina.red + - miapp.abyaya.la + nodo: miapp.comun + force_https: yes +``` + +**Resultado:** +- Los tres dominios funcionan +- Todos redirigen al primero (miapp.com.ar) +- Certificados SSL para cada dominio + wildcards +- DNS actualizado en zonas: `com.ar.`, `latina.red.`, `abyaya.la.` + +### Subdominios de FQDN + +```yaml +- service_name: api + domains: + - api.ejemplo.com.ar + nodo: api.comun + force_https: yes +``` + +**Resultado:** +- `api.ejemplo.com.ar` → Dominio principal (hostname: api, zona: com.ar.) +- `api.abyaya.la` → Generado automáticamente + +## Archivos Modificados + +1. **roles/proxy/tasks/main.yml**: Añade dominio default .abyaya.la automáticamente +2. **roles/knsupdate/vars/main.yml**: Lista de TLDs compuestos +3. **roles/knsupdate/tasks/update.yml**: Procesa múltiples dominios +4. **roles/knsupdate/tasks/update_domain.yml**: Nuevo archivo que detecta tipo de dominio +5. **roles/knsupdate/tasks/templates/commands.j2**: Usa zona y hostname dinámicos + +## Comportamiento de Certificados SSL + +Certbot obtiene certificados para **todos** los dominios listados más sus wildcards: + +```bash +certbot certonly -d ejemplo.com.ar -d *.ejemplo.com.ar -d ejemplo.abyaya.la -d *.ejemplo.abyaya.la +``` + +Usa el método `dns-standalone` que requiere que el proxy controle el DNS autoritativo. Esto funciona porque knsupdate actualiza Knot con todos los dominios. + +## Migración desde Configuración Anterior + +La configuración anterior sigue funcionando sin cambios: + +```yaml +- service_name: viejo + domains: + - viejo.abyaya.la + nodo: viejo.comun + force_https: yes +``` + +Todo funciona exactamente igual para subdominios .abyaya.la existentes. + +## Notas Técnicas + +- La detección de tipo de dominio es **completamente automática** basada en el sufijo `.abyaya.la` +- Los subdominios .abyaya.la siempre se generan automáticamente si no están presentes +- La zona DNS se detecta automáticamente considerando TLDs simples y compuestos +- Todos los dominios apuntan al mismo nodo en la VPN +- El primer dominio en la lista es considerado el principal para certificados SSL +- No se requiere ningún flag especial en la configuración diff --git a/abyayala.yml b/abyayala.yml index a621f15..6e78f34 100644 --- a/abyayala.yml +++ b/abyayala.yml @@ -214,14 +214,14 @@ matrix: - service_name: kipu domains: - - abyaya.la - - www.abyaya.la -# - kipu.abyaya.la +# - abyaya.la +# - www.abyaya.la + - kipu.latina.red nodo: kipu.comun ports: - 223 - force_https: yes - root: yes + ssl: yes +# root: yes - service_name: carabobolibre domains: diff --git a/roles/certbot/tasks/certbot.yml b/roles/certbot/tasks/certbot.yml index eb36b2b..f079a5c 100644 --- a/roles/certbot/tasks/certbot.yml +++ b/roles/certbot/tasks/certbot.yml @@ -31,7 +31,7 @@ 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: @@ -50,7 +50,7 @@ volumes: - "{{ althost }}_certs_data:/etc/letsencrypt" - "{{ althost }}_certbot_webroot:{{ certbot_webroot }}" - command: "--non-interactive --agree-tos --email {{ webmaster_email }} certonly --webroot --webroot-path {{ certbot_webroot }} {% for domain in loop.domains %} -d {{ domain }} {% endfor %}" + 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: diff --git a/roles/knsupdate/tasks/templates/commands.j2 b/roles/knsupdate/tasks/templates/commands.j2 index 91a7e8f..8e85edc 100644 --- a/roles/knsupdate/tasks/templates/commands.j2 +++ b/roles/knsupdate/tasks/templates/commands.j2 @@ -1,25 +1,27 @@ {% for dns_server in dns_servers %} server {{ dns_server }} -zone abyaya.la. -origin abyaya.la. +zone {{ zone }} +origin {{ zone }} ttl 60 -del {{ vho }} a -del {{ vho }} ns +del {{ hostname }} a +del {{ hostname }} ns +add {{ hostname }} a {{ host_ip }} -add {{ vho }} a {{ host_ip }} - -{% if vho != '@' and vho != 'www' %} -add *.{{ vho }} a {{ host_ip }} +{% if hostname != '@' and hostname != 'www' %} +add *.{{ hostname }} a {{ host_ip }} +{% else %} +add {{ domain }} a {{ host_ip }} +add *.{{ domain }} a {{ host_ip }} {% endif %} -{% if vho == '@' %} +{% if hostname == '@' %} add _acme-challenge a {{ host_ip }} add _acme-challenge ns _acme-challenge {% else %} -add _acme-challenge.{{ vho }} a {{ host_ip }} -add _acme-challenge.{{ vho }} ns _acme-challenge +add _acme-challenge.{{ hostname }} a {{ host_ip }} +add _acme-challenge.{{ hostname }} ns _acme-challenge {% endif %} {% include "files/dns_extras/" ~ vhost.domains[0] ignore missing %} diff --git a/roles/knsupdate/tasks/templates/dns_info.j2 b/roles/knsupdate/tasks/templates/dns_info.j2 new file mode 100644 index 0000000..f46df2e --- /dev/null +++ b/roles/knsupdate/tasks/templates/dns_info.j2 @@ -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 %} + +======================================== diff --git a/roles/knsupdate/tasks/update.yml b/roles/knsupdate/tasks/update.yml index 74e2266..9e4098a 100644 --- a/roles/knsupdate/tasks/update.yml +++ b/roles/knsupdate/tasks/update.yml @@ -1,14 +1,5 @@ - - set_fact: - vho: >- - {%- if vhost.domains[0] == 'abyaya.la' -%} - @ - {%- elif vhost.domains[0].endswith('.abyaya.la') -%} - {{ vhost.domains[0] | regex_replace('([a-z0-9]+)\\.abyaya\\.la', '\\1') }} - {%- else -%} - {{ vhost.domains[0] | regex_replace('\\.abyaya\\.la$', '') }} - {%- endif -%} - - - name: knsupdate - shell: knsupdate - args: - stdin: "{{ lookup('template', 'templates/commands.j2') }}" + - name: process each domain in the list + include_tasks: update_domain.yml + with_items: "{{ vhost.domains }}" + loop_control: + loop_var: domain \ No newline at end of file diff --git a/roles/knsupdate/tasks/update_domain.yml b/roles/knsupdate/tasks/update_domain.yml new file mode 100644 index 0000000..841342d --- /dev/null +++ b/roles/knsupdate/tasks/update_domain.yml @@ -0,0 +1,42 @@ + - set_fact: + is_abyayala_subdomain: "{{ domain.endswith('.abyaya.la') }}" + + - name: extract zone and hostname for abyaya.la subdomains + set_fact: + zone: "abyaya.la." + hostname: "{{ domain | regex_replace('([a-z0-9-]+)\\.abyaya\\.la', '\\1') }}" + when: is_abyayala_subdomain + + - name: split domain into parts + set_fact: + domain_parts: "{{ domain.split('.') }}" + when: not is_abyayala_subdomain + + - name: detect if domain uses compound TLD + set_fact: + domain_suffix_2: "{{ domain_parts[-2:] | join('.') }}" + uses_compound_tld: "{{ domain_parts[-2:] | join('.') in compound_tlds }}" + when: not is_abyayala_subdomain + + - name: extract zone and hostname for FQDN with compound TLD + set_fact: + zone: "{{ domain_parts[-3:] | join('.') }}." + hostname: "{{ domain_parts[:-3] | join('.') if domain_parts | length > 3 else '@' }}" + when: not is_abyayala_subdomain and uses_compound_tld + + - name: extract zone and hostname for FQDN with simple TLD + set_fact: + 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: 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 diff --git a/roles/knsupdate/vars/main.yml b/roles/knsupdate/vars/main.yml index d81c409..e8e1797 100644 --- a/roles/knsupdate/vars/main.yml +++ b/roles/knsupdate/vars/main.yml @@ -3,3 +3,10 @@ dns_servers: - "athshe.sutty.nl" - "gethen.sutty.nl" - "ganam.sutty.nl" + +compound_tlds: + - com.ar + - com.mx + - com.br + - org.ar + - edu.ar \ No newline at end of file diff --git a/roles/proxy/tasks/main.yml b/roles/proxy/tasks/main.yml index c472f86..8ce8c1b 100644 --- a/roles/proxy/tasks/main.yml +++ b/roles/proxy/tasks/main.yml @@ -47,6 +47,24 @@ loop_control: loop_var: domino + - 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: + 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" + 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([]) }}" + + - 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([]) }}"