From 8f539a76effcfb794145905736106de9e7f6aa8d Mon Sep 17 00:00:00 2001 From: Ahmed Abdelhalim Date: Mon, 11 Aug 2025 21:50:28 +0200 Subject: Add pihole role to configure raspberry pi --- host_vars/rpi5.local.yml | Bin 104 -> 198 bytes host_vars/rpi5.local.yml.example | 3 ++ molecule/default/converge.yml | 8 +++++ roles/pihole/README.md | 13 +++++++ roles/pihole/defaults/main.yml | 11 ++++++ roles/pihole/handlers/main.yml | 5 +++ roles/pihole/meta/argument_specs.yml | 36 ++++++++++++++++++++ roles/pihole/meta/main.yml | 16 +++++++++ roles/pihole/tasks/main.yml | 62 ++++++++++++++++++++++++++++++++++ roles/pihole/templates/pihole.toml.j2 | 61 +++++++++++++++++++++++++++++++++ site.yml | 8 +++++ 11 files changed, 223 insertions(+) create mode 100644 roles/pihole/README.md create mode 100644 roles/pihole/defaults/main.yml create mode 100644 roles/pihole/handlers/main.yml create mode 100644 roles/pihole/meta/argument_specs.yml create mode 100644 roles/pihole/meta/main.yml create mode 100644 roles/pihole/tasks/main.yml create mode 100644 roles/pihole/templates/pihole.toml.j2 diff --git a/host_vars/rpi5.local.yml b/host_vars/rpi5.local.yml index d2c64414..96d80283 100644 Binary files a/host_vars/rpi5.local.yml and b/host_vars/rpi5.local.yml differ diff --git a/host_vars/rpi5.local.yml.example b/host_vars/rpi5.local.yml.example index aa9af763..57ce2750 100644 --- a/host_vars/rpi5.local.yml.example +++ b/host_vars/rpi5.local.yml.example @@ -1,3 +1,6 @@ --- network_ipv4_address: "10.0.0.202" network_ipv4_gateway: "10.0.0.1" + +pihole_totp: "CHANGEME" # TOTP generator: `openssl rand -base64 32 | head -c "20" | base32` +pihole_password: "{{ ('changeme' | hash('sha256') | hash('sha256'))[:64] }}" diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index eacb6559..90c760b1 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -1,6 +1,7 @@ --- - name: Converge hosts: instance + gather_facts: true vars: ansible_become_method: su network_wifi_ssid: "TEST_WIFI_SSID" @@ -18,3 +19,10 @@ - role: password_policy - role: locales - role: timezone + - role: pihole + vars: + pihole_interface: "eth0" + pihole_domain: "pi.hole" + when: + - lookup('ansible.builtin.env', 'MOLECULE_DISTRO') == 'ubuntu' or + lookup('ansible.builtin.env', 'MOLECULE_DISTRO') == 'raspberrypi' diff --git a/roles/pihole/README.md b/roles/pihole/README.md new file mode 100644 index 00000000..719020b5 --- /dev/null +++ b/roles/pihole/README.md @@ -0,0 +1,13 @@ +# Ansible Role: pihole + +This role configure the [pihole](https://github.com/pi-hole/pi-hole) DNS Sinkhole. + +## Role Variables + +- `pihole_password` the pi-hole web-server plain text password (default: `changeme`). +- `pihole_totp_secret` the pi-hole TOTP 2FA secret (default: `CHANGEME`). +- `pihole_interface` the interface to bind the pi-hole on (default: "{{ ansible_default_ipv4.interface }}"). +- `pihole_dns` the list of DNS servers to use as upstreams (default: `[9.9.9.9, 1.1.1.1, 8.8.8.8]`). +- `pihole_dns_blocking_enabled` the boolean flag to toggle DNS blocking (default: true). +- `pihole_dhcp_enabled` the boolean flag to toggle DHCP on the pi-hole (default: false). +- `pihole_domain` the domain name to be configured when the pi is exposed over the internet (default: ""). diff --git a/roles/pihole/defaults/main.yml b/roles/pihole/defaults/main.yml new file mode 100644 index 00000000..e5edf117 --- /dev/null +++ b/roles/pihole/defaults/main.yml @@ -0,0 +1,11 @@ +--- +pihole_password: "{{ ('changeme' | hash('sha256') | hash('sha256'))[:64] }}" +pihole_totp_secret: "CHANGEME" +pihole_dns: + - "9.9.9.9" # Quad9 + - "1.1.1.1" # Cloudflare + - "8.8.8.8" # Google +pihole_interface: "{{ ansible_default_ipv4.interface }}" +pihole_dns_blocking_enabled: true +pihole_dhcp_enabled: false +pihole_domain: "" diff --git a/roles/pihole/handlers/main.yml b/roles/pihole/handlers/main.yml new file mode 100644 index 00000000..f400be2c --- /dev/null +++ b/roles/pihole/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: "Restart pihole" + ansible.builtin.systemd_service: + name: "pihole-FTL" + state: "restarted" diff --git a/roles/pihole/meta/argument_specs.yml b/roles/pihole/meta/argument_specs.yml new file mode 100644 index 00000000..b50fc5c5 --- /dev/null +++ b/roles/pihole/meta/argument_specs.yml @@ -0,0 +1,36 @@ +--- +argument_specs: + main: + options: + pihole_password: + type: "str" + description: "The pi-hole web-server plain text password (double sha265 hashed) default: changeme" + default: "{{ ('changeme' | hash('sha256') | hash('sha256'))[:64] }}" + pihole_totp_secret: + type: "str" + description: "The pi-hole TOTP 2FA secret" + default: "CHANGEME" + pihole_dns: + type: "list" + description: "The list of DNS servers to use as upstreams" + elements: "str" + default: + - "9.9.9.9" # Quad9 + - "1.1.1.1" # Cloudflare + - "8.8.8.8" # Google + pihole_interface: + type: "str" + description: "The interface to bind the pi-hole on" + default: "{{ ansible_default_ipv4.interface }}" + pihole_dns_blocking_enabled: + type: "bool" + description: "The boolean flag to toggle DNS blocking" + default: true + pihole_dhcp_enabled: + type: "bool" + description: "The boolean flag to toggle DHCP on the pi-hole" + default: false + pihole_domain: + type: "str" + description: "The domain name to be configured when the pi is exposed over the internet" + default: "" diff --git a/roles/pihole/meta/main.yml b/roles/pihole/meta/main.yml new file mode 100644 index 00000000..8b21005e --- /dev/null +++ b/roles/pihole/meta/main.yml @@ -0,0 +1,16 @@ +--- +dependencies: [] +galaxy_info: + role_name: pihole + author: a14m + description: "Configure pihole on supported devices" + company: "kartoffeln.work GmbH." + license: "license MIT" + min_ansible_version: "2.18" + platforms: + - name: Ubuntu + versions: + - noble + - name: Debian + versions: + - bookworm diff --git a/roles/pihole/tasks/main.yml b/roles/pihole/tasks/main.yml new file mode 100644 index 00000000..bb377957 --- /dev/null +++ b/roles/pihole/tasks/main.yml @@ -0,0 +1,62 @@ +--- +- name: "Crete Pi-hole user" + ansible.builtin.user: + name: "pihole" + system: true + shell: "/usr/sbin/nologin" + home: "/var/lib/pihole" + create_home: true + +- name: "Create Pi-hole directories" + ansible.builtin.file: + path: "{{ item }}" + state: "directory" + owner: "pihole" + group: "pihole" + mode: "0755" + with_items: + - "/etc/pihole" + - "/var/lib/pihole" + - "/var/log/pihole" + +- name: "Create Pi-hole configuration file" + ansible.builtin.template: + src: "pihole.toml.j2" + dest: "/etc/pihole/pihole.toml" + owner: "pihole" + group: "pihole" + mode: "0644" + notify: "Restart pihole" + tags: + - molecule-idempotence-notest + +- name: "Download Pi-hole installer" + ansible.builtin.get_url: + url: "https://install.pi-hole.net" + dest: "/tmp/install_pihole.sh" + mode: "0755" + +- name: "Run Pi-hole installer" + ansible.builtin.command: + cmd: "/tmp/install_pihole.sh --unattended" + register: pihole_install + environment: + PIHOLE_SKIP_OS_CHECK: "true" + args: + creates: "/usr/local/bin/pihole" + +- name: "Show install output" + ansible.builtin.debug: + var: pihole_install.stdout + +- name: "Ensure Pi-hole started/enabled" + ansible.builtin.systemd_service: + name: "pihole-FTL" + enabled: true + state: "started" + daemon_reload: true + +- name: "Update Pi-hole blocklists" + ansible.builtin.command: + cmd: "pihole -g" + changed_when: false diff --git a/roles/pihole/templates/pihole.toml.j2 b/roles/pihole/templates/pihole.toml.j2 new file mode 100644 index 00000000..6d730d11 --- /dev/null +++ b/roles/pihole/templates/pihole.toml.j2 @@ -0,0 +1,61 @@ +# Based on https://github.com/pi-hole/FTL/blob/bc185680fc2af2f7e21bd120f56749051207914f/test/pihole.toml + +[misc] +# Put configuration into read-only mode. This will prevent any changes to the +# configuration file via the API or CLI. This setting useful when a configuration is +# to be forced/modified by some third-party application (like infrastructure-as-code +# providers) and should not be changed by any means. +readOnly = false + +[dns] +# Array of upstream DNS servers used by Pi-hole +# Example: [ "8.8.8.8", "127.0.0.1#5335", "docker-resolver" ] +upstreams = {{ pihole_dns }} + +domainNeeded = true # Never forward plain names (without dots or domain parts) +expandHosts = true # Add the domain to simple names in /etc/hosts +domain = "lan" # Local domain name +bogusPriv = true # Don't forward reverse DNS queries for private IP ranges +dnssec = false # Validate DNS replies using DNSSEC +port = 53 # Port used by the DNS Server + +# Network interface Pi-hole should listen on +# Usually "eth0" for Ethernet or "wlan0" for WiFi +interface = "{{ pihole_interface }}" + +# Pi-hole interface listening modes +# - "LOCAL" (default) +# Allow only local requests. This setting accepts DNS queries only from hosts +# whose address is on a local subnet. +# - "SINGLE" +# Permit all origins, accept only on the specified interface. Respond only to +# queries arriving on the specified interface. +# - "BIND" +# By default, FTL binds the wildcard address. +# - "ALL" +# Permit all origins, accept on all interfaces. Make sure your Pi-hole is firewalled. +# - "NONE" +# Do not add any configuration concerning the listening mode to the dnsmasq config file. +listeningMode = "SINGLE" + +[dns.cache] +size = 10000 # Cache size of the DNS server. + +[dns.blocking] +# Master switch for Pi-hole's blocking functionality +active = {{ pihole_dns_blocking_enabled | lower }} + +[dhcp] +active = {{ pihole_dhcp_enabled | lower }} + +[webserver] +{% if (pihole_domain is defined) and (pihole_domain != '') %} +domain = {{ pihole_domain }} +{% endif %} + +[webserver.tls] +cert = "/etc/pihole/tls.pem" + +[webserver.api] +pwhash = "{{ pihole_password }}" # valid double hashed string +totp_secret = "{{ pihole_totp_secret }}" # valid TOTP secret (20 Bytes in Base32 encoding) diff --git a/site.yml b/site.yml index acd37938..82bfe8e7 100644 --- a/site.yml +++ b/site.yml @@ -30,3 +30,11 @@ roles: - role: "locales" - role: "timezone" + +- name: "Raspberry Pi configure" + gather_facts: true + hosts: + - rpi5.local + become: true + roles: + - role: "pihole" -- cgit v1.2.3