--- - name: Set up technitium LXC hosts: technitium become: yes vars: dotnet_dir: /opt/dotnet dotnet_version: "10.0" dotnet_runtime_prefix: "Microsoft.AspNetCore.App 10.0." dotnet_url: "https://dot.net/v1/dotnet-install.sh" dns_config: /etc/dns dns_log: /var/log/technitium/dns dns_url: "https://download.technitium.com/dns/DnsServerPortable.tar.gz" service_user: dns-server tasks: - name: default_packages ansible.builtin.include_tasks: ../../templates/default_packages.yml - name: create_user ansible.builtin.include_tasks: ../../templates/create_user.yml vars: username: nick github_user: Chewbaccalakis # Determine dns_dir: use /etc/dns if an existing install is detected, otherwise /opt/technitium/dns - name: Check for existing install at /etc/dns/config ansible.builtin.stat: path: /etc/dns/config register: etc_dns_config - name: Set dns_dir for existing install ansible.builtin.set_fact: dns_dir: /etc/dns dns_tar: /etc/dns/DnsServerPortable.tar.gz when: etc_dns_config.stat.exists and etc_dns_config.stat.isdir - name: Set dns_dir for fresh install ansible.builtin.set_fact: dns_dir: /opt/technitium/dns dns_tar: /opt/technitium/dns/DnsServerPortable.tar.gz when: not (etc_dns_config.stat.exists and etc_dns_config.stat.isdir) - name: Create required directories ansible.builtin.file: path: "{{ item }}" state: directory mode: '0755' loop: - "{{ dns_dir }}" - "{{ dns_config }}" # .NET Runtime - name: Check installed .NET runtimes ansible.builtin.command: dotnet --list-runtimes register: dotnet_runtimes changed_when: false failed_when: false - name: Install ASP.NET Core Runtime when: dotnet_runtime_prefix not in (dotnet_runtimes.stdout | default('')) block: - name: Download dotnet-install.sh ansible.builtin.get_url: url: "{{ dotnet_url }}" dest: /tmp/dotnet-install.sh mode: '0755' - name: Run dotnet-install.sh ansible.builtin.command: > bash /tmp/dotnet-install.sh -c {{ dotnet_version }} --runtime aspnetcore --no-path --install-dir {{ dotnet_dir }} - name: Create /usr/bin/dotnet symlink ansible.builtin.file: src: "{{ dotnet_dir }}/dotnet" dest: /usr/bin/dotnet state: link # DNS Server - name: Download Technitium DNS Server archive ansible.builtin.get_url: url: "{{ dns_url }}" dest: "{{ dns_tar }}" mode: '0644' - name: Extract Technitium DNS Server ansible.builtin.unarchive: src: "{{ dns_tar }}" dest: "{{ dns_dir }}" remote_src: yes # ICU - name: Test ICU availability for .NET ansible.builtin.command: "dotnet {{ dns_dir }}/DnsServerApp.dll --icu-test" register: icu_test changed_when: false failed_when: false - name: Install ICU library when: icu_test.rc != 0 block: - name: Find available ICU package ansible.builtin.shell: | for pkg in libicu74 libicu72 libicu70; do if apt-cache show $pkg >/dev/null 2>&1; then echo $pkg exit 0 fi done echo "libicu-dev" register: icu_package changed_when: false - name: Install ICU package ansible.builtin.apt: name: "{{ icu_package.stdout }}" state: present update_cache: yes - name: Verify ICU works after install ansible.builtin.command: "dotnet {{ dns_dir }}/DnsServerApp.dll --icu-test" # Systemd service - name: Check if dns.service is already installed ansible.builtin.stat: path: /etc/systemd/system/dns.service register: dns_service_file - name: Restart existing dns service (update path) ansible.builtin.systemd: name: dns.service state: restarted when: dns_service_file.stat.exists - name: First-time service setup when: not dns_service_file.stat.exists block: - name: Create DNS log directory ansible.builtin.file: path: "{{ dns_log }}" state: directory mode: '0755' - name: Create dns-server service user ansible.builtin.user: name: "{{ service_user }}" system: yes create_home: no shell: /usr/sbin/nologin - name: Set ownership on DNS directories ansible.builtin.file: path: "{{ item }}" owner: "{{ service_user }}" group: "{{ service_user }}" recurse: yes loop: - "{{ dns_dir }}" - "{{ dns_config }}" - "{{ dns_log }}" - name: Copy systemd service file ansible.builtin.copy: src: "{{ dns_dir }}/systemd.service" dest: /etc/systemd/system/dns.service remote_src: yes - name: Enable dns.service ansible.builtin.systemd: name: dns.service enabled: yes daemon_reload: yes - name: Stop and disable systemd-resolved ansible.builtin.systemd: name: systemd-resolved state: stopped enabled: no failed_when: false - name: Start dns.service ansible.builtin.systemd: name: dns.service state: started - name: Check if NetworkManager.conf exists ansible.builtin.stat: path: /etc/NetworkManager/NetworkManager.conf register: nm_conf - name: Configure NetworkManager to not manage DNS community.general.ini_file: path: /etc/NetworkManager/NetworkManager.conf section: main option: dns value: none no_extra_spaces: yes when: nm_conf.stat.exists - name: Back up existing resolv.conf ansible.builtin.copy: src: /etc/resolv.conf dest: "{{ dns_dir }}/resolv.conf.bak" remote_src: yes force: no - name: Configure resolv.conf to use local DNS ansible.builtin.copy: dest: /etc/resolv.conf content: | # Generated by Technitium DNS Server Installer nameserver 127.0.0.1 # Admin user setup - name: Prompt for Technitium admin password ansible.builtin.pause: prompt: "Set Technitium admin password" echo: no register: technitium_pass_raw - name: Prompt to confirm Technitium admin password ansible.builtin.pause: prompt: "Confirm Technitium admin password" echo: no register: technitium_pass_confirm - name: Fail if passwords do not match ansible.builtin.fail: msg: "Passwords do not match!" when: technitium_pass_raw.user_input != technitium_pass_confirm.user_input - name: Wait for Technitium web console to be available ansible.builtin.wait_for: host: localhost port: 5380 timeout: 60 - name: Login with default admin credentials ansible.builtin.uri: url: "http://localhost:5380/api/user/login" method: GET body_format: form-urlencoded body: user: admin pass: admin includeInfo: "true" register: technitium_login no_log: true failed_when: technitium_login.json.status != 'ok' - name: Change admin password ansible.builtin.uri: url: "http://localhost:5380/api/user/changePassword" method: GET body_format: form-urlencoded body: token: "{{ technitium_login.json.response.token }}" pass: "{{ technitium_pass_raw.user_input }}" no_log: true failed_when: technitium_change_pass.json.status != 'ok' register: technitium_change_pass