mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-10 19:55:41 +03:00
pihole: init at various (#361571)
Adds pihole-ftl.service and pihole-ftl-log-deleter.service. Authored-By: williamvds <william@williamvds.me>
This commit is contained in:
commit
8922d4f099
17 changed files with 2596 additions and 1 deletions
|
@ -56,6 +56,12 @@
|
||||||
"module-services-opencloud-basic-usage": [
|
"module-services-opencloud-basic-usage": [
|
||||||
"index.html#module-services-opencloud-basic-usage"
|
"index.html#module-services-opencloud-basic-usage"
|
||||||
],
|
],
|
||||||
|
"module-services-networking-pihole-ftl-configuration-inherit-dnsmasq": [
|
||||||
|
"index.html#module-services-networking-pihole-ftl-configuration-inherit-dnsmasq"
|
||||||
|
],
|
||||||
|
"module-services-networking-pihole-ftl-configuration-multiple-interfaces": [
|
||||||
|
"index.html#module-services-networking-pihole-ftl-configuration-multiple-interfaces"
|
||||||
|
],
|
||||||
"module-services-strfry": [
|
"module-services-strfry": [
|
||||||
"index.html#module-services-strfry"
|
"index.html#module-services-strfry"
|
||||||
],
|
],
|
||||||
|
@ -1448,6 +1454,21 @@
|
||||||
"module-services-input-methods-kime": [
|
"module-services-input-methods-kime": [
|
||||||
"index.html#module-services-input-methods-kime"
|
"index.html#module-services-input-methods-kime"
|
||||||
],
|
],
|
||||||
|
"module-services-networking-pihole-ftl": [
|
||||||
|
"index.html#module-services-networking-pihole-ftl"
|
||||||
|
],
|
||||||
|
"module-services-networking-pihole-ftl-administration": [
|
||||||
|
"index.html#module-services-networking-pihole-ftl-administration"
|
||||||
|
],
|
||||||
|
"module-services-networking-pihole-ftl-configuration": [
|
||||||
|
"index.html#module-services-networking-pihole-ftl-configuration"
|
||||||
|
],
|
||||||
|
"module-services-web-apps-pihole-web": [
|
||||||
|
"index.html#module-services-web-apps-pihole-web"
|
||||||
|
],
|
||||||
|
"module-services-web-apps-pihole-web-configuration": [
|
||||||
|
"index.html#module-services-web-apps-pihole-web-configuration"
|
||||||
|
],
|
||||||
"ch-profiles": [
|
"ch-profiles": [
|
||||||
"index.html#ch-profiles"
|
"index.html#ch-profiles"
|
||||||
],
|
],
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
- [gtklock](https://github.com/jovanlanik/gtklock), a GTK-based lockscreen for Wayland. Available as [programs.gtklock](#opt-programs.gtklock.enable).
|
- [gtklock](https://github.com/jovanlanik/gtklock), a GTK-based lockscreen for Wayland. Available as [programs.gtklock](#opt-programs.gtklock.enable).
|
||||||
- [Chrysalis](https://github.com/keyboardio/Chrysalis), a graphical configurator for Kaleidoscope-powered keyboards. Available as [programs.chrysalis](#opt-programs.chrysalis.enable).
|
- [Chrysalis](https://github.com/keyboardio/Chrysalis), a graphical configurator for Kaleidoscope-powered keyboards. Available as [programs.chrysalis](#opt-programs.chrysalis.enable).
|
||||||
|
|
||||||
|
- [Pi-hole](https://pi-hole.net/), a DNS sinkhole for advertisements based on Dnsmasq. Available as [services.pihole-ftl](#opt-services.pihole-ftl.enable), and [services.pihole-web](#opt-services.pihole-web.enable) for the web GUI and API.
|
||||||
|
|
||||||
- [FileBrowser](https://filebrowser.org/), a web application for managing and sharing files. Available as [services.filebrowser](#opt-services.filebrowser.enable).
|
- [FileBrowser](https://filebrowser.org/), a web application for managing and sharing files. Available as [services.filebrowser](#opt-services.filebrowser.enable).
|
||||||
|
|
||||||
- [LACT](https://github.com/ilya-zlobintsev/LACT), a GPU monitoring and configuration tool, can now be enabled through [services.lact.enable](#opt-services.lact.enable).
|
- [LACT](https://github.com/ilya-zlobintsev/LACT), a GPU monitoring and configuration tool, can now be enabled through [services.lact.enable](#opt-services.lact.enable).
|
||||||
|
|
|
@ -1268,6 +1268,7 @@
|
||||||
./services/networking/pdnsd.nix
|
./services/networking/pdnsd.nix
|
||||||
./services/networking/peroxide.nix
|
./services/networking/peroxide.nix
|
||||||
./services/networking/picosnitch.nix
|
./services/networking/picosnitch.nix
|
||||||
|
./services/networking/pihole-ftl.nix
|
||||||
./services/networking/pixiecore.nix
|
./services/networking/pixiecore.nix
|
||||||
./services/networking/pleroma.nix
|
./services/networking/pleroma.nix
|
||||||
./services/networking/powerdns.nix
|
./services/networking/powerdns.nix
|
||||||
|
@ -1629,6 +1630,7 @@
|
||||||
./services/web-apps/photoprism.nix
|
./services/web-apps/photoprism.nix
|
||||||
./services/web-apps/phylactery.nix
|
./services/web-apps/phylactery.nix
|
||||||
./services/web-apps/pict-rs.nix
|
./services/web-apps/pict-rs.nix
|
||||||
|
./services/web-apps/pihole-web.nix
|
||||||
./services/web-apps/pingvin-share.nix
|
./services/web-apps/pingvin-share.nix
|
||||||
./services/web-apps/pixelfed.nix
|
./services/web-apps/pixelfed.nix
|
||||||
./services/web-apps/plantuml-server.nix
|
./services/web-apps/plantuml-server.nix
|
||||||
|
|
|
@ -115,6 +115,12 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
configFile = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = dnsmasqConf;
|
||||||
|
internal = true;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -172,7 +178,7 @@ in
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "dbus";
|
Type = "dbus";
|
||||||
BusName = "uk.org.thekelleys.dnsmasq";
|
BusName = "uk.org.thekelleys.dnsmasq";
|
||||||
ExecStart = "${dnsmasq}/bin/dnsmasq -k --enable-dbus --user=dnsmasq -C ${dnsmasqConf}";
|
ExecStart = "${dnsmasq}/bin/dnsmasq -k --enable-dbus --user=dnsmasq -C ${cfg.configFile}";
|
||||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
PrivateTmp = true;
|
PrivateTmp = true;
|
||||||
ProtectSystem = true;
|
ProtectSystem = true;
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
cfg,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
pihole = pkgs.pihole;
|
||||||
|
makePayload =
|
||||||
|
list:
|
||||||
|
builtins.toJSON {
|
||||||
|
inherit (list) type enabled;
|
||||||
|
address = list.url;
|
||||||
|
comment = list.description;
|
||||||
|
};
|
||||||
|
payloads = map makePayload cfg.lists;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
# Can't use -u (unset) because api.sh uses API_URL before it is set
|
||||||
|
set -eo pipefail
|
||||||
|
pihole="${lib.getExe pihole}"
|
||||||
|
jq="${lib.getExe pkgs.jq}"
|
||||||
|
|
||||||
|
# If the database doesn't exist, it needs to be created with gravity.sh
|
||||||
|
if [ ! -f '${cfg.stateDirectory}'/gravity.db ]; then
|
||||||
|
$pihole -g
|
||||||
|
# Send SIGRTMIN to FTL, which makes it reload the database, opening the newly created one
|
||||||
|
${pkgs.procps}/bin/kill -s SIGRTMIN $(systemctl show --property MainPID --value ${config.systemd.services.pihole-ftl.name})
|
||||||
|
fi
|
||||||
|
|
||||||
|
source ${pihole}/usr/share/pihole/advanced/Scripts/api.sh
|
||||||
|
source ${pihole}/usr/share/pihole/advanced/Scripts/utils.sh
|
||||||
|
|
||||||
|
any_failed=0
|
||||||
|
|
||||||
|
addList() {
|
||||||
|
local payload="$1"
|
||||||
|
|
||||||
|
echo "Adding list: $payload"
|
||||||
|
local result=$(PostFTLData "lists" "$payload")
|
||||||
|
|
||||||
|
local error="$($jq '.error' <<< "$result")"
|
||||||
|
if [[ "$error" != "null" ]]; then
|
||||||
|
echo "Error: $error"
|
||||||
|
any_failed=1
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
id="$($jq '.lists.[].id?' <<< "$result")"
|
||||||
|
if [[ "$id" == "null" ]]; then
|
||||||
|
any_failed=1
|
||||||
|
error="$($jq '.processed.errors.[].error' <<< "$result")"
|
||||||
|
echo "Error: $error"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Added list ID $id: $result"
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 1 2 3; do
|
||||||
|
(TestAPIAvailability) && break
|
||||||
|
echo "Retrying API shortly..."
|
||||||
|
${pkgs.coreutils}/bin/sleep .5s
|
||||||
|
done;
|
||||||
|
|
||||||
|
LoginAPI
|
||||||
|
|
||||||
|
${builtins.concatStringsSep "\n" (
|
||||||
|
map (
|
||||||
|
payload:
|
||||||
|
lib.pipe payload [
|
||||||
|
lib.strings.escapeShellArg
|
||||||
|
(payload: "addList ${payload}")
|
||||||
|
]
|
||||||
|
) payloads
|
||||||
|
)}
|
||||||
|
|
||||||
|
# Run gravity.sh to load any new lists
|
||||||
|
$pihole -g
|
||||||
|
exit $any_failed
|
||||||
|
''
|
128
nixos/modules/services/networking/pihole-ftl.md
Normal file
128
nixos/modules/services/networking/pihole-ftl.md
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
# pihole-FTL {#module-services-networking-pihole-ftl}
|
||||||
|
|
||||||
|
*Upstream documentation*: <https://docs.pi-hole.net/ftldns/>
|
||||||
|
|
||||||
|
pihole-FTL is a fork of [Dnsmasq](index.html#module-services-networking-dnsmasq),
|
||||||
|
providing some additional features, including an API for analysis and
|
||||||
|
statistics.
|
||||||
|
|
||||||
|
Note that pihole-FTL and Dnsmasq cannot be enabled at
|
||||||
|
the same time.
|
||||||
|
|
||||||
|
## Configuration {#module-services-networking-pihole-ftl-configuration}
|
||||||
|
|
||||||
|
pihole-FTL can be configured with [{option}`services.pihole-ftl.settings`](options.html#opt-services.pihole-ftl.settings), which controls the content of `pihole.toml`.
|
||||||
|
|
||||||
|
The template pihole.toml is provided in `pihole-ftl.passthru.settingsTemplate`,
|
||||||
|
which describes all settings.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
services.pihole-ftl = {
|
||||||
|
enable = true;
|
||||||
|
openFirewallDHCP = true;
|
||||||
|
queryLogDeleter.enable = true;
|
||||||
|
lists = [
|
||||||
|
{
|
||||||
|
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
|
||||||
|
# Alternatively, use the file from nixpkgs. Note its contents won't be
|
||||||
|
# automatically updated by Pi-hole, as it would with an online URL.
|
||||||
|
# url = "file://${pkgs.stevenblack-blocklist}/hosts";
|
||||||
|
description = "Steven Black's unified adlist";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
settings = {
|
||||||
|
dns = {
|
||||||
|
domainNeeded = true;
|
||||||
|
expandHosts = true;
|
||||||
|
interface = "br-lan";
|
||||||
|
listeningMode = "BIND";
|
||||||
|
upstreams = [ "127.0.0.1#5053" ];
|
||||||
|
};
|
||||||
|
dhcp = {
|
||||||
|
active = true;
|
||||||
|
router = "192.168.10.1";
|
||||||
|
start = "192.168.10.2";
|
||||||
|
end = "192.168.10.254";
|
||||||
|
leaseTime = "1d";
|
||||||
|
ipv6 = true;
|
||||||
|
multiDNS = true;
|
||||||
|
hosts = [
|
||||||
|
# Static address for the current host
|
||||||
|
"aa:bb:cc:dd:ee:ff,192.168.10.1,${config.networking.hostName},infinite"
|
||||||
|
];
|
||||||
|
rapidCommit = true;
|
||||||
|
};
|
||||||
|
misc.dnsmasq_lines = [
|
||||||
|
# This DHCP server is the only one on the network
|
||||||
|
"dhcp-authoritative"
|
||||||
|
# Source: https://data.iana.org/root-anchors/root-anchors.xml
|
||||||
|
"trust-anchor=.,38696,8,2,683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inheriting configuration from Dnsmasq {#module-services-networking-pihole-ftl-configuration-inherit-dnsmasq}
|
||||||
|
|
||||||
|
If [{option}`services.pihole-ftl.useDnsmasqConfig`](options.html#opt-services.pihole-ftl.useDnsmasqConfig) is enabled, the configuration [options of the Dnsmasq
|
||||||
|
module](index.html#module-services-networking-dnsmasq) will be automatically
|
||||||
|
used by pihole-FTL. Note that this may cause duplicate option errors
|
||||||
|
depending on pihole-FTL settings.
|
||||||
|
|
||||||
|
See the [Dnsmasq
|
||||||
|
example](index.html#module-services-networking-dnsmasq-configuration-home) for
|
||||||
|
an exemplar Dnsmasq configuration. Make sure to set
|
||||||
|
[{option}`services.dnsmasq.enable`](options.html#opt-services.dnsmasq.enable) to false and
|
||||||
|
[{option}`services.pihole-ftl.enable`](options.html#opt-services.pihole-ftl.enable) to true instead:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
services.pihole-ftl = {
|
||||||
|
enable = true;
|
||||||
|
useDnsmasqConfig = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Serving on multiple interfaces {#module-services-networking-pihole-ftl-configuration-multiple-interfaces}
|
||||||
|
|
||||||
|
Pi-hole's configuration only supports specifying a single interface. If you want
|
||||||
|
to configure additional interfaces with different configuration, use
|
||||||
|
`misc.dnsmasq_lines` to append extra Dnsmasq options.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
services.pihole-ftl = {
|
||||||
|
settings.misc.dnsmasq_lines = [
|
||||||
|
# Specify the secondary interface
|
||||||
|
"interface=enp1s0"
|
||||||
|
# A different device is the router on this network, e.g. the one
|
||||||
|
# provided by your ISP
|
||||||
|
"dhcp-option=enp1s0,option:router,192.168.0.1"
|
||||||
|
# Specify the IPv4 ranges to allocate, with a 1-day lease time
|
||||||
|
"dhcp-range=enp1s0,192.168.0.10,192.168.0.253,1d"
|
||||||
|
# Enable IPv6
|
||||||
|
"dhcp-range=::f,::ff,constructor:enp1s0,ra-names,ra-stateless"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Administration {#module-services-networking-pihole-ftl-administration}
|
||||||
|
|
||||||
|
*pihole command documentation*: <https://docs.pi-hole.net/main/pihole-command>
|
||||||
|
|
||||||
|
Enabling pihole-FTL provides the `pihole` command, which can be used to control
|
||||||
|
the daemon and some configuration.
|
||||||
|
|
||||||
|
Note that in NixOS the script has been patched to remove the reinstallation,
|
||||||
|
update, and Dnsmasq configuration commands. In NixOS, Pi-hole's configuration is
|
||||||
|
immutable and must be done with NixOS options.
|
||||||
|
|
||||||
|
For more convenient administration and monitoring, see [Pi-hole
|
||||||
|
Dashboard](#module-services-web-apps-pihole-web)
|
483
nixos/modules/services/networking/pihole-ftl.nix
Normal file
483
nixos/modules/services/networking/pihole-ftl.nix
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
with {
|
||||||
|
inherit (lib)
|
||||||
|
elemAt
|
||||||
|
getExe
|
||||||
|
hasAttrByPath
|
||||||
|
mkEnableOption
|
||||||
|
mkIf
|
||||||
|
mkOption
|
||||||
|
strings
|
||||||
|
types
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
let
|
||||||
|
mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
|
||||||
|
|
||||||
|
cfg = config.services.pihole-ftl;
|
||||||
|
|
||||||
|
piholeScript = pkgs.writeScriptBin "pihole" ''
|
||||||
|
sudo=exec
|
||||||
|
if [[ "$USER" != '${cfg.user}' ]]; then
|
||||||
|
sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
|
||||||
|
fi
|
||||||
|
$sudo ${getExe cfg.piholePackage} "$@"
|
||||||
|
'';
|
||||||
|
|
||||||
|
settingsFormat = pkgs.formats.toml { };
|
||||||
|
settingsFile = settingsFormat.generate "pihole.toml" cfg.settings;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.pihole-ftl = {
|
||||||
|
enable = mkEnableOption "Pi-hole FTL";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "pihole-ftl" { };
|
||||||
|
piholePackage = lib.mkPackageOption pkgs "pihole" { };
|
||||||
|
|
||||||
|
privacyLevel = mkOption {
|
||||||
|
type = types.numbers.between 0 3;
|
||||||
|
description = ''
|
||||||
|
Level of detail in generated statistics. 0 enables full statistics, 3
|
||||||
|
shows only anonymous statistics.
|
||||||
|
|
||||||
|
See [the documentation](https://docs.pi-hole.net/ftldns/privacylevels).
|
||||||
|
|
||||||
|
Also see services.dnsmasq.settings.log-queries to completely disable
|
||||||
|
query logging.
|
||||||
|
'';
|
||||||
|
default = 0;
|
||||||
|
example = "3";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewallDHCP = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Open ports in the firewall for pihole-FTL's DHCP server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewallWebserver = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Open ports in the firewall for pihole-FTL's webserver, as configured in `settings.webserver.port`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
configDirectory = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/etc/pihole";
|
||||||
|
internal = true;
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
Path for pihole configuration.
|
||||||
|
pihole does not currently support any path other than /etc/pihole.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDirectory = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/pihole";
|
||||||
|
description = ''
|
||||||
|
Path for pihole state files.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
logDirectory = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/log/pihole";
|
||||||
|
description = "Path for Pi-hole log files";
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = settingsFormat.type;
|
||||||
|
description = ''
|
||||||
|
Configuration options for pihole.toml.
|
||||||
|
See the upstream [documentation](https://docs.pi-hole.net/ftldns/configfile).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
useDnsmasqConfig = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Import options defined in [](#opt-services.dnsmasq.settings) via
|
||||||
|
misc.dnsmasq_lines in Pi-hole's config.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
pihole = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
default = piholeScript;
|
||||||
|
internal = true;
|
||||||
|
description = "Pi-hole admin script";
|
||||||
|
};
|
||||||
|
|
||||||
|
lists =
|
||||||
|
let
|
||||||
|
adlistType = types.submodule {
|
||||||
|
options = {
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "URL of the domain list";
|
||||||
|
};
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [
|
||||||
|
"allow"
|
||||||
|
"block"
|
||||||
|
];
|
||||||
|
default = "block";
|
||||||
|
description = "Whether domains on this list should be explicitly allowed, or blocked";
|
||||||
|
};
|
||||||
|
enabled = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Whether this list is enabled";
|
||||||
|
};
|
||||||
|
description = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Description of the list";
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
mkOption {
|
||||||
|
type = with types; listOf adlistType;
|
||||||
|
description = "Deny (or allow) domain lists to use";
|
||||||
|
default = [ ];
|
||||||
|
example = [
|
||||||
|
{
|
||||||
|
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "pihole";
|
||||||
|
description = "User to run the service as.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "pihole";
|
||||||
|
description = "Group to run the service as.";
|
||||||
|
};
|
||||||
|
|
||||||
|
queryLogDeleter = {
|
||||||
|
enable = mkEnableOption ("Pi-hole FTL DNS query log deleter");
|
||||||
|
|
||||||
|
age = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 90;
|
||||||
|
description = ''
|
||||||
|
Delete DNS query logs older than this many days, if
|
||||||
|
[](#opt-services.pihole-ftl.queryLogDeleter.enable) is on.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
interval = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "weekly";
|
||||||
|
description = ''
|
||||||
|
How often the query log deleter is run. See systemd.time(7) for more
|
||||||
|
information about the format.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !config.services.dnsmasq.enable;
|
||||||
|
message = "pihole-ftl conflicts with dnsmasq. Please disable one of them.";
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
builtins.length cfg.lists == 0
|
||||||
|
|| (
|
||||||
|
(hasAttrByPath [ "webserver" "port" ] cfg.settings)
|
||||||
|
&& !builtins.elem cfg.settings.webserver.port [
|
||||||
|
""
|
||||||
|
null
|
||||||
|
]
|
||||||
|
);
|
||||||
|
message = ''
|
||||||
|
The Pi-hole webserver must be enabled for lists set in services.pihole-ftl.lists to be automatically loaded on startup via the web API.
|
||||||
|
services.pihole-ftl.settings.port must be defined, e.g. by enabling services.pihole-web.enable and defining services.pihole-web.port.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
builtins.length cfg.lists == 0
|
||||||
|
|| !(hasAttrByPath [ "webserver" "api" "cli_pw" ] cfg.settings)
|
||||||
|
|| cfg.settings.webserver.api.cli_pw == true;
|
||||||
|
message = ''
|
||||||
|
services.pihole-ftl.settings.webserver.api.cli_pw must be true for lists set in services.pihole-ftl.lists to be automatically loaded on startup.
|
||||||
|
This enables an ephemeral password used by the pihole command.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
services.pihole-ftl.settings = lib.mkMerge [
|
||||||
|
# Defaults
|
||||||
|
(mkDefaults {
|
||||||
|
misc.readOnly = true; # Prevent config changes via API or CLI by default
|
||||||
|
webserver.port = ""; # Disable the webserver by default
|
||||||
|
misc.privacyLevel = cfg.privacyLevel;
|
||||||
|
})
|
||||||
|
|
||||||
|
# Move state files to cfg.stateDirectory
|
||||||
|
{
|
||||||
|
# TODO: Pi-hole currently hardcodes dhcp-leasefile this in its
|
||||||
|
# generated dnsmasq.conf, and we can't override it
|
||||||
|
misc.dnsmasq_lines = [
|
||||||
|
# "dhcp-leasefile=${cfg.stateDirectory}/dhcp.leases"
|
||||||
|
# "hostsdir=${cfg.stateDirectory}/hosts"
|
||||||
|
];
|
||||||
|
|
||||||
|
files = {
|
||||||
|
database = "${cfg.stateDirectory}/pihole-FTL.db";
|
||||||
|
gravity = "${cfg.stateDirectory}/gravity.db";
|
||||||
|
macvendor = "${cfg.stateDirectory}/gravity.db";
|
||||||
|
log.ftl = "${cfg.logDirectory}/FTL.log";
|
||||||
|
log.dnsmasq = "${cfg.logDirectory}/pihole.log";
|
||||||
|
log.webserver = "${cfg.logDirectory}/webserver.log";
|
||||||
|
};
|
||||||
|
|
||||||
|
webserver.tls = "${cfg.stateDirectory}/tls.pem";
|
||||||
|
}
|
||||||
|
|
||||||
|
(lib.optionalAttrs cfg.useDnsmasqConfig {
|
||||||
|
misc.dnsmasq_lines = lib.pipe config.services.dnsmasq.configFile [
|
||||||
|
builtins.readFile
|
||||||
|
(lib.strings.splitString "\n")
|
||||||
|
(builtins.filter (s: s != ""))
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.configDirectory} 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
|
"d ${cfg.stateDirectory} 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
|
"d ${cfg.logDirectory} 0700 ${cfg.user} ${cfg.group} - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services = {
|
||||||
|
pihole-ftl =
|
||||||
|
let
|
||||||
|
setupService = config.systemd.services.pihole-ftl-setup.name;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
description = "Pi-hole FTL";
|
||||||
|
|
||||||
|
after = [ "network.target" ];
|
||||||
|
before = [ setupService ];
|
||||||
|
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
wants = [ setupService ];
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
# Currently unused, but allows the service to be reloaded
|
||||||
|
# automatically when the config is changed.
|
||||||
|
PIHOLE_CONFIG = settingsFile;
|
||||||
|
|
||||||
|
# pihole is executed by the /actions/gravity API endpoint
|
||||||
|
PATH = lib.mkForce (
|
||||||
|
lib.makeBinPath [
|
||||||
|
cfg.piholePackage
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
AmbientCapabilities = [
|
||||||
|
"CAP_NET_BIND_SERVICE"
|
||||||
|
"CAP_NET_RAW"
|
||||||
|
"CAP_NET_ADMIN"
|
||||||
|
"CAP_SYS_NICE"
|
||||||
|
"CAP_IPC_LOCK"
|
||||||
|
"CAP_CHOWN"
|
||||||
|
"CAP_SYS_TIME"
|
||||||
|
];
|
||||||
|
ExecStart = "${getExe cfg.package} no-daemon";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 1;
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ReadWritePaths = [
|
||||||
|
cfg.configDirectory
|
||||||
|
cfg.stateDirectory
|
||||||
|
cfg.logDirectory
|
||||||
|
];
|
||||||
|
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pihole-ftl-setup = {
|
||||||
|
description = "Pi-hole FTL setup";
|
||||||
|
# Wait for network so lists can be downloaded
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
requires = [ "network-online.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ReadWritePaths = [
|
||||||
|
cfg.configDirectory
|
||||||
|
cfg.stateDirectory
|
||||||
|
cfg.logDirectory
|
||||||
|
];
|
||||||
|
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
};
|
||||||
|
script = import ./pihole-ftl-setup-script.nix {
|
||||||
|
inherit
|
||||||
|
cfg
|
||||||
|
config
|
||||||
|
lib
|
||||||
|
pkgs
|
||||||
|
;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pihole-ftl-log-deleter = mkIf cfg.queryLogDeleter.enable {
|
||||||
|
description = "Pi-hole FTL DNS query log deleter";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ReadWritePaths = [ cfg.stateDirectory ];
|
||||||
|
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
};
|
||||||
|
script =
|
||||||
|
let
|
||||||
|
days = toString cfg.queryLogDeleter.age;
|
||||||
|
database = "${cfg.stateDirectory}/pihole-FTL.db";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Deleting query logs older than ${days} days"
|
||||||
|
${getExe cfg.package} sqlite3 "${database}" "DELETE FROM query_storage WHERE timestamp <= CAST(strftime('%s', date('now', '-${days} day')) AS INT); select changes() from query_storage limit 1"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.pihole-ftl-log-deleter = mkIf cfg.queryLogDeleter.enable {
|
||||||
|
description = "Pi-hole FTL DNS query log deleter";
|
||||||
|
before = [
|
||||||
|
config.systemd.services.pihole-ftl.name
|
||||||
|
config.systemd.services.pihole-ftl-setup.name
|
||||||
|
];
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = cfg.queryLogDeleter.interval;
|
||||||
|
Unit = "pihole-ftl-log-deleter.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = lib.mkMerge [
|
||||||
|
(mkIf cfg.openFirewallDHCP {
|
||||||
|
allowedUDPPorts = [ 53 ];
|
||||||
|
allowedTCPPorts = [ 53 ];
|
||||||
|
})
|
||||||
|
|
||||||
|
(mkIf cfg.openFirewallWebserver {
|
||||||
|
allowedTCPPorts = lib.pipe cfg.settings.webserver.port [
|
||||||
|
(lib.splitString ",")
|
||||||
|
(map (
|
||||||
|
port:
|
||||||
|
lib.pipe port [
|
||||||
|
(builtins.split "[[:alpha:]]+")
|
||||||
|
builtins.head
|
||||||
|
lib.toInt
|
||||||
|
]
|
||||||
|
))
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.${cfg.user} = {
|
||||||
|
group = cfg.group;
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups.${cfg.group} = { };
|
||||||
|
|
||||||
|
environment.etc."pihole/pihole.toml" = {
|
||||||
|
source = settingsFile;
|
||||||
|
user = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
mode = "400";
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [ cfg.pihole ];
|
||||||
|
|
||||||
|
services.logrotate.settings.pihole-ftl = {
|
||||||
|
enable = true;
|
||||||
|
files = [ "${cfg.logDirectory}/FTL.log" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
doc = ./pihole-ftl.md;
|
||||||
|
maintainers = with lib.maintainers; [ williamvds ];
|
||||||
|
};
|
||||||
|
}
|
19
nixos/modules/services/web-apps/pihole-web.md
Normal file
19
nixos/modules/services/web-apps/pihole-web.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Pi-hole Web Dashboard {#module-services-web-apps-pihole-web}
|
||||||
|
|
||||||
|
The Pi-hole suite provides a web GUI for controlling and monitoring
|
||||||
|
[pihole-FTL](index.html#module-services-networking-pihole-ftl).
|
||||||
|
|
||||||
|
## Configuration {#module-services-web-apps-pihole-web-configuration}
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
services.pihole-web = {
|
||||||
|
enable = true;
|
||||||
|
ports = [ 80 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The dashboard can be configured using [{option}`services.pihole-ftl.settings`](options.html#opt-services.pihole-ftl.settings), in particular the `webserver` subsection.
|
104
nixos/modules/services/web-apps/pihole-web.nix
Normal file
104
nixos/modules/services/web-apps/pihole-web.nix
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.pihole-web;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.pihole-web = {
|
||||||
|
enable = lib.mkEnableOption "Pi-hole dashboard";
|
||||||
|
|
||||||
|
package = lib.mkPackageOption pkgs "pihole-web" { };
|
||||||
|
|
||||||
|
hostName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Domain name for the website.";
|
||||||
|
default = "pi.hole";
|
||||||
|
};
|
||||||
|
|
||||||
|
ports =
|
||||||
|
let
|
||||||
|
portType = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
description = "Port to bind";
|
||||||
|
};
|
||||||
|
optional = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Skip the port if it cannot be bound";
|
||||||
|
};
|
||||||
|
redirectSSL = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Redirect from this port to the first configured SSL port";
|
||||||
|
};
|
||||||
|
ssl = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Serve SSL on the port";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
lib.mkOption {
|
||||||
|
type = lib.types.listOf (
|
||||||
|
lib.types.oneOf [
|
||||||
|
lib.types.port
|
||||||
|
lib.types.str
|
||||||
|
portType
|
||||||
|
]
|
||||||
|
);
|
||||||
|
description = ''
|
||||||
|
Port(s) for the webserver to serve on.
|
||||||
|
|
||||||
|
If provided as a string, optionally append suffixes to control behaviour:
|
||||||
|
|
||||||
|
- `o`: to make the port is optional - failure to bind will not be an error.
|
||||||
|
- `s`: for the port to be used for SSL.
|
||||||
|
- `r`: for a non-SSL port to redirect to the first available SSL port.
|
||||||
|
'';
|
||||||
|
example = [
|
||||||
|
"80r"
|
||||||
|
"443s"
|
||||||
|
];
|
||||||
|
apply =
|
||||||
|
values:
|
||||||
|
let
|
||||||
|
convert =
|
||||||
|
value:
|
||||||
|
if (builtins.typeOf) value == "int" then
|
||||||
|
toString value
|
||||||
|
else if builtins.typeOf value == "set" then
|
||||||
|
lib.strings.concatStrings [
|
||||||
|
(toString value.port)
|
||||||
|
(lib.optionalString value.optional "o")
|
||||||
|
(lib.optionalString value.redirectSSL "r")
|
||||||
|
(lib.optionalString value.ssl "s")
|
||||||
|
]
|
||||||
|
else
|
||||||
|
value;
|
||||||
|
in
|
||||||
|
lib.strings.concatStringsSep "," (map convert values);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
services.pihole-ftl.settings.webserver = {
|
||||||
|
domain = cfg.hostName;
|
||||||
|
port = cfg.ports;
|
||||||
|
paths.webroot = "${cfg.package}/share/";
|
||||||
|
paths.webhome = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
doc = ./pihole-web.md;
|
||||||
|
maintainers = with lib.maintainers; [ williamvds ];
|
||||||
|
};
|
||||||
|
}
|
85
pkgs/by-name/pi/pihole-ftl/package.nix
Normal file
85
pkgs/by-name/pi/pihole-ftl/package.nix
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchFromGitHub,
|
||||||
|
cmake,
|
||||||
|
gmp,
|
||||||
|
libidn2,
|
||||||
|
libunistring,
|
||||||
|
mbedtls,
|
||||||
|
ncurses,
|
||||||
|
nettle,
|
||||||
|
readline,
|
||||||
|
xxd,
|
||||||
|
iproute2,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
|
pname = "pihole-ftl";
|
||||||
|
version = "6.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "pi-hole";
|
||||||
|
repo = "FTL";
|
||||||
|
tag = "v${finalAttrs.version}";
|
||||||
|
hash = "sha256-b3/kyDQa6qDK2avvDObWLvwUpAn6TFr1ZBdQC9AZWa4=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
cmake
|
||||||
|
xxd
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
gmp
|
||||||
|
libidn2
|
||||||
|
libunistring
|
||||||
|
mbedtls
|
||||||
|
ncurses
|
||||||
|
nettle
|
||||||
|
readline
|
||||||
|
];
|
||||||
|
|
||||||
|
cmakeFlags = [
|
||||||
|
(lib.cmakeBool "STATIC" stdenv.hostPlatform.isStatic)
|
||||||
|
];
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
substituteInPlace src/version.c.in \
|
||||||
|
--replace-quiet "@GIT_VERSION@" "v${finalAttrs.version}" \
|
||||||
|
--replace-quiet "@GIT_DATE@" "1970-01-01" \
|
||||||
|
--replace-quiet "@GIT_BRANCH@" "master" \
|
||||||
|
--replace-quiet "@GIT_TAG@" "v${finalAttrs.version}" \
|
||||||
|
--replace-quiet "@GIT_HASH@" "builtfromreleasetarball"
|
||||||
|
|
||||||
|
# Remove hard-coded absolute path to the pihole script, rely on it being provided by $PATH
|
||||||
|
# Use execvp instead of execv so PATH is followed
|
||||||
|
substituteInPlace src/api/action.c \
|
||||||
|
--replace-fail "/usr/local/bin/pihole" "pihole" \
|
||||||
|
--replace-fail "execv" "execvp"
|
||||||
|
|
||||||
|
substituteInPlace src/database/network-table.c \
|
||||||
|
--replace-fail "ip neigh show" "${iproute2}/bin/ip neigh show" \
|
||||||
|
--replace-fail "ip address show" "${iproute2}/bin/ip address show"
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
install -Dm 555 -t $out/bin pihole-FTL
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru.settingsTemplate = ./pihole.toml;
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Pi-hole FTL engine";
|
||||||
|
homepage = "https://github.com/pi-hole/FTL";
|
||||||
|
license = lib.licenses.eupl12;
|
||||||
|
maintainers = with lib.maintainers; [ williamvds ];
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
mainProgram = "pihole-FTL";
|
||||||
|
};
|
||||||
|
})
|
1194
pkgs/by-name/pi/pihole-ftl/pihole.toml
Normal file
1194
pkgs/by-name/pi/pihole-ftl/pihole.toml
Normal file
File diff suppressed because it is too large
Load diff
51
pkgs/by-name/pi/pihole-web/package.nix
Normal file
51
pkgs/by-name/pi/pihole-web/package.nix
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
stdenv,
|
||||||
|
lib,
|
||||||
|
fetchFromGitHub,
|
||||||
|
pihole,
|
||||||
|
pihole-ftl,
|
||||||
|
procps,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
|
pname = "pihole-web";
|
||||||
|
version = "6.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "pi-hole";
|
||||||
|
repo = "web";
|
||||||
|
tag = "v${finalAttrs.version}";
|
||||||
|
hash = "sha256-+h4cPDsTAKR8MM+Za0mp2nOX1cHW8LRlFmLqvrBHfbs=";
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = [
|
||||||
|
pihole
|
||||||
|
pihole-ftl
|
||||||
|
procps
|
||||||
|
];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/share
|
||||||
|
cp -r -t $out/share *.lp img/ scripts/ style/ vendor/
|
||||||
|
|
||||||
|
mkdir -p $out/share/doc/$name/
|
||||||
|
cp README.md $out/share/doc/$name/
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Pi-hole web dashboard displaying stats and more";
|
||||||
|
longDescription = ''
|
||||||
|
Pi-hole's Web interface (based off of AdminLTE) provides a central
|
||||||
|
location to manage your Pi-hole and review the statistics generated by
|
||||||
|
FTLDNS.
|
||||||
|
'';
|
||||||
|
license = lib.licenses.eupl12;
|
||||||
|
maintainers = with lib.maintainers; [ williamvds ];
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
};
|
||||||
|
})
|
32
pkgs/by-name/pi/pihole/0001-Remove-sudo.patch
Normal file
32
pkgs/by-name/pi/pihole/0001-Remove-sudo.patch
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
From a2b3aa45d6e073272608506b1d27e4f43f2b0032 Mon Sep 17 00:00:00 2001
|
||||||
|
From: williamvds <william@williamvds.me>
|
||||||
|
Date: Sun, 6 Apr 2025 23:00:41 +0100
|
||||||
|
Subject: [PATCH 1/3] Remove sudo
|
||||||
|
|
||||||
|
Rely on polkit and sensible permissions
|
||||||
|
---
|
||||||
|
pihole | 8 --------
|
||||||
|
1 file changed, 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/pihole b/pihole
|
||||||
|
index 1d5093c..6afc48a 100755
|
||||||
|
--- a/pihole
|
||||||
|
+++ b/pihole
|
||||||
|
@@ -570,14 +570,6 @@ if [[ -z ${USER} ]]; then
|
||||||
|
USER=$(whoami)
|
||||||
|
fi
|
||||||
|
|
||||||
|
-# Check if the current user is neither root nor pihole and if the command
|
||||||
|
-# requires root. If so, exit with an error message.
|
||||||
|
-if [[ $EUID -ne 0 && ${USER} != "pihole" && need_root -eq 1 ]];then
|
||||||
|
- echo -e " ${CROSS} The Pi-hole command requires root privileges, try:"
|
||||||
|
- echo -e " ${COL_GREEN}sudo pihole $*${COL_NC}"
|
||||||
|
- exit 1
|
||||||
|
-fi
|
||||||
|
-
|
||||||
|
# Handle redirecting to specific functions based on arguments
|
||||||
|
case "${1}" in
|
||||||
|
"allow" | "allowlist" ) listFunc "$@";;
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
From ab0650484cdd89afb5b60a0a046509ec5ae14375 Mon Sep 17 00:00:00 2001
|
||||||
|
From: williamvds <william@williamvds.me>
|
||||||
|
Date: Sun, 6 Apr 2025 23:01:30 +0100
|
||||||
|
Subject: [PATCH 2/3] Remove unsupported commands
|
||||||
|
|
||||||
|
Remove some unsupported maintenance commands, particularly the ones which
|
||||||
|
reinstall, update, and uninstall pihole. This is managed by NixOS, after all.
|
||||||
|
---
|
||||||
|
pihole | 9 +++++++--
|
||||||
|
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/pihole b/pihole
|
||||||
|
index 6afc48a..cce7c97 100755
|
||||||
|
--- a/pihole
|
||||||
|
+++ b/pihole
|
||||||
|
@@ -92,6 +92,7 @@ debugFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
flushFunc() {
|
||||||
|
+ unsupportedFunc
|
||||||
|
"${PI_HOLE_SCRIPT_DIR}"/piholeLogFlush.sh "$@"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
@@ -102,6 +103,7 @@ arpFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePiholeFunc() {
|
||||||
|
+ unsupportedFunc
|
||||||
|
if [ -n "${DOCKER_VERSION}" ]; then
|
||||||
|
unsupportedFunc
|
||||||
|
else
|
||||||
|
@@ -137,6 +139,7 @@ chronometerFunc() {
|
||||||
|
|
||||||
|
|
||||||
|
uninstallFunc() {
|
||||||
|
+ unsupportedFunc
|
||||||
|
if [ -n "${DOCKER_VERSION}" ]; then
|
||||||
|
unsupportedFunc
|
||||||
|
else
|
||||||
|
@@ -405,6 +408,7 @@ tailFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
piholeCheckoutFunc() {
|
||||||
|
+ unsupportedFunc
|
||||||
|
if [ -n "${DOCKER_VERSION}" ]; then
|
||||||
|
echo -e "${CROSS} Function not supported in Docker images"
|
||||||
|
echo "Please build a custom image following the steps at"
|
||||||
|
@@ -460,13 +464,14 @@ tricorderFunc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCheckFunc() {
|
||||||
|
+ unsupportedFunc
|
||||||
|
"${PI_HOLE_SCRIPT_DIR}"/updatecheck.sh "$@"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
unsupportedFunc(){
|
||||||
|
- echo "Function not supported in Docker images"
|
||||||
|
- exit 0
|
||||||
|
+ echo "Function not supported in NixOS"
|
||||||
|
+ exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
helpFunc() {
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
From cca2f6437e3ba09019b8fcb1986b4558d7c6db4e Mon Sep 17 00:00:00 2001
|
||||||
|
From: williamvds <william@williamvds.me>
|
||||||
|
Date: Sat, 31 May 2025 13:43:42 +0100
|
||||||
|
Subject: [PATCH 3/3] Fix redefinition of readonly variable utilsfile
|
||||||
|
|
||||||
|
---
|
||||||
|
advanced/Scripts/api.sh | 2 +-
|
||||||
|
pihole | 10 +++-------
|
||||||
|
2 files changed, 4 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/advanced/Scripts/api.sh b/advanced/Scripts/api.sh
|
||||||
|
index 613a8d8..8720043 100755
|
||||||
|
--- a/advanced/Scripts/api.sh
|
||||||
|
+++ b/advanced/Scripts/api.sh
|
||||||
|
@@ -19,7 +19,7 @@
|
||||||
|
|
||||||
|
TestAPIAvailability() {
|
||||||
|
|
||||||
|
- local chaos_api_list authResponse authStatus authData apiAvailable DNSport
|
||||||
|
+ local chaos_api_list authResponse authStatus authData apiAvailable DNSport utilsfile
|
||||||
|
|
||||||
|
# as we are running locally, we can get the port value from FTL directly
|
||||||
|
readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||||
|
diff --git a/pihole b/pihole
|
||||||
|
index cce7c97..d63d064 100755
|
||||||
|
--- a/pihole
|
||||||
|
+++ b/pihole
|
||||||
|
@@ -16,18 +16,14 @@ readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||||
|
# error due to modifying a readonly variable.
|
||||||
|
PI_HOLE_BIN_DIR="/usr/local/bin"
|
||||||
|
|
||||||
|
-readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
|
||||||
|
# shellcheck source=./advanced/Scripts/COL_TABLE
|
||||||
|
-source "${colfile}"
|
||||||
|
+source "${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
|
||||||
|
|
||||||
|
-readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||||
|
# shellcheck source=./advanced/Scripts/utils.sh
|
||||||
|
-source "${utilsfile}"
|
||||||
|
+source "${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||||
|
|
||||||
|
-# Source api functions
|
||||||
|
-readonly apifile="${PI_HOLE_SCRIPT_DIR}/api.sh"
|
||||||
|
# shellcheck source=./advanced/Scripts/api.sh
|
||||||
|
-source "${apifile}"
|
||||||
|
+source "${PI_HOLE_SCRIPT_DIR}/api.sh"
|
||||||
|
|
||||||
|
versionsfile="/etc/pihole/versions"
|
||||||
|
if [ -f "${versionsfile}" ]; then
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
257
pkgs/by-name/pi/pihole/package.nix
Normal file
257
pkgs/by-name/pi/pihole/package.nix
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
fetchFromGitHub,
|
||||||
|
makeBinaryWrapper,
|
||||||
|
installShellFiles,
|
||||||
|
bash,
|
||||||
|
coreutils,
|
||||||
|
curl,
|
||||||
|
dig,
|
||||||
|
gawk,
|
||||||
|
getent,
|
||||||
|
glibc,
|
||||||
|
gnugrep,
|
||||||
|
gnused,
|
||||||
|
iproute2,
|
||||||
|
jq,
|
||||||
|
killall,
|
||||||
|
libidn2,
|
||||||
|
locale,
|
||||||
|
ncurses,
|
||||||
|
netcat,
|
||||||
|
nettools,
|
||||||
|
pihole-ftl,
|
||||||
|
procps,
|
||||||
|
resholve,
|
||||||
|
sqlite,
|
||||||
|
systemd,
|
||||||
|
util-linux,
|
||||||
|
stateDir ? "/etc/pihole",
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
(resholve.mkDerivation rec {
|
||||||
|
pname = "pihole";
|
||||||
|
version = "6.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "pi-hole";
|
||||||
|
repo = "pi-hole";
|
||||||
|
tag = "v${version}";
|
||||||
|
hash = "sha256-aEnv8Lhb5vf0yDyuriVTaUY1wcdVmTdqoK+KDHvT/Lw=";
|
||||||
|
};
|
||||||
|
|
||||||
|
patches = [
|
||||||
|
# Remove use of sudo in the original script, prefer to use a wrapper
|
||||||
|
./0001-Remove-sudo.patch
|
||||||
|
# Disable unsupported subcommands, particularly those for imperatively installing/upgrading Pi-hole
|
||||||
|
./0002-Remove-unsupported-commands.patch
|
||||||
|
# Fix a readonly variable error caused by defining a shadowing local variable
|
||||||
|
./0003-Fix-redefinition-of-readonly-variable-utilsfile.patch
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
makeBinaryWrapper
|
||||||
|
installShellFiles
|
||||||
|
];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
readonly scriptsDir=$out/usr/share/pihole
|
||||||
|
|
||||||
|
install -Dm 555 -t $out/bin pihole
|
||||||
|
install -Dm 555 -t $scriptsDir/advanced/Scripts gravity.sh
|
||||||
|
|
||||||
|
# The installation script is sourced by advanced/Scripts/piholeARPTable.sh etc
|
||||||
|
cp --parents -r -t $scriptsDir/ 'automated install/' advanced/{Scripts,Templates}/
|
||||||
|
|
||||||
|
installShellCompletion --bash --name pihole.bash \
|
||||||
|
advanced/bash-completion/pihole
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
solutions.default =
|
||||||
|
let
|
||||||
|
out = builtins.placeholder "out";
|
||||||
|
scriptsDir = "${out}/usr/share/pihole/advanced/Scripts";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
scripts =
|
||||||
|
let
|
||||||
|
relativeScripts = "usr/share/pihole/advanced/Scripts";
|
||||||
|
in
|
||||||
|
[
|
||||||
|
"bin/pihole"
|
||||||
|
"${relativeScripts}/api.sh"
|
||||||
|
"${relativeScripts}/database_migration/gravity-db.sh"
|
||||||
|
"${relativeScripts}/gravity.sh"
|
||||||
|
"${relativeScripts}/list.sh"
|
||||||
|
"${relativeScripts}/piholeARPTable.sh"
|
||||||
|
"${relativeScripts}/piholeCheckout.sh"
|
||||||
|
"${relativeScripts}/piholeDebug.sh"
|
||||||
|
"${relativeScripts}/piholeLogFlush.sh"
|
||||||
|
"${relativeScripts}/query.sh"
|
||||||
|
"${relativeScripts}/update.sh"
|
||||||
|
"${relativeScripts}/updatecheck.sh"
|
||||||
|
"${relativeScripts}/utils.sh"
|
||||||
|
"${relativeScripts}/version.sh"
|
||||||
|
];
|
||||||
|
interpreter = lib.getExe bash;
|
||||||
|
inputs = [
|
||||||
|
# TODO: see if these inputs can help resholving
|
||||||
|
"bin"
|
||||||
|
"usr/share/pihole/advanced/Scripts"
|
||||||
|
|
||||||
|
bash
|
||||||
|
coreutils
|
||||||
|
curl
|
||||||
|
dig
|
||||||
|
gawk
|
||||||
|
getent
|
||||||
|
gnugrep
|
||||||
|
gnused
|
||||||
|
iproute2
|
||||||
|
jq
|
||||||
|
killall
|
||||||
|
libidn2
|
||||||
|
locale
|
||||||
|
ncurses
|
||||||
|
netcat
|
||||||
|
nettools
|
||||||
|
pihole-ftl
|
||||||
|
procps
|
||||||
|
sqlite
|
||||||
|
systemd
|
||||||
|
util-linux
|
||||||
|
];
|
||||||
|
fake = {
|
||||||
|
source = [
|
||||||
|
"/etc/os-release"
|
||||||
|
"/etc/pihole/versions"
|
||||||
|
"/etc/pihole/setupVars.conf"
|
||||||
|
];
|
||||||
|
external = [
|
||||||
|
# Used by chronometer.sh to get GPU information on Raspberry Pis
|
||||||
|
"sudo"
|
||||||
|
"vcgencmd"
|
||||||
|
# used by the checkout and update scripts, which are patched out
|
||||||
|
"git"
|
||||||
|
"getenforce"
|
||||||
|
"firewall-cmd"
|
||||||
|
# Conditionally used in Docker builds
|
||||||
|
"service"
|
||||||
|
"lighttpd"
|
||||||
|
# Used in piholeLogFlush.sh
|
||||||
|
"/usr/sbin/logrotate"
|
||||||
|
# Used by teleporter in webpage.sh
|
||||||
|
"php"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
fix = {
|
||||||
|
"$PI_HOLE_BIN_DIR" = [ "${out}/bin" ];
|
||||||
|
"$PI_HOLE_FILES_DIR" = [ "${out}/usr/share/pihole" ];
|
||||||
|
"$PI_HOLE_INSTALL_DIR" = [ scriptsDir ];
|
||||||
|
"$PI_HOLE_LOCAL_REPO" = [ "${out}/usr/share/pihole" ];
|
||||||
|
"$PI_HOLE_SCRIPT_DIR" = [ scriptsDir ];
|
||||||
|
"$colfile" = [ "${scriptsDir}/COL_TABLE" ];
|
||||||
|
"$coltable" = [ "${scriptsDir}/COL_TABLE" ];
|
||||||
|
"$PIHOLE_COLTABLE_FILE" = [ "${scriptsDir}/COL_TABLE" ];
|
||||||
|
"$utilsfile" = [ "${scriptsDir}/utils.sh" ];
|
||||||
|
"$apifile" = [ "${scriptsDir}/api.sh" ];
|
||||||
|
"$piholeGitDir" = [ "${out}/usr/share/pihole" ];
|
||||||
|
"$PIHOLE_COMMAND" = [ "pihole" ];
|
||||||
|
};
|
||||||
|
keep = {
|
||||||
|
source = [
|
||||||
|
"$pihole_FTL" # Global config file
|
||||||
|
"$setupVars" # Global config file
|
||||||
|
"$PIHOLE_SETUP_VARS_FILE"
|
||||||
|
"$versionsfile" # configuration file, doesn't exist on NixOS
|
||||||
|
"${out}/usr/share/pihole/automated install/basic-install.sh"
|
||||||
|
"${scriptsDir}/COL_TABLE"
|
||||||
|
"${scriptsDir}/database_migration/gravity-db.sh"
|
||||||
|
"${scriptsDir}/gravity.sh"
|
||||||
|
"${scriptsDir}/piholeCheckout.sh"
|
||||||
|
"${scriptsDir}/utils.sh"
|
||||||
|
"${scriptsDir}/api.sh"
|
||||||
|
"/etc/os-release"
|
||||||
|
"/etc/pihole/versions"
|
||||||
|
"/etc/pihole/setupVars.conf"
|
||||||
|
"$cachedVersions"
|
||||||
|
];
|
||||||
|
|
||||||
|
"$PIHOLE_SETUP_VARS_FILE" = true;
|
||||||
|
"$PKG_INSTALL" = true; # System package manager, patched out
|
||||||
|
"$PKG_MANAGER" = true; # System package manager, patched out
|
||||||
|
"$cmd" = true; # ping or ping6
|
||||||
|
"$program_name" = true; # alias for $1
|
||||||
|
"$svc" = true; # dynamic restart command
|
||||||
|
"${out}/bin/pihole" = true;
|
||||||
|
"${scriptsDir}/api.sh" = true;
|
||||||
|
"${scriptsDir}/gravity.sh" = true;
|
||||||
|
"${scriptsDir}/list.sh" = true;
|
||||||
|
"${scriptsDir}/piholeARPTable.sh" = true;
|
||||||
|
"${scriptsDir}/piholeDebug.sh" = true;
|
||||||
|
"${scriptsDir}/piholeLogFlush.sh" = true;
|
||||||
|
"${scriptsDir}/query.sh" = true;
|
||||||
|
"${scriptsDir}/uninstall.sh" = true;
|
||||||
|
"${scriptsDir}/update.sh" = true;
|
||||||
|
"${scriptsDir}/updatecheck.sh" = true;
|
||||||
|
"${scriptsDir}/version.sh" = true;
|
||||||
|
|
||||||
|
# boolean variables
|
||||||
|
"$addmode" = true;
|
||||||
|
"$noReloadRequested" = true;
|
||||||
|
"$oldAvail" = true;
|
||||||
|
"$verbose" = true;
|
||||||
|
"$web" = true;
|
||||||
|
"$wildcard" = true;
|
||||||
|
|
||||||
|
# Note that this path needs to be quoted due to the whitespace.
|
||||||
|
# TODO: raise upstream resholve issue. pihole scripts specify this path
|
||||||
|
# both quoted and escaped. Resholve apparently requires matching the
|
||||||
|
# literal path, so we need to provide a version with and without the
|
||||||
|
# backslash.
|
||||||
|
"'${out}/usr/share/pihole/automated\\ install/basic-install.sh'" = true;
|
||||||
|
"'${out}/usr/share/pihole/automated install/basic-install.sh'" = true;
|
||||||
|
|
||||||
|
"/etc/.pihole" = true; # Patched with an override
|
||||||
|
"/etc/os-release" = true;
|
||||||
|
"/etc/pihole/versions" = true;
|
||||||
|
"/etc/pihole/setupVars.conf" = true;
|
||||||
|
};
|
||||||
|
execer = [
|
||||||
|
"cannot:${pihole-ftl}/bin/pihole-FTL"
|
||||||
|
"cannot:${iproute2}/bin/ip"
|
||||||
|
"cannot:${systemd}/bin/systemctl"
|
||||||
|
"cannot:${glibc.bin}/bin/ldd"
|
||||||
|
"cannot:${out}/bin/pihole"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "A black hole for Internet advertisements";
|
||||||
|
license = lib.licenses.eupl12;
|
||||||
|
maintainers = with lib.maintainers; [ williamvds ];
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
mainProgram = "pihole";
|
||||||
|
};
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
stateDir = stateDir;
|
||||||
|
};
|
||||||
|
}).overrideAttrs
|
||||||
|
(old: {
|
||||||
|
# Resholve can't fix the hardcoded absolute paths, so substitute them before resholving
|
||||||
|
preFixup =
|
||||||
|
''
|
||||||
|
scriptsDir=$out/usr/share/pihole
|
||||||
|
|
||||||
|
substituteInPlace $out/bin/pihole $scriptsDir/advanced/Scripts/*.sh \
|
||||||
|
--replace-quiet /etc/.pihole $scriptsDir \
|
||||||
|
--replace-quiet /opt/pihole $scriptsDir/advanced/Scripts
|
||||||
|
''
|
||||||
|
+ old.preFixup;
|
||||||
|
})
|
10
sync.sh
Executable file
10
sync.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
local_root=~/.config/nix/
|
||||||
|
nixpkgs_root="$(dirname "$0")"
|
||||||
|
|
||||||
|
cp "$local_root"/modules/pihole-ftl* "$nixpkgs_root"/nixos/modules/services/networking/
|
||||||
|
cp "$local_root"/modules/pihole-web* "$nixpkgs_root"/nixos/modules/services/web-apps/
|
||||||
|
|
||||||
|
cp -r "$local_root"/pkgs/pihole* "$nixpkgs_root"/pkgs/by-name/pi/
|
||||||
|
cp -r "$local_root"/modules/pihole-web* "$nixpkgs_root"/nixos/modules/services/web-apps/
|
Loading…
Add table
Add a link
Reference in a new issue