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 816f01b..55d3d62 100644 --- a/abyayala.yml +++ b/abyayala.yml @@ -204,11 +204,10 @@ matrix: - service_name: kipu domains: - - kipu.abyaya.la + - kipu.latina.red nodo: kipu.comun ports: - 223 - force_https: yes - service_name: carabobolibre domains: diff --git a/group_vars/hetzner/vars b/group_vars/hetzner/vars index 87a5c11..c1b2458 100644 --- a/group_vars/hetzner/vars +++ b/group_vars/hetzner/vars @@ -1,3 +1,5 @@ host_ip: 5.161.236.18 +main_zone: abyaya.la +vpn_name: comun proxy_scale: 2 domains_default_force_https: yes diff --git a/group_vars/testing/vars b/group_vars/testing/vars index d43597d..d33832b 100644 --- a/group_vars/testing/vars +++ b/group_vars/testing/vars @@ -1 +1,2 @@ host_ip: 157.180.114.62 +main_zone: abyayala.red diff --git a/roles/dnsmasq/tasks/main.yml b/roles/dnsmasq/tasks/main.yml index 718b9d5..88eec72 100644 --- a/roles/dnsmasq/tasks/main.yml +++ b/roles/dnsmasq/tasks/main.yml @@ -2,12 +2,14 @@ apt: name: dnsmasq state: present + - name: configuracion de red comun template: src: dnsmasq.conf dest: "/etc/dnsmasq.conf" notify: - restart dnsmasq + - name: activar el servicio systemd_service: name: dnsmasq diff --git a/roles/dnsmasq/templates/dnsmasq.conf b/roles/dnsmasq/templates/dnsmasq.conf index c3c1060..392dee4 100644 --- a/roles/dnsmasq/templates/dnsmasq.conf +++ b/roles/dnsmasq/templates/dnsmasq.conf @@ -74,8 +74,8 @@ resolv-file=/etc/resolv.local # Add local-only domains here, queries in these domains are answered # from /etc/hosts or DHCP only. -local=/comun/ -domain=comun +local=/{{ vpn_name }}/ +domain={{ vpn_name }} # Add domains which you want to force to an IP address here. # The example below send any host in double-click.net to a local @@ -117,7 +117,7 @@ domain=comun # specified interfaces (and the loopback) give the name of the # interface (eg eth0) here. # Repeat the line for more than one interface. -interface=comun +interface={{ vpn_name }} # Or you can specify which interface _not_ to listen on except-interface=eth0 # Or which to listen on by address (remember to include 127.0.0.1 if diff --git a/roles/firewall/templates/rules.v4.j2 b/roles/firewall/templates/rules.v4.j2 index 2a453e6..959def9 100644 --- a/roles/firewall/templates/rules.v4.j2 +++ b/roles/firewall/templates/rules.v4.j2 @@ -6,7 +6,7 @@ -A INPUT -m conntrack --ctstate INVALID -j DROP -A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT -A INPUT -i lo -j ACCEPT --A INPUT -i comun -j ACCEPT +-A INPUT -i {{ vpn_name }} -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p udp -m udp --dport 53 -j ACCEPT -A INPUT -p tcp -m tcp --dport 53 -j ACCEPT diff --git a/roles/knsupdate/tasks/templates/commands.j2 b/roles/knsupdate/tasks/templates/commands.j2 index 45438b5..032e270 100644 --- a/roles/knsupdate/tasks/templates/commands.j2 +++ b/roles/knsupdate/tasks/templates/commands.j2 @@ -1,14 +1,19 @@ {% 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 -add {{ vho }} a {{ host_ip }} -add *.{{ vho }} a {{ host_ip }} -add _acme-challenge.{{ vho }} a {{ host_ip }} -add _acme-challenge.{{ vho }} ns _acme-challenge +del {{ hostname }} a +del {{ hostname }} ns +add {{ hostname }} a {{ host_ip }} +{% if is_abyayala_subdomain %} +add *.{{ hostname }} a {{ host_ip }} +{% else %} +add {{ domain }} a {{ host_ip }} +add *.{{ domain }} a {{ host_ip }} +{% endif %} +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 }} diff --git a/roles/knsupdate/tasks/update.yml b/roles/knsupdate/tasks/update.yml index b3783f3..a0e43c7 100644 --- a/roles/knsupdate/tasks/update.yml +++ b/roles/knsupdate/tasks/update.yml @@ -1,7 +1,5 @@ - - set_fact: - vho: "{{ vhost.domains[0] | regex_replace('([a-z0-9]+)\\.abyaya\\.la', '\\1')}}" - - - 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 diff --git a/roles/knsupdate/tasks/update_domain.yml b/roles/knsupdate/tasks/update_domain.yml new file mode 100644 index 0000000..a16db51 --- /dev/null +++ b/roles/knsupdate/tasks/update_domain.yml @@ -0,0 +1,38 @@ + - set_fact: + is_abyayala_subdomain: "{{ domain.endswith('.' ~ main_zone) }}" + + - name: extract zone and hostname for main zone subdomains + set_fact: + zone: "{{ main_zone ~ '.' }}" + hostname: "{{ domain | regex_replace('([a-z0-9-]+)\\.' ~ main_zone|regex_escape , '\\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 + - debug: + msg: "{{ lookup('template', 'templates/commands.j2') }}" + + - name: knsupdate for this domain + shell: knsupdate + args: + stdin: "{{ lookup('template', 'templates/commands.j2') }}" 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 5397241..2e50195 100644 --- a/roles/proxy/tasks/main.yml +++ b/roles/proxy/tasks/main.yml @@ -11,12 +11,12 @@ tags: certbot - name: configuration paths - file: path={{ comun }} state=directory + file: path={{ abc }} state=directory with_items: - "{{ stream_path }}" - "{{ conf_path }}" loop_control: - loop_var: comun + loop_var: abc - name: virtual hosts path file: path={{ vhosts_path }} state=directory @@ -45,6 +45,21 @@ 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([]) }}" diff --git a/roles/proxy/templates/stream.conf b/roles/proxy/templates/stream.conf index a63eef6..9eb2e6c 100644 --- a/roles/proxy/templates/stream.conf +++ b/roles/proxy/templates/stream.conf @@ -5,7 +5,7 @@ upstream ssh_{{ vhost.nodo | replace(".", "") }} { server { listen {{ vhost.ports[0] }}; - server_name .{{ vhost.domains | join(' .') }}; + server_name {{ vhost.service_name }}.{{ main_zone }}; proxy_pass ssh_{{ vhost.nodo | replace(".", "") }}; } \ No newline at end of file diff --git a/roles/rap/tasks/client.yml b/roles/rap/tasks/client.yml index fed4dc0..dfb49e1 100644 --- a/roles/rap/tasks/client.yml +++ b/roles/rap/tasks/client.yml @@ -28,11 +28,11 @@ cmd: "./rap init -i {{ nodo }}" chdir: "{{ rap_path }}/rap" environment: - NETWORK: comun + NETWORK: "{{ vpn_name }}" - name: instalar el nodo shell: cmd: "./rap install -v {{ nodo }}" chdir: "{{ rap_path }}/rap" environment: - NETWORK: comun + NETWORK: "{{ vpn_name }}" diff --git a/roles/rap/tasks/main.yml b/roles/rap/tasks/main.yml index 80df79b..598c3af 100644 --- a/roles/rap/tasks/main.yml +++ b/roles/rap/tasks/main.yml @@ -24,9 +24,9 @@ cmd: "./rap add-host {{ althost }} {{ nod }}" chdir: "{{ rap_path }}" args: - creates: "{{ rap_path }}/networks/comun/abyayala/hosts/{{ nod }}" + creates: "{{ rap_path }}/networks/{{ vpn_name }}/abyayala/hosts/{{ nod }}" environment: - NETWORK: comun + NETWORK: "{{ vpn_name }}" with_items: "{{ item.nodos }}" loop_control: loop_var: nod @@ -36,4 +36,4 @@ cmd: "./rap install -v {{ althost }}" chdir: "{{ rap_path }}" environment: - NETWORK: comun + NETWORK: "{{ vpn_name }}"