mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-20 01:06:03 +02:00
DNS & certs refactor:
- Switch certbot flag from MODE_TEST → MODE_DUMMY in dedicated certs - Add sys-svc-dns defaults for CLOUDFLARE_NAMESERVERS - Introduce 02_nameservers.yml for NS cleanup + enforce, adjust task ordering (apex now 03_apex.yml) - Enforce quoting for Bluesky and Mailu TXT records - Add cleanup of MX/TXT/DMARC/DKIM in Mailu role - Normalize no_log handling in Nextcloud plugin - Simplify async conditionals in Collabora role Conversation: https://chatgpt.com/share/68cd20d8-9ba8-800f-b070-f7294f072c40
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
{% else %}
|
||||
-d {{ domain }}
|
||||
{% endif %}
|
||||
{{ '--test-cert' if MODE_TEST | bool else '' }}
|
||||
{{ '--test-cert' if MODE_DUMMY | bool else '' }}
|
||||
register: certbot_result
|
||||
changed_when: "'Certificate not yet due for renewal' not in certbot_result.stdout"
|
||||
when: not cert_check.exists
|
1
roles/sys-svc-dns/defaults/main.yml
Normal file
1
roles/sys-svc-dns/defaults/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
CLOUDFLARE_NAMESERVERS: [] # Cloudflare Nameservers for NS records
|
@@ -5,8 +5,15 @@
|
||||
The variable "CLOUDFLARE_API_TOKEN" must be defined and cannot be empty!
|
||||
when: (CLOUDFLARE_API_TOKEN | default('') | trim) == ''
|
||||
|
||||
- name: "Setup Nameservers"
|
||||
include_tasks: 02_nameservers.yml
|
||||
loop: "{{ SYS_SVC_DNS_BASE_DOMAINS | list }}"
|
||||
loop_control:
|
||||
loop_var: base_domain
|
||||
label: "{{ base_domain }}"
|
||||
|
||||
- name: "Apply apex A/AAAA for base domains"
|
||||
include_tasks: 02_apex.yml
|
||||
include_tasks: 03_apex.yml
|
||||
loop: "{{ SYS_SVC_DNS_BASE_DOMAINS | list }}"
|
||||
loop_control:
|
||||
loop_var: base_domain
|
||||
|
60
roles/sys-svc-dns/tasks/02_nameservers.yml
Normal file
60
roles/sys-svc-dns/tasks/02_nameservers.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
# Ensure CLOUDFLARE_NAMESERVERS is provided
|
||||
- name: "Assert CLOUDFLARE_NAMESERVERS is not empty"
|
||||
ansible.builtin.fail:
|
||||
msg: >
|
||||
CLOUDFLARE_NAMESERVERS must be a non-empty list of nameserver hostnames,
|
||||
e.g. ['bob.ns.cloudflare.com', 'dina.ns.cloudflare.com'].
|
||||
when: (CLOUDFLARE_NAMESERVERS | length) == 0
|
||||
|
||||
- block:
|
||||
# Gather current NS records for this base domain
|
||||
- name: "NS | Fetch existing NS records for {{ base_domain }}"
|
||||
community.general.cloudflare_dns_info:
|
||||
api_token: "{{ CLOUDFLARE_API_TOKEN }}"
|
||||
zone: "{{ base_domain }}"
|
||||
register: _cf_ns_info
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
# Build a list with ONLY the NS records (Cloudflare returns a mixed list)
|
||||
- name: "NS | Build deletion list (all NS at apex and subdomains)"
|
||||
set_fact:
|
||||
_cf_ns_to_delete: >-
|
||||
{{
|
||||
_cf_ns_info.records
|
||||
| selectattr('type', 'equalto', 'NS')
|
||||
| list
|
||||
}}
|
||||
|
||||
# Delete all existing NS records (exact matches)
|
||||
- name: "NS | Delete existing NS records"
|
||||
community.general.cloudflare_dns:
|
||||
api_token: "{{ CLOUDFLARE_API_TOKEN }}"
|
||||
zone: "{{ base_domain }}"
|
||||
type: NS
|
||||
name: "{{ item.name }}"
|
||||
value: "{{ item.value | default(item.content) }}"
|
||||
state: absent
|
||||
ttl: 1
|
||||
loop: "{{ _cf_ns_to_delete }}"
|
||||
loop_control:
|
||||
label: "NS {{ item.name }} -> {{ item.value | default(item.content) }}"
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
when: MODE_CLEANUP | bool
|
||||
|
||||
# Create enforced NS set at the zone apex (@)
|
||||
- name: "NS | Create NS apex set for {{ base_domain }}"
|
||||
community.general.cloudflare_dns:
|
||||
api_token: "{{ CLOUDFLARE_API_TOKEN }}"
|
||||
zone: "{{ base_domain }}"
|
||||
type: NS
|
||||
name: "@"
|
||||
value: "{{ item }}"
|
||||
ttl: 1
|
||||
state: present
|
||||
loop: "{{ CLOUDFLARE_NAMESERVERS }}"
|
||||
loop_control:
|
||||
label: "@ -> {{ item }}"
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
@@ -39,7 +39,7 @@
|
||||
- type: TXT
|
||||
zone: "{{ PRIMARY_DOMAIN | to_zone }}"
|
||||
name: "_atproto.{{ PRIMARY_DOMAIN }}"
|
||||
value: "did=did:web:{{ BLUESKY_API_DOMAIN }}"
|
||||
value: '"did=did:web:{{ BLUESKY_API_DOMAIN }}"'
|
||||
|
||||
# 3) Web UI host (only if enabled)
|
||||
- type: A
|
||||
|
@@ -1,20 +1,56 @@
|
||||
- block:
|
||||
- name: "CLEANUP | Gather existing apex TXT/MX (and DMARC/DKIM) records"
|
||||
community.general.cloudflare_dns_info:
|
||||
api_token: "{{ CLOUDFLARE_API_TOKEN }}"
|
||||
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||
register: cf_info
|
||||
|
||||
- name: "CLEANUP | Build deletion list"
|
||||
set_fact:
|
||||
cf_records_to_delete: >-
|
||||
{{
|
||||
cf_info.records
|
||||
| selectattr('type','in',['MX','TXT'])
|
||||
| selectattr('name','in', [
|
||||
MAILU_DOMAIN,
|
||||
'_dmarc.' ~ MAILU_DOMAIN_DNS_ZONE,
|
||||
'dkim._domainkey.' ~ MAILU_DOMAIN_DNS_ZONE
|
||||
])
|
||||
| list
|
||||
}}
|
||||
|
||||
- name: "CLEANUP | Remove matched records (exact Value match)"
|
||||
community.general.cloudflare_dns:
|
||||
api_token: "{{ CLOUDFLARE_API_TOKEN }}"
|
||||
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||
type: "{{ item.type }}"
|
||||
name: "{{ item.name }}"
|
||||
value: "{{ item.value | default(item.content) }}"
|
||||
state: absent
|
||||
loop: "{{ cf_records_to_delete }}"
|
||||
loop_control:
|
||||
label: "{{ item.type }} {{ item.name }} -> {{ item.value | default(item.content) }}"
|
||||
when:
|
||||
- DNS_PROVIDER | lower == 'cloudflare'
|
||||
- MODE_CLEANUP | bool
|
||||
|
||||
- name: "DNS (Cloudflare) for Mailu"
|
||||
include_role:
|
||||
name: sys-dns-cloudflare-records
|
||||
when: DNS_PROVIDER | lower == 'cloudflare'
|
||||
vars:
|
||||
cloudflare_async_enabled: "{{ ASYNC_ENABLED | default(false) | bool }}"
|
||||
cloudflare_async_time: "{{ ASYNC_TIME | default(45) }}"
|
||||
cloudflare_async_poll: "{{ ASYNC_POLL | default(5) }}"
|
||||
cloudflare_no_log: "{{ MASK_CREDENTIALS_IN_LOGS | default(true) | bool }}"
|
||||
cloudflare_async_enabled: "{{ ASYNC_ENABLED | bool }}"
|
||||
cloudflare_async_time: "{{ ASYNC_TIME }}"
|
||||
cloudflare_async_poll: "{{ ASYNC_POLL }}"
|
||||
cloudflare_no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
cloudflare_records:
|
||||
- { type: A, zone: "{{ MAILU_HOSTNAME_DNS_ZONE }}", name: "{{ MAILU_HOSTNAME }}", content: "{{ MAILU_IP4_PUBLIC }}", proxied: false }
|
||||
# - { type: AAAA, zone: "{{ MAILU_HOSTNAME_DNS_ZONE }}", name: "{{ MAILU_HOSTNAME }}", content: "{{ MAILU_IP6_PUBLIC }}", proxied: false }
|
||||
- { type: CNAME, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "autoconfig.{{ MAILU_DOMAIN_DNS_ZONE }}", value: "{{ MAILU_HOSTNAME }}" }
|
||||
- { type: MX, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "{{ MAILU_DOMAIN }}", value: "{{ MAILU_HOSTNAME }}", priority: 10 }
|
||||
- { type: TXT, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "{{ MAILU_DOMAIN }}", value: "v=spf1 mx a:{{ MAILU_HOSTNAME }} ~all" }
|
||||
- { type: TXT, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "_dmarc.{{ MAILU_DOMAIN_DNS_ZONE }}", value: "v=DMARC1; p=reject; ruf=mailto:{{ MAILU_DMARC_RUF }}; adkim=s; aspf=s" }
|
||||
- { type: TXT, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "dkim._domainkey.{{ MAILU_DOMAIN_DNS_ZONE }}", value: "{{ mailu_dkim_public_key }}" }
|
||||
- { type: TXT, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "{{ MAILU_DOMAIN }}", value: '"v=spf1 mx a:{{ MAILU_HOSTNAME }} ~all"' }
|
||||
- { type: TXT, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "_dmarc.{{ MAILU_DOMAIN_DNS_ZONE }}", value: '"v=DMARC1; p=reject; ruf=mailto:{{ MAILU_DMARC_RUF }}; adkim=s; aspf=s"' }
|
||||
- { type: TXT, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", name: "dkim._domainkey.{{ MAILU_DOMAIN_DNS_ZONE }}", value: '"{{ mailu_dkim_public_key }}"' }
|
||||
- { type: SRV, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", service: "_submission", proto: "_tcp",name: "{{ MAILU_DOMAIN }}", priority: 20, weight: 1, port: 587, value: "{{ MAILU_HOSTNAME }}" }
|
||||
- { type: SRV, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", service: "_submissions", proto: "_tcp",name: "{{ MAILU_DOMAIN }}", priority: 20, weight: 1, port: 465, value: "{{ MAILU_HOSTNAME }}" }
|
||||
- { type: SRV, zone: "{{ MAILU_DOMAIN_DNS_ZONE }}", service: "_imaps", proto: "_tcp", name: "{{ MAILU_DOMAIN }}", priority: 20, weight: 1, port: 993, value: "{{ MAILU_HOSTNAME }}" }
|
||||
|
@@ -34,7 +34,7 @@
|
||||
failed_when: not ASYNC_ENABLED and config_set_shell.rc != 0
|
||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | default(true) | bool }}"
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
- name: Check if {{ plugin_task_path }} exists
|
||||
stat:
|
||||
|
@@ -18,8 +18,8 @@
|
||||
(not ASYNC_ENABLED | bool )
|
||||
and
|
||||
('updated' in (collabora_fonts.stdout | default('')))
|
||||
async: "{{ ASYNC_TIME if (ASYNC_ENABLED | default(false) | bool) else omit }}"
|
||||
poll: "{{ ASYNC_POLL if (ASYNC_ENABLED | default(false) | bool) else omit }}"
|
||||
async: "{{ ASYNC_TIME if (ASYNC_ENABLED | bool) else omit }}"
|
||||
poll: "{{ ASYNC_POLL if (ASYNC_ENABLED | bool) else omit }}"
|
||||
when: MODE_UPDATE | bool
|
||||
|
||||
- name: Allow Nextcloud host IP for Collabora preview conversion
|
||||
|
Reference in New Issue
Block a user