171 lines
No EOL
5 KiB
Nix
171 lines
No EOL
5 KiB
Nix
# AdGuard Home Module
|
|
# Provides: Private DNS-over-HTTPS with ClientID-based access control
|
|
#
|
|
# Usage:
|
|
# myModules.adguard = {
|
|
# enable = true;
|
|
# domain = "dns.example.com";
|
|
# clients = [
|
|
# { name = "phone"; idSecret = "adguard_client_phone"; }
|
|
# ];
|
|
# };
|
|
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.myModules.adguard;
|
|
in
|
|
{
|
|
options.myModules.adguard = {
|
|
enable = lib.mkEnableOption "AdGuard Home DNS server";
|
|
|
|
domain = lib.mkOption {
|
|
type = lib.types.str;
|
|
example = "dns.example.com";
|
|
description = "Public domain name for DoH endpoint";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 3000;
|
|
description = "Internal port for AdGuard HTTP/DoH listener";
|
|
};
|
|
|
|
upstreamDoh = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "https://dns.mullvad.net/dns-query";
|
|
description = "Upstream DoH server URL";
|
|
};
|
|
|
|
bootstrapDns = lib.mkOption {
|
|
type = lib.types.listOf (lib.types.str);
|
|
default = [ "194.242.2.2" "2a07:e340::2" ];
|
|
description = "Bootstrap DNS servers for resolving DoH upstream";
|
|
};
|
|
|
|
filters = lib.mkOption {
|
|
type = lib.types.listOf (lib.types.submodule {
|
|
options = {
|
|
name = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Friendly name for the filter list";
|
|
};
|
|
url = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "URL of the filter list (txt format)";
|
|
};
|
|
enabled = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Whether the filter is enabled";
|
|
};
|
|
};
|
|
});
|
|
default = [
|
|
{ name = "AdGuard DNS filter"; url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt"; }
|
|
{ name = "AdAway Default Blocklist"; url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt"; }
|
|
{ name = "HaGeZi Multi Light"; url = "https://hagezi.github.io/dns-blocklists/wildcard/light.txt"; }
|
|
{ name = "OISD Basic"; url = "https://small.oisd.nl/"; }
|
|
{ name = "Peter Lowe's List"; url = "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=adguard&showintro=0&mimetype=plaintext"; }
|
|
];
|
|
description = "DNS blocklists to maintain in AdGuard Home";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# Generate a temporary JSON file containing the filters for yq to inject
|
|
systemd.tmpfiles.rules = [
|
|
"f /run/adguardhome_filters.json 0644 root root - ${builtins.toJSON { filters = lib.imap0 (i: f: { inherit (f) name url enabled; id = i + 1; }) cfg.filters; }}"
|
|
];
|
|
|
|
services.adguardhome = {
|
|
enable = true;
|
|
host = "127.0.0.1";
|
|
port = cfg.port;
|
|
settings = {
|
|
dns = {
|
|
bind_hosts = [ "0.0.0.0" ];
|
|
port = 5353;
|
|
upstream_dns = [ cfg.upstreamDoh ];
|
|
bootstrap_dns = cfg.bootstrapDns;
|
|
querylog_enabled = true;
|
|
querylog_file_enabled = true;
|
|
statistics_enabled = true;
|
|
};
|
|
|
|
tls = {
|
|
enabled = true;
|
|
server_name = cfg.domain;
|
|
certificate_path = "/var/lib/acme/${cfg.domain}/fullchain.pem";
|
|
private_key_path = "/var/lib/acme/${cfg.domain}/key.pem";
|
|
port_https = 3001; # Prevent conflict with Nginx on port 443
|
|
port_dns_over_tls = 853;
|
|
port_dns_over_quic = 0;
|
|
allow_unencrypted_doh = false;
|
|
};
|
|
|
|
filtering = {
|
|
protection_enabled = true;
|
|
filtering_enabled = true;
|
|
};
|
|
|
|
safebrowsing = {
|
|
enabled = false;
|
|
};
|
|
|
|
parental = {
|
|
enabled = false;
|
|
};
|
|
|
|
safesearch = {
|
|
enabled = false;
|
|
};
|
|
|
|
log = {
|
|
file = "";
|
|
max_backups = 0;
|
|
max_size = 100;
|
|
compress = false;
|
|
local_time = false;
|
|
verbose = false;
|
|
};
|
|
};
|
|
};
|
|
|
|
# Inject filters into AdGuardHome.yaml before starting
|
|
systemd.services.adguardhome = {
|
|
requires = [ "acme-${cfg.domain}.service" ];
|
|
after = [ "acme-${cfg.domain}.service" ];
|
|
serviceConfig.SupplementaryGroups = [ "acme" ];
|
|
serviceConfig.SystemCallFilter = lib.mkForce []; # Allow yq-go to run its syscalls
|
|
preStart = lib.mkAfter ''
|
|
if [ -f /var/lib/private/AdGuardHome/AdGuardHome.yaml ]; then
|
|
${pkgs.yq-go}/bin/yq -i '.filters = load("/run/adguardhome_filters.json").filters' /var/lib/private/AdGuardHome/AdGuardHome.yaml
|
|
fi
|
|
'';
|
|
};
|
|
|
|
# Open firewall for DoT
|
|
networking.firewall.allowedTCPPorts = [ 853 ];
|
|
networking.firewall.allowedUDPPorts = [ 853 ];
|
|
|
|
# Nginx configuration (kept to satisfy ACME challenges for DoT certificates)
|
|
services.nginx.virtualHosts."${cfg.domain}" = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
|
|
# Block all paths (no DoH or UI exposed via Nginx)
|
|
locations."/" = {
|
|
return = "404";
|
|
};
|
|
};
|
|
|
|
# Ensure nginx user can access ACME certs
|
|
users.users.nginx.extraGroups = [ "acme" ];
|
|
};
|
|
} |