- 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>
245 lines
8.2 KiB
YAML
245 lines
8.2 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
|
|
|
|
# ── Pre-pull Docker image (before touching DNS) ───────────────────────────────
|
|
|
|
- name: Pre-pull Technitium Docker image (requires DNS — do this first)
|
|
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 (primary)
|
|
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 upstream forwarder zone for "." (forwards unknown queries)
|
|
ansible.builtin.uri:
|
|
url: "http://localhost:{{ technitium_web_port }}/api/zones/create"
|
|
method: POST
|
|
body_format: form-urlencoded
|
|
body:
|
|
token: "{{ technitium_token }}"
|
|
zone: "."
|
|
type: Forwarder
|
|
forwarder: "{{ technitium_forwarders[0] }}"
|
|
dnssecValidation: "false"
|
|
register: forwarder_zone
|
|
failed_when:
|
|
- forwarder_zone.json.status == 'error'
|
|
- "'Zone already exists' not in (forwarder_zone.json.errorMessage | default(''))"
|
|
changed_when: (forwarder_zone.json.status | default('')) == 'ok'
|
|
|
|
- name: Create .{{ technitium_domain }} primary zone
|
|
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: Primary
|
|
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: Configure zone transfer to secondary servers
|
|
ansible.builtin.uri:
|
|
url: "http://localhost:{{ technitium_web_port }}/api/zones/options/set"
|
|
method: POST
|
|
body_format: form-urlencoded
|
|
body:
|
|
token: "{{ technitium_token }}"
|
|
zone: "{{ technitium_domain }}"
|
|
zoneTransfer: Allow
|
|
when: technitium_secondary_ips | length > 0
|
|
changed_when: true
|
|
|
|
- name: Add/update DNS A records
|
|
ansible.builtin.uri:
|
|
url: "http://localhost:{{ technitium_web_port }}/api/zones/records/add"
|
|
method: POST
|
|
body_format: form-urlencoded
|
|
body:
|
|
token: "{{ technitium_token }}"
|
|
domain: "{{ item.name }}.{{ technitium_domain }}"
|
|
type: A
|
|
ttl: "{{ technitium_ttl }}"
|
|
ipAddress: "{{ item.ip }}"
|
|
overwrite: "true"
|
|
loop: "{{ technitium_dns_records }}"
|
|
loop_control:
|
|
label: "{{ item.name }}.{{ technitium_domain }} → {{ item.ip }}"
|
|
changed_when: true
|