--- # see: https://calomel.org/unbound_dns.html # see: https://wiki.archlinux.org/title/Unbound - name: Install unbound ansible.builtin.package: name: unbound state: present # Note: on archlinux this is already shipped within unbound - name: Install unbound-anchor on debian/ubuntu ansible.builtin.package: name: unbound-anchor state: present when: ansible_facts['os_family'] == 'Debian' - name: Ensure unbound configuration is owned by unbound ansible.builtin.shell: | find "{{ unbound_config_base_path }}" -type d -exec chmod 755 {} \; find "{{ unbound_config_base_path }}" -type f -exec chmod 644 {} \; chown -R unbound:unbound "{{ unbound_config_base_path }}" args: executable: /bin/bash - name: Ensure apparmor profile for unbound exists ansible.builtin.copy: dest: /etc/apparmor.d/usr.sbin.unbound content: | #include /usr/sbin/unbound { #include #include capability net_bind_service, capability setgid, capability setuid, capability sys_chroot, capability sys_resource, /etc/unbound/** r, /etc/unbound/root.key* rw, /var/lib/unbound/** rwk, /run/unbound.pid rw, /usr/sbin/unbound mr, # Allow reading system certificates /etc/ssl/certs/** r, /usr/share/ca-certificates/** r, # Allow network access network inet dgram, network inet6 dgram, network inet stream, network inet6 stream, } owner: root group: root mode: "0644" when: ansible_facts.apparmor.status == "enabled" notify: - Reload AppArmor profile - name: Check if root.hints exists ansible.builtin.stat: path: "{{ unbound_root_hints_path }}" register: root_hints - name: Update root.hints (if older than 6 months or missing) when: > (not root_hints.stat.exists) or (ansible_facts['date_time']['epoch'] | int - root_hints.stat.mtime > 15552000) block: - name: Download latest root hints from internic ansible.builtin.get_url: url: https://www.internic.net/domain/named.root dest: "{{ unbound_root_hints_path }}" owner: unbound group: unbound mode: "0644" - name: Check if unbound ad_servers configuration exists ansible.builtin.stat: path: "{{ unbound_ad_servers_config_path }}" register: ad_servers - name: Update the ad_servers list if older than 2 weeks or missing when: > (not ad_servers.stat.exists) or (ansible_facts['date_time']['epoch'] | int - ad_servers.stat.mtime > 1209600) block: - name: Download stevenblack's hosts file ansible.builtin.get_url: url: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts dest: /tmp/hosts.txt mode: "0644" - name: Convert hosts file to unbound format ansible.builtin.shell: | grep '^0\.0\.0\.0' /tmp/hosts.txt | awk '{print "local-zone: \""$2"\" always_nxdomain"}' > "{{ unbound_ad_servers_config_path }}" && chown unbound:unbound "{{ unbound_ad_servers_config_path }}" args: executable: /bin/bash - name: Clean up temporary file ansible.builtin.file: path: /tmp/hosts.txt state: absent - name: Initialize dnssec trust anchor if missing ansible.builtin.command: unbound-anchor -a {{ unbound_anchor_root_key }} args: creates: "{{ unbound_anchor_root_key }}" - name: Ensure root.key has correct ownership and permissions ansible.builtin.file: path: "{{ unbound_anchor_root_key }}" owner: unbound group: unbound mode: "0640" state: file - name: Create /etc/default/unbound for DAEMON_OPTS ansible.builtin.copy: dest: /etc/default/unbound content: | # Unbound daemon options DAEMON_OPTS="" owner: root group: root mode: "0644" - name: Configure sysctl for unbound socket buffers ansible.posix.sysctl: name: net.core.rmem_max value: "1048576" state: present sysctl_set: true reload: true - name: Install unbound config ansible.builtin.template: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: unbound group: unbound loop: - { src: unbound.conf.j2, dest: "{{ unbound_config_path }}" } - { src: custom-lan.conf.j2, dest: "{{ unbound_custom_lan_config_path }}" } - { src: custom-vpn.conf.j2, dest: "{{ unbound_custom_vpn_config_path }}" } notify: - Check Unbound config syntax - Reload systemd and restart unbound - name: Make sure unbound starts after wg-quick@wg0 block: - name: Ensure unbound.service.d directory exists ansible.builtin.file: path: /etc/systemd/system/unbound.service.d state: directory mode: "0755" - name: Configure unbound systemd service ansible.builtin.copy: dest: /etc/systemd/system/unbound.service.d/override.conf content: | [Unit] After=wg-quick@wg0.service Requires=wg-quick@wg0.service notify: Reload systemd and restart unbound - name: Enables unbound service ansible.builtin.service: name: unbound enabled: true state: started - name: Firewall ufw rules for unbound community.general.ufw: rule: allow port: "{{ unbound_port }}" proto: any src: "{{ item.src }}" comment: "{{ item.comment }}" direction: in loop: "{{ unbound_firewall_allowed_sources | default([]) }}" retries: 5 delay: 2 register: ufw_result until: ufw_result is succeeded