kvtb
May 26, 2020, 2:06pm
1
Q: how can I extend/adjust an existing NixOS service in a nice way without copying the complete service?
Background:
I’d like to install the unbound DNS service
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.unbound;
stateDir = "/var/lib/unbound";
access = concatMapStringsSep "\n " (x: "access-control: ${x} allow") cfg.allowedAccess;
interfaces = concatMapStringsSep "\n " (x: "interface: ${x}") cfg.interfaces;
isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1";
forward =
optionalString (any isLocalAddress cfg.forwardAddresses) ''
do-not-query-localhost: no
'' +
This file has been truncated. show original
and extend it with ad blocking.
Ad blocking basically means a block file is downloaded and converted to unbound local-zones, like this:
blocklists=$(cat <<'EOF'
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
EOF
)
echo "$blocklists" | xargs curl -o - > "$tmpfile"
awk '{sub(/\r$/,"")} {sub(/^127\.0\.0\.1/,"0.0.0.0")} BEGIN { OFS = "" } NF == 2 && $1 == "0.0.0.0" { print "local-zone: \"", $2, "\" static"}' "$tmpfile" | tr '[:upper:]' '[:lower:]' | sort -u > ${adblock}
and then including the output in the unbound configuration file (directly or using an include directive)
How can I do this in a nice way?
kvtb
May 27, 2020, 7:18pm
2
In fact there are two questions here:
How can I extend an existing attribute. Answer : use mkBefore or mkAfter to prepend/append text
how can I download something from the internet and feed it to the configuration. Answer: two possibilities:
make a separate package (derivation) for the blocklist and store it in the Nix store, then refer to it from the configuration. Disadvantage: needs manual update
create a systemd daemon to fetch it and reload the unbound service. Disadvantage: I don’t know how to do this in a reliable way (corner cases)
kvtb
May 28, 2020, 2:25pm
3
I came up with:
{ pkgs, lib, config, ... }:
let
stateDir = "/var/lib/unbound";
adblockLocalZones = pkgs.stdenv.mkDerivation {
name = "adblock";
src = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
sha256 =
"00ce3ee9dc806611de2ea7eb81b51bfb057457e53b770aa43daa62b5144596e6";
};
dontUnpack = true;
buildPhase = ''
cat $src | ${pkgs.gawk}/bin/awk '{sub(/\r$/,"")} {sub(/^127\.0\.0\.1/,"0.0.0.0")} BEGIN { OFS = "" } NF == 2 && $1 == "0.0.0.0" { print "local-zone: \"", $2, "\" static"}' | tr '[:upper:]' '[:lower:]' | sort -u > zones
'';
installPhase = ''
mv zones $out
'';
};
in {
networking.firewall.allowedUDPPorts = [ 53 ];
networking.firewall.allowedTCPPorts = [ 53 ];
services.unbound = {
enable = true;
allowedAccess = [ "127.0.0.0/8" "192.168.0.0/16" ];
interfaces = [ "0.0.0.0" ];
forwardAddresses =
[ "1.0.0.1@853#cloudflare-dns.com" "1.1.1.1@853#cloudflare-dns.com" ];
extraConfig = ''
so-reuseport: yes
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
tls-upstream: yes
include: "${adblockLocalZones}"
'';
};
}
it works, but I’m not excited about it, everytime the hosts file is updated, I have to manually change the sha256.