Extend a NixOS service? (add adblock to unbound)

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

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?

In fact there are two questions here:

  1. How can I extend an existing attribute. Answer: use mkBefore or mkAfter to prepend/append text
  2. how can I download something from the internet and feed it to the configuration. Answer: two possibilities:
    1. 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
    2. 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)

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.