From c7a4dfce1ad0b545b05a8043e321ad49e48873a6 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelhalim Date: Wed, 17 Jun 2026 18:37:49 +0200 Subject: Refactor pi roles to use .home.arpa subdomains for services --- .gitignore | 1 + README.md | 15 +++++++++++++ host_vars/rpi5.local.yml | Bin 2061 -> 2359 bytes host_vars/rpi5.local.yml.example | 10 +++++++++ roles/grafana/defaults/main.yml | 1 + roles/grafana/handlers/main.yml | 6 +++++ roles/grafana/meta/argument_specs.yml | 4 ++++ roles/grafana/tasks/main.yml | 15 +++++++++++++ roles/grafana/templates/grafana.ini.j2 | 4 +++- roles/grafana/templates/grafana.nginx.conf.j2 | 10 +++++++++ roles/nginx/tasks/main.yml | 9 ++++++++ roles/nginx/templates/nginx.conf.j2 | 2 ++ roles/pihole/defaults/main.yml | 3 ++- roles/pihole/handlers/main.yml | 12 ++++++++++ roles/pihole/meta/argument_specs.yml | 13 +++++++---- roles/pihole/tasks/main.yml | 22 ++++++------------ roles/pihole/templates/pihole.nginx.conf.j2 | 17 ++++++++++++++ roles/pihole/templates/pihole.toml.j2 | 3 +++ roles/prometheus/defaults/main.yml | 1 + roles/prometheus/handlers/main.yml | 6 +++++ roles/prometheus/meta/argument_specs.yml | 4 ++++ roles/prometheus/tasks/main.yml | 15 +++++++++++++ .../prometheus/templates/prometheus.nginx.conf.j2 | 10 +++++++++ roles/wg_portal/defaults/main.yml | 2 +- roles/wg_portal/meta/argument_specs.yml | 8 +++---- roles/wg_portal/tasks/main.yml | 25 ++++++--------------- roles/wg_portal/templates/wg_portal.nginx.conf.j2 | 10 +++++++++ 27 files changed, 184 insertions(+), 44 deletions(-) create mode 100644 roles/grafana/templates/grafana.nginx.conf.j2 create mode 100644 roles/pihole/templates/pihole.nginx.conf.j2 create mode 100644 roles/prometheus/templates/prometheus.nginx.conf.j2 create mode 100644 roles/wg_portal/templates/wg_portal.nginx.conf.j2 diff --git a/.gitignore b/.gitignore index 18d7d2ab..f6d4c7d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .ansible CLAUDE.md +.claude diff --git a/README.md b/README.md index 9dca1949..8f05865b 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,21 @@ ansible-galaxy install -r requirements.yml ansible-playbook site.yml --ask-become-pass --limit ${HOSTNAME} ``` +## Raspberry Pi Services + +Services deployed on `rpi5.local` and their default FQDNs (resolved via Pi-hole DNS): + +| Service | Default FQDN | Description | +|---|---|---| +| Pi-hole | `dns.home.arpa` | DNS filtering and ad blocking | +| WireGuard Portal | `vpn.home.arpa` | WireGuard VPN management UI | +| Grafana | `monitor.home.arpa` | Metrics dashboards | +| Prometheus | `metrics.home.arpa` | Metrics collection | + +DNS records are managed via `pihole_dns_hosts` in `host_vars/rpi5.local.yml`. +Override the default FQDN per service using `pihole_hostname`, `wg_portal_hostname`, +`grafana_hostname`, or `prometheus_hostname`. + ## Special Thanks to - [Jeff Geerling](https://www.jeffgeerling.com/), who I learned a **LOT** from his open-source work. diff --git a/host_vars/rpi5.local.yml b/host_vars/rpi5.local.yml index fd4cb1d4..356a1076 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 49fcc3ba..95308832 100644 --- a/host_vars/rpi5.local.yml.example +++ b/host_vars/rpi5.local.yml.example @@ -5,6 +5,11 @@ network_ipv4_gateway: "10.0.0.1" network_ipv6_address: "fd5c:fd8b:2b8c::254/64" network_ipv6_gateway: "fd5c:fd8b:2b8c::1" +pihole_hostname: "dns.home.arpa" +wg_portal_hostname: "vpn.home.arpa" +grafana_hostname: "monitor.home.arpa" +prometheus_hostname: "metrics.home.arpa" + # TOTP generator: `openssl rand -base64 32 | head -c "20" | base32` pihole_totp_secret: "CHANGEME" pihole_password: "{{ ('changeme' | hash('sha256') | hash('sha256'))[:64] }}" @@ -16,6 +21,11 @@ pihole_dhcp_router: "{{ network_ipv4_address }}" pihole_dhcp_hosts: - "ff:ff:ff:ff:ff:fe, 10.0.0.252,TV" - "ff:ff:ff:ff:ff:ff, 10.0.0.253,Printer" +pihole_dns_hosts: + - "{{ network_ipv4_address }} {{ pihole_hostname }}" + - "{{ network_ipv4_address }} {{ wg_portal_hostname }}" + - "{{ network_ipv4_address }} {{ grafana_hostname }}" + - "{{ network_ipv4_address }} {{ prometheus_hostname }}" wireguard_autostart_connection: "vpn2" wireguard_connections: diff --git a/roles/grafana/defaults/main.yml b/roles/grafana/defaults/main.yml index e61aeded..0cbbb71b 100644 --- a/roles/grafana/defaults/main.yml +++ b/roles/grafana/defaults/main.yml @@ -11,3 +11,4 @@ grafana_datasources: default: true grafana_dashboards: [] +grafana_hostname: "monitor.home.arpa" diff --git a/roles/grafana/handlers/main.yml b/roles/grafana/handlers/main.yml index 408c8c3d..b212487b 100644 --- a/roles/grafana/handlers/main.yml +++ b/roles/grafana/handlers/main.yml @@ -4,3 +4,9 @@ ansible.builtin.systemd_service: name: "{{ grafana_service }}" state: "restarted" + +- name: "Restart nginx" + become: true + ansible.builtin.systemd_service: + name: "nginx" + state: "restarted" diff --git a/roles/grafana/meta/argument_specs.yml b/roles/grafana/meta/argument_specs.yml index fee43a8c..5a38b97d 100644 --- a/roles/grafana/meta/argument_specs.yml +++ b/roles/grafana/meta/argument_specs.yml @@ -48,6 +48,10 @@ argument_specs: description: "Whether this is the default datasource" type: "bool" default: false + grafana_hostname: + type: "str" + description: "FQDN for the grafana nginx vhost (e.g. 'monitor.home.arpa')." + default: "monitor.home.arpa" grafana_dashboards: description: "List of community dashboards to download and provision" type: "list" diff --git a/roles/grafana/tasks/main.yml b/roles/grafana/tasks/main.yml index 3e29489b..f1fa479e 100644 --- a/roles/grafana/tasks/main.yml +++ b/roles/grafana/tasks/main.yml @@ -86,3 +86,18 @@ name: "{{ grafana_service }}" state: "started" enabled: true + +- name: "Create grafana nginx vhost configuration" + become: true + when: grafana_hostname | length > 0 + ansible.builtin.template: + src: "grafana.nginx.conf.j2" + dest: "/etc/nginx/vhosts.d/grafana.conf" + owner: "root" + group: "root" + mode: "0644" + backup: true + validate: > + bash -c 'echo "events { worker_connections 64; } http { include %s; }" > /tmp/nginx-vhost.conf; + sudo nginx -T -c /tmp/nginx-vhost.conf; ec=$?; rm -f /tmp/nginx-vhost.conf; exit $ec' + notify: "Restart nginx" diff --git a/roles/grafana/templates/grafana.ini.j2 b/roles/grafana/templates/grafana.ini.j2 index 7685bed2..05166994 100644 --- a/roles/grafana/templates/grafana.ini.j2 +++ b/roles/grafana/templates/grafana.ini.j2 @@ -1,6 +1,8 @@ [server] http_port = {{ grafana_port }} -root_url = http://localhost:{{ grafana_port }}/ +{% if grafana_hostname | length > 0 %} +root_url = http://{{ grafana_hostname }}/ +{% endif %} [database] type = {{ grafana_database_type }} diff --git a/roles/grafana/templates/grafana.nginx.conf.j2 b/roles/grafana/templates/grafana.nginx.conf.j2 new file mode 100644 index 00000000..e6e1052c --- /dev/null +++ b/roles/grafana/templates/grafana.nginx.conf.j2 @@ -0,0 +1,10 @@ +server { + listen 80; + listen [::]:80; + + server_name {{ grafana_hostname }}; + + location / { + proxy_pass http://127.0.0.1:{{ grafana_port }}/; + } +} diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml index f913272e..456f088f 100644 --- a/roles/nginx/tasks/main.yml +++ b/roles/nginx/tasks/main.yml @@ -21,6 +21,15 @@ - "/etc/nginx/modules-enabled" - "/var/log/nginx" +- name: "Ensure nginx vhosts.d directory exists" + become: true + ansible.builtin.file: + path: "/etc/nginx/vhosts.d" + state: "directory" + owner: "root" + group: "root" + mode: "0755" + - name: "Check if SSL certificate exists" become: true ansible.builtin.stat: diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 index 1bce57da..1ebbe3df 100644 --- a/roles/nginx/templates/nginx.conf.j2 +++ b/roles/nginx/templates/nginx.conf.j2 @@ -76,4 +76,6 @@ http { include /etc/nginx/conf.d/*.conf; } + + include /etc/nginx/vhosts.d/*.conf; } diff --git a/roles/pihole/defaults/main.yml b/roles/pihole/defaults/main.yml index 4e1d0bf7..d980c67c 100644 --- a/roles/pihole/defaults/main.yml +++ b/roles/pihole/defaults/main.yml @@ -16,4 +16,5 @@ pihole_dhcp_domain: "local" pihole_dhcp_hosts: [] pihole_domain: "" pihole_port: "8081" -pihole_by_nginx: false +pihole_hostname: "dns.home.arpa" +pihole_dns_hosts: [] diff --git a/roles/pihole/handlers/main.yml b/roles/pihole/handlers/main.yml index 1cad8eff..d839828e 100644 --- a/roles/pihole/handlers/main.yml +++ b/roles/pihole/handlers/main.yml @@ -14,3 +14,15 @@ - name: "Show install output" ansible.builtin.debug: var: pihole_install.stdout + +- name: "Restart pihole-FTL" + become: true + ansible.builtin.systemd_service: + name: "pihole-FTL" + state: "restarted" + +- name: "Restart nginx" + become: true + ansible.builtin.systemd_service: + name: "nginx" + state: "restarted" diff --git a/roles/pihole/meta/argument_specs.yml b/roles/pihole/meta/argument_specs.yml index adca47b9..40662436 100644 --- a/roles/pihole/meta/argument_specs.yml +++ b/roles/pihole/meta/argument_specs.yml @@ -63,7 +63,12 @@ argument_specs: type: "str" description: "The port to serve pihole on, when empty use pihole default 80,433" default: "8081" - pihole_by_nginx: - description: "Serve pihole behind nginx reverse proxy" - type: "bool" - default: false + pihole_hostname: + type: "str" + description: "FQDN for the pihole nginx vhost (e.g. 'dns.home.arpa')." + default: "dns.home.arpa" + pihole_dns_hosts: + type: "list" + description: "Custom DNS A records written to pihole.toml [dns] hosts (format: 'IP HOSTNAME')" + elements: "str" + default: [] diff --git a/roles/pihole/tasks/main.yml b/roles/pihole/tasks/main.yml index 52ccafcf..66208bdf 100644 --- a/roles/pihole/tasks/main.yml +++ b/roles/pihole/tasks/main.yml @@ -149,25 +149,17 @@ tags: - molecule-idempotence-notest -- name: "Create pihole nginx configurations" +- name: "Create pihole nginx vhost configuration" become: true - when: pihole_by_nginx is defined and pihole_by_nginx | bool - ansible.builtin.copy: - content: | - location /admin/ { - proxy_pass http://127.0.0.1:{{ pihole_port }}/admin/; - } - location /api/ { - proxy_pass http://127.0.0.1:{{ pihole_port }}/api/; - } - dest: "/etc/nginx/conf.d/pihole.conf" + when: pihole_hostname | length > 0 + ansible.builtin.template: + src: "pihole.nginx.conf.j2" + dest: "/etc/nginx/vhosts.d/pihole.conf" owner: "root" group: "root" mode: "0644" backup: true - # workaround issue: https://github.com/ansible/ansible/issues/9112 - # ref: https://serverfault.com/a/811520 validate: > - bash -c 'echo "events { worker_connections 2; } http { server { include %s; } }" > /tmp/nginx.conf; - sudo nginx -T -c /tmp/nginx.conf; ec=$?; rm -f /tmp/nginx.conf; exit $ec' + bash -c 'echo "events { worker_connections 64; } http { include %s; }" > /tmp/nginx-vhost.conf; + sudo nginx -T -c /tmp/nginx-vhost.conf; ec=$?; rm -f /tmp/nginx-vhost.conf; exit $ec' notify: "Restart nginx" diff --git a/roles/pihole/templates/pihole.nginx.conf.j2 b/roles/pihole/templates/pihole.nginx.conf.j2 new file mode 100644 index 00000000..91f0fea4 --- /dev/null +++ b/roles/pihole/templates/pihole.nginx.conf.j2 @@ -0,0 +1,17 @@ +server { + listen 80; + listen [::]:80; + + server_name {{ pihole_hostname }}; + + location = / { + return 301 /admin/; + } + + location /admin/ { + proxy_pass http://127.0.0.1:{{ pihole_port }}/admin/; + } + location /api/ { + proxy_pass http://127.0.0.1:{{ pihole_port }}/api/; + } +} diff --git a/roles/pihole/templates/pihole.toml.j2 b/roles/pihole/templates/pihole.toml.j2 index 13f7ba1f..a755040b 100644 --- a/roles/pihole/templates/pihole.toml.j2 +++ b/roles/pihole/templates/pihole.toml.j2 @@ -37,6 +37,9 @@ interface = "{{ pihole_interface }}" # - "NONE" # Do not add any configuration concerning the listening mode to the dnsmasq config file. listeningMode = "LOCAL" +{% if pihole_dns_hosts | length > 0 %} +hosts = {{ pihole_dns_hosts | to_json }} +{% endif %} [dns.cache] size = 10000 # Cache size of the DNS server. diff --git a/roles/prometheus/defaults/main.yml b/roles/prometheus/defaults/main.yml index 7a231b5c..cd91dee6 100644 --- a/roles/prometheus/defaults/main.yml +++ b/roles/prometheus/defaults/main.yml @@ -8,3 +8,4 @@ prometheus_targets: - "{{ prometheus_host }}:{{ prometheus_port }}" prometheus_node_exporter_targets: [] +prometheus_hostname: "metrics.home.arpa" diff --git a/roles/prometheus/handlers/main.yml b/roles/prometheus/handlers/main.yml index d35a8531..b42a2391 100644 --- a/roles/prometheus/handlers/main.yml +++ b/roles/prometheus/handlers/main.yml @@ -4,3 +4,9 @@ ansible.builtin.systemd_service: name: "prometheus" state: "restarted" + +- name: "Restart nginx" + become: true + ansible.builtin.systemd_service: + name: "nginx" + state: "restarted" diff --git a/roles/prometheus/meta/argument_specs.yml b/roles/prometheus/meta/argument_specs.yml index e1a72307..5f908119 100644 --- a/roles/prometheus/meta/argument_specs.yml +++ b/roles/prometheus/meta/argument_specs.yml @@ -28,3 +28,7 @@ argument_specs: type: "list" elements: "str" default: [] + prometheus_hostname: + type: "str" + description: "FQDN for the prometheus nginx vhost (e.g. 'metrics.home.arpa')." + default: "metrics.home.arpa" diff --git a/roles/prometheus/tasks/main.yml b/roles/prometheus/tasks/main.yml index 1f639c6d..8e315bec 100644 --- a/roles/prometheus/tasks/main.yml +++ b/roles/prometheus/tasks/main.yml @@ -61,3 +61,18 @@ name: "prometheus" state: "started" enabled: true + +- name: "Create prometheus nginx vhost configuration" + become: true + when: prometheus_hostname | length > 0 + ansible.builtin.template: + src: "prometheus.nginx.conf.j2" + dest: "/etc/nginx/vhosts.d/prometheus.conf" + owner: "root" + group: "root" + mode: "0644" + backup: true + validate: > + bash -c 'echo "events { worker_connections 64; } http { include %s; }" > /tmp/nginx-vhost.conf; + sudo nginx -T -c /tmp/nginx-vhost.conf; ec=$?; rm -f /tmp/nginx-vhost.conf; exit $ec' + notify: "Restart nginx" diff --git a/roles/prometheus/templates/prometheus.nginx.conf.j2 b/roles/prometheus/templates/prometheus.nginx.conf.j2 new file mode 100644 index 00000000..5ae9c20c --- /dev/null +++ b/roles/prometheus/templates/prometheus.nginx.conf.j2 @@ -0,0 +1,10 @@ +server { + listen 80; + listen [::]:80; + + server_name {{ prometheus_hostname }}; + + location / { + proxy_pass http://127.0.0.1:{{ prometheus_port }}/; + } +} diff --git a/roles/wg_portal/defaults/main.yml b/roles/wg_portal/defaults/main.yml index 38f1bda7..3d47add4 100644 --- a/roles/wg_portal/defaults/main.yml +++ b/roles/wg_portal/defaults/main.yml @@ -2,4 +2,4 @@ wg_portal_version: "latest" wg_portal_port: "8080" wg_portal_password: "{{ ('changeme' | hash('sha256') | hash('sha256'))[:64] }}" -wg_portal_by_nginx: false +wg_portal_hostname: "vpn.home.arpa" diff --git a/roles/wg_portal/meta/argument_specs.yml b/roles/wg_portal/meta/argument_specs.yml index 3ffdc604..eee9d7ed 100644 --- a/roles/wg_portal/meta/argument_specs.yml +++ b/roles/wg_portal/meta/argument_specs.yml @@ -14,7 +14,7 @@ argument_specs: description: "Password hash for wg-portal authentication" type: "str" default: "{{ ('changeme' | hash('sha256') | hash('sha256'))[:64] }}" - wg_portal_by_nginx: - description: "Serve wg_portal behind nginx reverse proxy" - type: "bool" - default: false + wg_portal_hostname: + type: "str" + description: "FQDN for the wg-portal nginx vhost (e.g. 'vpn.home.arpa')." + default: "vpn.home.arpa" diff --git a/roles/wg_portal/tasks/main.yml b/roles/wg_portal/tasks/main.yml index a7962919..78a235a1 100644 --- a/roles/wg_portal/tasks/main.yml +++ b/roles/wg_portal/tasks/main.yml @@ -94,28 +94,17 @@ state: "started" enabled: true -- name: "Create wg-portal nginx configurations" +- name: "Create wg-portal nginx vhost configuration" become: true - when: wg_portal_by_nginx is defined and wg_portal_by_nginx | bool - ansible.builtin.copy: - content: | - location / { - proxy_pass http://127.0.0.1:{{ wg_portal_port }}/; - } - location /api/connections { - proxy_pass http://127.0.0.1:{{ wg_portal_port }}/api/connections; - } - location /api/status { - proxy_pass http://127.0.0.1:{{ wg_portal_port }}/api/status; - } - dest: "/etc/nginx/conf.d/wg-portal.conf" + when: wg_portal_hostname | length > 0 + ansible.builtin.template: + src: "wg_portal.nginx.conf.j2" + dest: "/etc/nginx/vhosts.d/wg-portal.conf" owner: "root" group: "root" mode: "0644" backup: true - # workaround issue: https://github.com/ansible/ansible/issues/9112 - # ref: https://serverfault.com/a/811520 validate: > - bash -c 'echo "events { worker_connections 2; } http { server { include %s; } }" > /tmp/nginx.conf; - sudo nginx -T -c /tmp/nginx.conf; ec=$?; rm -f /tmp/nginx.conf; exit $ec' + bash -c 'echo "events { worker_connections 64; } http { include %s; }" > /tmp/nginx-vhost.conf; + sudo nginx -T -c /tmp/nginx-vhost.conf; ec=$?; rm -f /tmp/nginx-vhost.conf; exit $ec' notify: "Restart nginx" diff --git a/roles/wg_portal/templates/wg_portal.nginx.conf.j2 b/roles/wg_portal/templates/wg_portal.nginx.conf.j2 new file mode 100644 index 00000000..734ecac9 --- /dev/null +++ b/roles/wg_portal/templates/wg_portal.nginx.conf.j2 @@ -0,0 +1,10 @@ +server { + listen 80; + listen [::]:80; + + server_name {{ wg_portal_hostname }}; + + location / { + proxy_pass http://127.0.0.1:{{ wg_portal_port }}/; + } +} -- cgit v1.2.3