homelab/orchestration/ansible/roles/technitium-dns-secondary/tasks/main.yml
Dan V deb6c38d7b chore: commit homelab setup — deployment, services, orchestration, skill
- Add .gitignore: exclude compiled binaries, build artifacts, and Helm
  values files containing real secrets (authentik, prometheus)
- Add all Kubernetes deployment manifests (deployment/)
- Add services source code: ha-sync, device-inventory, games-console,
  paperclip, parts-inventory
- Add Ansible orchestration: playbooks, roles, inventory, cloud-init
- Add hardware specs, execution plans, scripts, HOMELAB.md
- Add skills/homelab/SKILL.md + skills/install.sh to preserve Copilot skill
- Remove previously-tracked inventory-cli binary from git index

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 08:10:32 +02:00

240 lines
8.1 KiB
YAML

---
# ── Docker ────────────────────────────────────────────────────────────────────
- name: Install Docker prerequisites
ansible.builtin.apt:
name:
- ca-certificates
- curl
- gnupg
state: present
update_cache: true
- name: Add Docker GPG key
ansible.builtin.shell:
cmd: |
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
creates: /etc/apt/keyrings/docker.gpg
- name: Add Docker apt repository
ansible.builtin.apt_repository:
repo: >-
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg]
https://download.docker.com/linux/{{ ansible_distribution | lower }}
{{ ansible_distribution_release }} stable
state: present
filename: docker
- name: Install Docker CE
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
update_cache: true
- name: Ensure Docker service is enabled and running
ansible.builtin.systemd:
name: docker
enabled: true
state: started
# ── systemd-resolved: free up port 53 ────────────────────────────────────────
- name: Check if systemd-resolved is active
ansible.builtin.systemd:
name: systemd-resolved
register: resolved_status
failed_when: false
- name: Disable systemd-resolved stub listener on port 53
ansible.builtin.lineinfile:
path: /etc/systemd/resolved.conf
regexp: '^#?DNSStubListener='
line: 'DNSStubListener=no'
state: present
when: resolved_status.status.ActiveState | default('') == 'active'
notify: Restart systemd-resolved
- name: Flush handlers to restart resolved before checking resolv.conf
ansible.builtin.meta: flush_handlers
- name: Check that non-stub resolv.conf exists and has nameservers
ansible.builtin.shell:
cmd: grep -q '^nameserver' /run/systemd/resolve/resolv.conf
register: resolv_check
failed_when: false
changed_when: false
when: resolved_status.status.ActiveState | default('') == 'active'
- name: Point /etc/resolv.conf to non-stub systemd-resolved config
ansible.builtin.file:
src: /run/systemd/resolve/resolv.conf
dest: /etc/resolv.conf
state: link
force: true
when:
- resolved_status.status.ActiveState | default('') == 'active'
- resolv_check.rc | default(1) == 0
- name: Warn if non-stub resolv.conf is missing or empty
ansible.builtin.debug:
msg: >-
WARNING: /run/systemd/resolve/resolv.conf not found or has no nameservers.
Skipping resolv.conf symlink — DNS resolution is unchanged.
Verify /etc/resolv.conf manually before running Technitium.
when: >-
resolved_status.status.ActiveState | default('') != 'active'
or resolv_check.rc | default(1) != 0
# ── Disable libvirt default network (frees 192.168.122.1:53) ─────────────────
- name: Check if libvirt daemon is running
ansible.builtin.systemd:
name: libvirtd
register: libvirtd_status
failed_when: false
- name: Destroy libvirt default network if active
ansible.builtin.command: virsh net-destroy default
register: net_destroy
failed_when:
- net_destroy.rc != 0
- "'not active' not in net_destroy.stderr"
- "'not found' not in net_destroy.stderr"
- "'Connection refused' not in net_destroy.stderr"
changed_when: net_destroy.rc == 0
when: libvirtd_status.status.ActiveState | default('') == 'active'
- name: Disable libvirt default network autostart
ansible.builtin.command: virsh net-autostart default --disable
register: net_autostart
failed_when:
- net_autostart.rc != 0
- "'not found' not in net_autostart.stderr"
- "'Connection refused' not in net_autostart.stderr"
changed_when: net_autostart.rc == 0
when: libvirtd_status.status.ActiveState | default('') == 'active'
# ── Pre-pull Docker image (requires DNS — do this before touching resolved) ───
- name: Pre-pull Technitium Docker image
ansible.builtin.command:
cmd: docker pull {{ technitium_image }}
register: docker_pull
changed_when: "'Pull complete' in docker_pull.stdout or 'Downloaded' in docker_pull.stdout"
failed_when: docker_pull.rc != 0
# ── Technitium container ──────────────────────────────────────────────────────
- name: Ensure Technitium data directory exists
ansible.builtin.file:
path: "{{ technitium_data_dir }}"
state: directory
mode: "0755"
- name: Install Technitium admin password environment file
ansible.builtin.copy:
dest: /etc/technitium-dns.env
mode: "0600"
content: |
DNS_SERVER_ADMIN_PASSWORD={{ technitium_admin_password }}
notify: Restart Technitium DNS
- name: Install Technitium DNS systemd service
ansible.builtin.copy:
dest: /etc/systemd/system/technitium-dns.service
mode: "0644"
content: |
[Unit]
Description=Technitium DNS Server (secondary)
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target
[Service]
Restart=always
RestartSec=5
TimeoutStopSec=30
EnvironmentFile=/etc/technitium-dns.env
ExecStartPre=-/usr/bin/docker stop technitium-dns
ExecStartPre=-/usr/bin/docker rm technitium-dns
ExecStart=/usr/bin/docker run --rm --name technitium-dns \
-p 53:53/udp \
-p 53:53/tcp \
-p {{ technitium_web_port }}:5380/tcp \
-v {{ technitium_data_dir }}:/etc/dns \
--env-file /etc/technitium-dns.env \
{{ technitium_image }}
ExecStop=/usr/bin/docker stop technitium-dns
[Install]
WantedBy=multi-user.target
notify: Restart Technitium DNS
- name: Flush handlers to (re)start Technitium before configuring
ansible.builtin.meta: flush_handlers
- name: Ensure Technitium DNS service is enabled and running
ansible.builtin.systemd:
name: technitium-dns
daemon_reload: true
enabled: true
state: started
- name: Wait for Technitium web UI to be available
ansible.builtin.uri:
url: "http://localhost:{{ technitium_web_port }}/"
status_code: 200
register: technitium_ui_check
retries: 20
delay: 5
until: technitium_ui_check.status == 200
# ── Technitium API configuration ──────────────────────────────────────────────
- name: Authenticate with Technitium API
ansible.builtin.uri:
url: "http://localhost:{{ technitium_web_port }}/api/user/login"
method: GET
body_format: form-urlencoded
body:
user: "{{ technitium_admin_user }}"
pass: "{{ technitium_admin_password }}"
register: technitium_auth
failed_when: technitium_auth.json.status != 'ok'
- name: Set Technitium API token fact
ansible.builtin.set_fact:
technitium_token: "{{ technitium_auth.json.token }}"
- name: Create .{{ technitium_domain }} secondary zone (pulls from primary)
ansible.builtin.uri:
url: "http://localhost:{{ technitium_web_port }}/api/zones/create"
method: POST
body_format: form-urlencoded
body:
token: "{{ technitium_token }}"
zone: "{{ technitium_domain }}"
type: Secondary
primaryNameServerAddresses: "{{ technitium_primary_ip }}"
register: zone_create
failed_when:
- zone_create.json.status == 'error'
- "'Zone already exists' not in (zone_create.json.errorMessage | default(''))"
changed_when: (zone_create.json.status | default('')) == 'ok'
- name: Trigger initial zone transfer from primary
ansible.builtin.uri:
url: "http://localhost:{{ technitium_web_port }}/api/zones/resync"
method: POST
body_format: form-urlencoded
body:
token: "{{ technitium_token }}"
zone: "{{ technitium_domain }}"
ignore_errors: true
changed_when: false