Skip to content

Commit

Permalink
nixos/pihole-web: init
Browse files Browse the repository at this point in the history
Pihole's dashboard is a web app which visualises statistics from pihole-FTL
(i.e. dnsmasq), shows query logs, and allows configuration.

With this module, configuration is largely declarative and immutable, so
settings can't be changed, but they can be viewed from the webpage.

The admin page also allows regenerating the DNS ("gravity") database, which
requires write access to the pihole state directory, as well as being able to
signal pihole-FTL to reload its DNS cache. For the latter, a polkit rule is
optionally enabled.
  • Loading branch information
williamvds committed Jan 12, 2024
1 parent ac13b49 commit 3ec0d54
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Expand Up @@ -1332,6 +1332,7 @@
./services/web-apps/phylactery.nix
./services/web-apps/photoprism.nix
./services/web-apps/pict-rs.nix
./services/web-apps/pihole-web.nix
./services/web-apps/plantuml-server.nix
./services/web-apps/plausible.nix
./services/web-apps/powerdns-admin.nix
Expand Down
13 changes: 13 additions & 0 deletions nixos/modules/services/web-apps/pihole-web.md
@@ -0,0 +1,13 @@
# Pi-hole Dashboard {#module-services-web-apps-pihole-web}

The Pi-hole suite provides a web GUI for controlling and monitoring
[pihole-FTL](#module-services-networking-pihole-ftl).

## Configuration {#module-services-web-apps-pihole-web-configuration}

The dashboard requires little configuration, because it is largely parsed from
[the Dnsmasq configuration](#module-services-networking-dnsmasq).

Note that most settings on the *Settings* page are Dnsmasq options. Since the
configuration is immutable and comes from NixOS options, most settings cannot be
changed.
131 changes: 131 additions & 0 deletions nixos/modules/services/web-apps/pihole-web.nix
@@ -0,0 +1,131 @@
{ pkgs
, lib
, config
, ...
}:

with { inherit (lib) mdDoc mkOption types; };

let
cfg = config.services.pihole-web;
ftlCfg = config.services.pihole-ftl;
in
{
options.services.pihole-web = {
enable = lib.mkEnableOption (mdDoc "Pi-hole dashboard");
hostName = mkOption {
type = types.str;
description = mdDoc "Domain name for the website.";
default = "pi.hole";
};
package = lib.mkPackageOptionMD pkgs "pihole-web" {};
dnsServers = mkOption {
type = types.str;
description = mdDoc ''
DNS providers list.
The default value is extracted from [the pihole installation
script](https://github.com/pi-hole/pi-hole/blob/6a45c6a8e027e1ac30d4556a88f31684bc80ccf1/advanced/Scripts/piholeLogFlush.sh#L45-L53).
'';
default = ''
Google (ECS);8.8.8.8;8.8.4.4;2001:4860:4860:0:0:0:0:8888;2001:4860:4860:0:0:0:0:8844
OpenDNS (ECS, DNSSEC);208.67.222.222;208.67.220.220;2620:119:35::35;2620:119:53::53
Level3;4.2.2.1;4.2.2.2;;
Comodo;8.26.56.26;8.20.247.20;;
DNS.WATCH;84.200.69.80;84.200.70.40;2001:1608:10:25:0:0:1c04:b12f;2001:1608:10:25:0:0:9249:d69b
Quad9 (filtered, DNSSEC);9.9.9.9;149.112.112.112;2620:fe::fe;2620:fe::9
Quad9 (unfiltered, no DNSSEC);9.9.9.10;149.112.112.10;2620:fe::10;2620:fe::fe:10
Quad9 (filtered + ECS);9.9.9.11;149.112.112.11;2620:fe::11;2620:fe::fe:11
Cloudflare;1.1.1.1;1.0.0.1;2606:4700:4700::1111;2606:4700:4700::1001
'';
};
theme = mkOption {
type = types.enum [ "default-light" "default-dark" "default-darker" "default-auto" "lcars" ];
description = mdDoc "Website theme";
default = "default-light";
example = "default-dark";
};
temperatureUnit = mkOption {
type = types.enum [ "C" "F" ];
description = mdDoc "Temperature display unit";
default = "C";
example = "F";
};
enablePolkitRule = mkOption {
type = types.bool;
description = mdDoc ''
Enable a Polkit rule which allows users to restart the pihole-FTL daemon
from the Update Gravity webpage.
'';
default = true;
};
};

config = lib.mkIf cfg.enable {
services.phpfpm.pools.pihole = {
user = ftlCfg.user;
group = ftlCfg.group;
phpPackage = pkgs.php;
settings = lib.mapAttrs (name: lib.mkDefault) {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
"pm" = "ondemand";
"pm.max_children" = 5;
};
};

services.nginx.virtualHosts.${cfg.hostName} = {
root = "${cfg.package}/share";
locations = {
"/".index = "index.php";
"~ \\.php$".extraConfig = ''
include ${config.services.nginx.package}/conf/fastcgi.conf;
fastcgi_param SERVER_NAME $host;
fastcgi_pass unix:${config.services.phpfpm.pools.pihole.socket};
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
'';
"= /favicon.ico".extraConfig = "access_log off; log_not_found off;";
};

# Some inline styles and scripts require the unsafe-inline CSP
# See https://github.com/pi-hole/web/pull/1377
extraConfig = ''
add_header X-Pi-hole "The Pi-hole Web interface is working!";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "0";
add_header X-Content-Type-Options "nosniff";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
add_header X-Permitted-Cross-Domain-Policies "none";
add_header Referrer-Policy "same-origin";
'';
};

# The Update Gravity page requires writing to pihole's state directory
systemd.services.phpfpm-pihole.serviceConfig = {
ReadWritePaths = [ ftlCfg.stateDirectory ftlCfg.logDirectory ];
};

environment.etc."pihole/dns-servers.conf" = {
user = ftlCfg.user;
group = ftlCfg.group;
text = cfg.dnsServers;
};

# The Update Gravity page needs to restart pihole-ftl
security.polkit.extraConfig = lib.mkIf cfg.enablePolkitRule ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "pihole-ftl.service" &&
action.lookup("verb") == "restart" &&
subject.user == "${ftlCfg.user}") {
return polkit.Result.YES;
}
});
'';
};

meta = {
doc = ./pihole-web.md;
maintainers = with lib.maintainers; [ williamvds ];
};
}

0 comments on commit 3ec0d54

Please sign in to comment.