Adding DNS role
This commit is contained in:
@@ -25,7 +25,12 @@
|
|||||||
"Bash(ansible-playbook:*)",
|
"Bash(ansible-playbook:*)",
|
||||||
"Bash(just test:*)",
|
"Bash(just test:*)",
|
||||||
"Bash(pip show:*)",
|
"Bash(pip show:*)",
|
||||||
"Bash(molecule test:*)"
|
"Bash(molecule test:*)",
|
||||||
|
"Bash(python3 -m molecule test -s default)",
|
||||||
|
"Bash(ansible-lint roles/dns/)",
|
||||||
|
"Bash(ansible-lint roles/graylog/)",
|
||||||
|
"Bash(ansible-lint .)",
|
||||||
|
"Bash(grep -v \"^WARNING\\\\|^$\\\\|^A new\\\\|^Upgrade\\\\|^Read\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,3 +136,23 @@ smtp_from: "noreply@{{ base_domain }}"
|
|||||||
smtp_user: "noreply@{{ base_domain }}"
|
smtp_user: "noreply@{{ base_domain }}"
|
||||||
smtp_password: "changeme_smtp"
|
smtp_password: "changeme_smtp"
|
||||||
smtp_tls: "starttls"
|
smtp_tls: "starttls"
|
||||||
|
|
||||||
|
# DNS / BIND9 — authoritative nameserver
|
||||||
|
bind_version: "9.18-22.04_beta"
|
||||||
|
# dns_server_ip must be the public IPv4 address of this server.
|
||||||
|
# Register ns1.{{ base_domain }} as a glue record at your domain registrar
|
||||||
|
# pointing to this IP, then set your domain's nameservers to ns1.{{ base_domain }}.
|
||||||
|
dns_server_ip: "changeme_server_public_ip"
|
||||||
|
dns_ns_hostname: "ns1.{{ base_domain }}"
|
||||||
|
dns_ttl: 3600
|
||||||
|
|
||||||
|
# DKIM — retrieve the public key from the Stalwart admin UI at
|
||||||
|
# mail.{{ base_domain }} → Settings → DKIM keys after first deployment.
|
||||||
|
# Leave empty to skip the DKIM TXT record until the key is available.
|
||||||
|
stalwart_dkim_selector: "default"
|
||||||
|
stalwart_dkim_public_key: "" # e.g. "MIGfMA0GCSqGSIb3DQEB..."
|
||||||
|
|
||||||
|
# DMARC — email authentication policy
|
||||||
|
dmarc_policy: "quarantine" # none | quarantine | reject
|
||||||
|
dmarc_rua: "mailto:dmarc-reports@{{ base_domain }}"
|
||||||
|
dmarc_ruf: "mailto:dmarc-forensics@{{ base_domain }}"
|
||||||
|
|||||||
@@ -139,3 +139,16 @@ forgejo_data_dir: /tmp/sovereign_test/forgejo
|
|||||||
# Website
|
# Website
|
||||||
website_nginx_version: "alpine"
|
website_nginx_version: "alpine"
|
||||||
website_data_dir: /tmp/sovereign_test/website
|
website_data_dir: /tmp/sovereign_test/website
|
||||||
|
|
||||||
|
# DNS / BIND9
|
||||||
|
bind_version: "9.18-22.04_beta"
|
||||||
|
dns_server_ip: "192.0.2.1"
|
||||||
|
dns_ns_hostname: "ns1.test.example.com"
|
||||||
|
dns_ttl: 3600
|
||||||
|
dkim_selector: "default"
|
||||||
|
stalwart_dkim_selector: "default"
|
||||||
|
stalwart_dkim_public_key: ""
|
||||||
|
dmarc_policy: "quarantine"
|
||||||
|
dmarc_rua: "mailto:dmarc-reports@test.example.com"
|
||||||
|
dmarc_ruf: "mailto:dmarc-forensics@test.example.com"
|
||||||
|
dns_data_dir: /tmp/sovereign_test/dns
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
roles:
|
roles:
|
||||||
- role: common
|
- role: common
|
||||||
tags: [common, traefik]
|
tags: [common, traefik]
|
||||||
|
- role: dns
|
||||||
|
tags: [dns, nameserver]
|
||||||
- role: graylog
|
- role: graylog
|
||||||
tags: [graylog, logging]
|
tags: [graylog, logging]
|
||||||
- role: authentik
|
- role: authentik
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
dns_data_dir: "{{ sovereign_base_dir }}/dns"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: Restart BIND9
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: "{{ dns_data_dir }}"
|
||||||
|
state: present
|
||||||
|
recreate: always
|
||||||
|
when: not (molecule_test_mode | default(false))
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
- name: Converge
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
vars_files:
|
||||||
|
- ../../../molecule/shared/vars.yml
|
||||||
|
roles:
|
||||||
|
- role: dns
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: galaxy
|
||||||
|
options:
|
||||||
|
requirements-file: requirements.yml
|
||||||
|
driver:
|
||||||
|
name: default
|
||||||
|
platforms:
|
||||||
|
- name: localhost
|
||||||
|
groups:
|
||||||
|
- sovereign
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
env:
|
||||||
|
ANSIBLE_ROLES_PATH: "${MOLECULE_PROJECT_DIRECTORY}/.."
|
||||||
|
inventory:
|
||||||
|
host_vars:
|
||||||
|
localhost:
|
||||||
|
ansible_connection: local
|
||||||
|
verifier:
|
||||||
|
name: ansible
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
---
|
||||||
|
- name: Verify dns role
|
||||||
|
hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
dns_data_dir: /tmp/sovereign_test/dns
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Check dns data directory exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns
|
||||||
|
register: data_dir_stat
|
||||||
|
|
||||||
|
- name: Assert dns data directory is present
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: data_dir_stat.stat.isdir
|
||||||
|
fail_msg: "Data directory /tmp/sovereign_test/dns was not created"
|
||||||
|
|
||||||
|
- name: Check dns config directory exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns/config
|
||||||
|
register: config_dir_stat
|
||||||
|
|
||||||
|
- name: Assert dns config directory is present
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: config_dir_stat.stat.isdir
|
||||||
|
fail_msg: "Directory /tmp/sovereign_test/dns/config was not created"
|
||||||
|
|
||||||
|
- name: Check dns zones directory exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns/zones
|
||||||
|
register: zones_dir_stat
|
||||||
|
|
||||||
|
- name: Assert dns zones directory is present
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: zones_dir_stat.stat.isdir
|
||||||
|
fail_msg: "Directory /tmp/sovereign_test/dns/zones was not created"
|
||||||
|
|
||||||
|
- name: Check dns cache directory exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns/cache
|
||||||
|
register: cache_dir_stat
|
||||||
|
|
||||||
|
- name: Assert dns cache directory is present
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: cache_dir_stat.stat.isdir
|
||||||
|
fail_msg: "Directory /tmp/sovereign_test/dns/cache was not created"
|
||||||
|
|
||||||
|
- name: Check named.conf exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns/config/named.conf
|
||||||
|
register: named_conf_stat
|
||||||
|
|
||||||
|
- name: Assert named.conf was rendered
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: named_conf_stat.stat.exists
|
||||||
|
fail_msg: "named.conf was not rendered"
|
||||||
|
|
||||||
|
- name: Read named.conf
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /tmp/sovereign_test/dns/config/named.conf
|
||||||
|
register: named_conf_raw
|
||||||
|
|
||||||
|
- name: Set named.conf content fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
named_conf: "{{ named_conf_raw.content | b64decode }}"
|
||||||
|
|
||||||
|
- name: Assert zone declaration for base domain in named.conf
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'zone \"test.example.com\"' in named_conf"
|
||||||
|
fail_msg: "Expected zone declaration for test.example.com not found in named.conf"
|
||||||
|
|
||||||
|
- name: Assert recursion disabled in named.conf
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'recursion no' in named_conf"
|
||||||
|
fail_msg: "Expected 'recursion no' not found in named.conf"
|
||||||
|
|
||||||
|
- name: Check zone file exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns/zones/test.example.com.zone
|
||||||
|
register: zone_stat
|
||||||
|
|
||||||
|
- name: Assert zone file was rendered
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: zone_stat.stat.exists
|
||||||
|
fail_msg: "Zone file test.example.com.zone was not rendered"
|
||||||
|
|
||||||
|
- name: Read zone file
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /tmp/sovereign_test/dns/zones/test.example.com.zone
|
||||||
|
register: zone_raw
|
||||||
|
|
||||||
|
- name: Set zone content fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
zone: "{{ zone_raw.content | b64decode }}"
|
||||||
|
|
||||||
|
- name: Assert SOA record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'SOA' in zone"
|
||||||
|
fail_msg: "SOA record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert NS record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'IN NS' in zone"
|
||||||
|
fail_msg: "NS record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert server IP in zone for ns1 A record
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'192.0.2.1' in zone"
|
||||||
|
fail_msg: "Expected dns_server_ip 192.0.2.1 not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert mail A record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'mail IN A' in zone"
|
||||||
|
fail_msg: "mail A record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert MX record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'IN MX' in zone"
|
||||||
|
fail_msg: "MX record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert SPF TXT record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'v=spf1 mx ~all' in zone"
|
||||||
|
fail_msg: "SPF record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert DMARC TXT record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'v=DMARC1' in zone"
|
||||||
|
fail_msg: "DMARC record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert DMARC policy in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'p=quarantine' in zone"
|
||||||
|
fail_msg: "Expected DMARC policy 'quarantine' not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert DMARC rua address in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'mailto:dmarc-reports@test.example.com' in zone"
|
||||||
|
fail_msg: "Expected DMARC rua address not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert auth A record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'auth IN A' in zone"
|
||||||
|
fail_msg: "auth A record not found in zone file"
|
||||||
|
|
||||||
|
- name: Assert git A record in zone
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'git IN A' in zone"
|
||||||
|
fail_msg: "git A record not found in zone file"
|
||||||
|
|
||||||
|
- name: Check docker-compose.yml exists
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /tmp/sovereign_test/dns/docker-compose.yml
|
||||||
|
register: compose_stat
|
||||||
|
|
||||||
|
- name: Assert docker-compose.yml was rendered
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: compose_stat.stat.exists
|
||||||
|
fail_msg: "docker-compose.yml was not rendered for dns"
|
||||||
|
|
||||||
|
- name: Read docker-compose.yml
|
||||||
|
ansible.builtin.slurp:
|
||||||
|
src: /tmp/sovereign_test/dns/docker-compose.yml
|
||||||
|
register: compose_raw
|
||||||
|
|
||||||
|
- name: Set compose content fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
compose: "{{ compose_raw.content | b64decode }}"
|
||||||
|
|
||||||
|
- name: Assert bind9 image in compose
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'ubuntu/bind9:9.18-22.04_beta' in compose"
|
||||||
|
fail_msg: "Expected bind9 image not found in docker-compose.yml"
|
||||||
|
|
||||||
|
- name: Assert port 53 TCP in compose
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'53:53/tcp' in compose"
|
||||||
|
fail_msg: "Expected port 53/tcp mapping not found in docker-compose.yml"
|
||||||
|
|
||||||
|
- name: Assert port 53 UDP in compose
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'53:53/udp' in compose"
|
||||||
|
fail_msg: "Expected port 53/udp mapping not found in docker-compose.yml"
|
||||||
|
|
||||||
|
- name: Assert GELF logging address in compose
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'udp://127.0.0.1:12201' in compose"
|
||||||
|
fail_msg: "Expected GELF address udp://127.0.0.1:12201 not found in docker-compose.yml"
|
||||||
|
|
||||||
|
- name: Assert sovereign network is external in compose
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: "'external: true' in compose"
|
||||||
|
fail_msg: "Expected 'external: true' not found in docker-compose.yml"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
- name: Create BIND9 directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
loop:
|
||||||
|
- "{{ dns_data_dir }}"
|
||||||
|
- "{{ dns_data_dir }}/config"
|
||||||
|
- "{{ dns_data_dir }}/zones"
|
||||||
|
- "{{ dns_data_dir }}/cache"
|
||||||
|
|
||||||
|
- name: Set DNS zone serial from current timestamp
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
dns_zone_serial: "{{ lookup('pipe', 'date +%Y%m%d%H') | int }}"
|
||||||
|
|
||||||
|
- name: Deploy named.conf
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: named.conf.j2
|
||||||
|
dest: "{{ dns_data_dir }}/config/named.conf"
|
||||||
|
mode: '0644'
|
||||||
|
notify: Restart BIND9
|
||||||
|
|
||||||
|
- name: Deploy zone file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: zone.j2
|
||||||
|
dest: "{{ dns_data_dir }}/zones/{{ base_domain }}.zone"
|
||||||
|
mode: '0644'
|
||||||
|
notify: Restart BIND9
|
||||||
|
|
||||||
|
- name: Deploy BIND9 docker-compose
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: docker-compose.yml.j2
|
||||||
|
dest: "{{ dns_data_dir }}/docker-compose.yml"
|
||||||
|
mode: '0644'
|
||||||
|
notify: Restart BIND9
|
||||||
|
|
||||||
|
- name: Start BIND9
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: "{{ dns_data_dir }}"
|
||||||
|
state: present
|
||||||
|
when: not (molecule_test_mode | default(false))
|
||||||
|
|
||||||
|
- name: Wait for BIND9 to be ready
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 53
|
||||||
|
timeout: 30
|
||||||
|
when: not (molecule_test_mode | default(false))
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
services:
|
||||||
|
bind9:
|
||||||
|
image: ubuntu/bind9:{{ bind_version }}
|
||||||
|
container_name: bind9
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
TZ: UTC
|
||||||
|
ports:
|
||||||
|
- "53:53/tcp"
|
||||||
|
- "53:53/udp"
|
||||||
|
volumes:
|
||||||
|
- {{ dns_data_dir }}/config/named.conf:/etc/bind/named.conf:ro
|
||||||
|
- {{ dns_data_dir }}/zones:/var/lib/bind:ro
|
||||||
|
- {{ dns_data_dir }}/cache:/var/cache/bind
|
||||||
|
networks:
|
||||||
|
- {{ sovereign_network_name }}
|
||||||
|
logging:
|
||||||
|
driver: gelf
|
||||||
|
options:
|
||||||
|
gelf-address: "udp://{{ graylog_host }}:{{ graylog_gelf_port }}"
|
||||||
|
tag: "bind9"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
{{ sovereign_network_name }}:
|
||||||
|
external: true
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// named.conf — authoritative-only configuration for {{ base_domain }}
|
||||||
|
// Managed by Ansible — do not edit manually.
|
||||||
|
|
||||||
|
options {
|
||||||
|
directory "/var/cache/bind";
|
||||||
|
|
||||||
|
// Authoritative only — no recursion to prevent DNS amplification attacks
|
||||||
|
recursion no;
|
||||||
|
allow-recursion { none; };
|
||||||
|
|
||||||
|
// Accept queries from any source
|
||||||
|
allow-query { any; };
|
||||||
|
|
||||||
|
// Only allow zone transfers to trusted hosts (none by default)
|
||||||
|
allow-transfer { none; };
|
||||||
|
|
||||||
|
// Listen on all interfaces
|
||||||
|
listen-on { any; };
|
||||||
|
listen-on-v6 { any; };
|
||||||
|
|
||||||
|
dnssec-validation no;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Authoritative zone for the base domain
|
||||||
|
zone "{{ base_domain }}" IN {
|
||||||
|
type master;
|
||||||
|
file "/var/lib/bind/{{ base_domain }}.zone";
|
||||||
|
allow-update { none; };
|
||||||
|
};
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
; Zone file for {{ base_domain }}
|
||||||
|
; Managed by Ansible — do not edit manually.
|
||||||
|
; Serial: {{ dns_zone_serial }} (YYYYMMDDHH — increment on manual edits)
|
||||||
|
|
||||||
|
$ORIGIN {{ base_domain }}.
|
||||||
|
$TTL {{ dns_ttl | default(3600) }}
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; SOA record
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
@ IN SOA {{ dns_ns_hostname | default('ns1.' + base_domain) }}. hostmaster.{{ base_domain }}. (
|
||||||
|
{{ dns_zone_serial }} ; Serial
|
||||||
|
3600 ; Refresh (1 hour)
|
||||||
|
900 ; Retry (15 min)
|
||||||
|
604800 ; Expire (7 days)
|
||||||
|
300 ; Negative TTL (5 min)
|
||||||
|
)
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; Name servers
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
@ IN NS {{ dns_ns_hostname | default('ns1.' + base_domain) }}.
|
||||||
|
ns1 IN A {{ dns_server_ip }}
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; Root domain
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
@ IN A {{ dns_server_ip }}
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; Service A records
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
traefik IN A {{ dns_server_ip }}
|
||||||
|
logs IN A {{ dns_server_ip }}
|
||||||
|
auth IN A {{ dns_server_ip }}
|
||||||
|
s3 IN A {{ dns_server_ip }}
|
||||||
|
minio IN A {{ dns_server_ip }}
|
||||||
|
cloud IN A {{ dns_server_ip }}
|
||||||
|
mail IN A {{ dns_server_ip }}
|
||||||
|
webmail IN A {{ dns_server_ip }}
|
||||||
|
matrix IN A {{ dns_server_ip }}
|
||||||
|
chat IN A {{ dns_server_ip }}
|
||||||
|
meet IN A {{ dns_server_ip }}
|
||||||
|
headscale IN A {{ dns_server_ip }}
|
||||||
|
wazuh IN A {{ dns_server_ip }}
|
||||||
|
vault IN A {{ dns_server_ip }}
|
||||||
|
git IN A {{ dns_server_ip }}
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; Mail exchange
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
@ IN MX 10 mail.{{ base_domain }}.
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; SPF — authorise the mail server to send on behalf of the domain
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
@ IN TXT "v=spf1 mx ~all"
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; DMARC — policy: {{ dmarc_policy | default('quarantine') }}
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
_dmarc IN TXT "v=DMARC1; p={{ dmarc_policy | default('quarantine') }}; rua={{ dmarc_rua | default('mailto:dmarc-reports@' + base_domain) }}; ruf={{ dmarc_ruf | default('mailto:dmarc-forensics@' + base_domain) }}; fo=1; adkim=r; aspf=r"
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; DKIM — public key from the Stalwart mail server
|
||||||
|
; Set stalwart_dkim_public_key in group_vars/all.yml (retrieve from the
|
||||||
|
; Stalwart admin UI at mail.{{ base_domain }} → Settings → DKIM keys).
|
||||||
|
; RSA-2048 keys exceed the 255-byte TXT string limit so they are split into
|
||||||
|
; multiple quoted strings, which compliant resolvers concatenate.
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
{% if stalwart_dkim_public_key | default('') != '' %}
|
||||||
|
{% set dkim_full = "v=DKIM1; k=rsa; p=" + stalwart_dkim_public_key %}
|
||||||
|
{% set dkim_chunks = dkim_full | regex_findall('.{1,255}') %}
|
||||||
|
{{ stalwart_dkim_selector | default('default') }}._domainkey IN TXT ( {% for chunk in dkim_chunks %}"{{ chunk }}" {% endfor %})
|
||||||
|
{% else %}
|
||||||
|
; DKIM record not configured — set stalwart_dkim_public_key to enable.
|
||||||
|
{% endif %}
|
||||||
@@ -110,6 +110,17 @@ forgejo_admin_password: "test_forgejo_admin"
|
|||||||
forgejo_admin_email: "admin@test.example.com"
|
forgejo_admin_email: "admin@test.example.com"
|
||||||
forgejo_ssh_port: 2222
|
forgejo_ssh_port: 2222
|
||||||
|
|
||||||
|
# DNS / BIND9
|
||||||
|
bind_version: "9.18-22.04_beta"
|
||||||
|
dns_server_ip: "192.0.2.1"
|
||||||
|
dns_ns_hostname: "ns1.test.example.com"
|
||||||
|
dns_ttl: 3600
|
||||||
|
stalwart_dkim_selector: "default"
|
||||||
|
stalwart_dkim_public_key: ""
|
||||||
|
dmarc_policy: "quarantine"
|
||||||
|
dmarc_rua: "mailto:dmarc-reports@test.example.com"
|
||||||
|
dmarc_ruf: "mailto:dmarc-forensics@test.example.com"
|
||||||
|
|
||||||
# Website
|
# Website
|
||||||
website_nginx_version: "alpine"
|
website_nginx_version: "alpine"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user