WIP: sane-vpn: fix "sane-vpn up none" to correctly delegate all DNS to the DHCP-provided servers when using BIND
previously this only worked when using hickory-dns as the local resolver
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
# debugging:
|
# debugging:
|
||||||
|
# - `man named`
|
||||||
|
# - `man named.conf`
|
||||||
# - `systemctl stop bind`
|
# - `systemctl stop bind`
|
||||||
# - `sudo /nix/store/0zpdy93sd3fgbxgvf8dsxhn8fbbya8d2-bind-9.18.28/sbin/named -g -u named -4 -c /nix/store/f1mp0myzmfms71h9vinwxpn2i9362a9a-named.conf`
|
# - `sudo /nix/store/0zpdy93sd3fgbxgvf8dsxhn8fbbya8d2-bind-9.18.28/sbin/named -g -u named -4 -c /nix/store/f1mp0myzmfms71h9vinwxpn2i9362a9a-named.conf`
|
||||||
# - `-g` = don't fork
|
# - `-g` = don't fork
|
||||||
# - `-u named` = start as superuser (to claim port 53), then drop to user `named`
|
# - `-u named` = start as superuser (to claim port 53), then drop to user `named`
|
||||||
{ config, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
hostCfg = config.sane.hosts.by-name."${config.networking.hostName}";
|
hostCfg = config.sane.hosts.by-name."${config.networking.hostName}";
|
||||||
|
bindCfg = config.services.bind;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
config = lib.mkIf (!config.sane.services.hickory-dns.asSystemResolver) {
|
config = lib.mkIf (!config.sane.services.hickory-dns.asSystemResolver) {
|
||||||
@@ -53,5 +56,52 @@ in
|
|||||||
networking.resolvconf.useLocalResolver = false; #< we manage resolvconf explicitly, above
|
networking.resolvconf.useLocalResolver = false; #< we manage resolvconf explicitly, above
|
||||||
|
|
||||||
# TODO: how to exempt `pool.ntp.org` from DNSSEC checks, as i did when using unbound?
|
# TODO: how to exempt `pool.ntp.org` from DNSSEC checks, as i did when using unbound?
|
||||||
|
|
||||||
|
# allow runtime insertion of zones or other config changes:
|
||||||
|
# add your supplemental config as a toplevel file in /run/named/dhcp-configs/, then `systemctl restart bind`
|
||||||
|
services.bind.extraConfig = ''
|
||||||
|
include "/run/named/dhcp-configs.conf";
|
||||||
|
'';
|
||||||
|
services.bind.extraOptions = ''
|
||||||
|
// we can't guarantee that all forwarders support DNSSEC,
|
||||||
|
// and as of 2025-01-30 BIND9 gives no way to disable DNSSEC per-forwarder/zone,
|
||||||
|
// so just disable it globally
|
||||||
|
dnssec-validation no;
|
||||||
|
'';
|
||||||
|
# re-implement the nixos default bind config, but without `options { forwarders { }; };`,
|
||||||
|
# as having an empty `forwarders` at the top-level prevents me from forwarding the `.` zone in a separate statement
|
||||||
|
# (which i want to do to allow sane-vpn to forward all DNS).
|
||||||
|
services.bind.configFile = pkgs.writeText "named.conf" ''
|
||||||
|
include "/etc/bind/rndc.key";
|
||||||
|
controls {
|
||||||
|
inet 127.0.0.1 allow {localhost;} keys {"rndc-key";};
|
||||||
|
};
|
||||||
|
|
||||||
|
acl cachenetworks { ${lib.concatMapStrings (entry: " ${entry}; ") bindCfg.cacheNetworks} };
|
||||||
|
acl badnetworks { ${lib.concatMapStrings (entry: " ${entry}; ") bindCfg.blockedNetworks} };
|
||||||
|
|
||||||
|
options {
|
||||||
|
listen-on { ${lib.concatMapStrings (entry: " ${entry}; ") bindCfg.listenOn} };
|
||||||
|
listen-on-v6 { ${lib.concatMapStrings (entry: " ${entry}; ") bindCfg.listenOnIpv6} };
|
||||||
|
allow-query-cache { cachenetworks; };
|
||||||
|
blackhole { badnetworks; };
|
||||||
|
//v disable top-level forwards, so that i can do forwarding more generically in `zone FOO { ... }` directives.
|
||||||
|
// forward ${bindCfg.forward};
|
||||||
|
// forwarders { ${lib.concatMapStrings (entry: " ${entry}; ") bindCfg.forwarders} };
|
||||||
|
directory "${bindCfg.directory}";
|
||||||
|
pid-file "/run/named/named.pid";
|
||||||
|
${bindCfg.extraOptions}
|
||||||
|
};
|
||||||
|
|
||||||
|
${bindCfg.extraConfig}
|
||||||
|
'';
|
||||||
|
|
||||||
|
systemd.services.bind.serviceConfig.ExecStartPre = pkgs.writeShellScript "named-generate-config" ''
|
||||||
|
mkdir -p /run/named/dhcp-configs
|
||||||
|
echo "// FILE GENERATED BY bind.service's ExecStartPre: CHANGES TO THIS FILE WILL BE OVERWRITTEN" > /run/named/dhcp-configs.conf
|
||||||
|
for c in $(ls /run/named/dhcp-configs/); do
|
||||||
|
cat "/run/named/dhcp-configs/$c" >> /run/named/dhcp-configs.conf
|
||||||
|
done
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -279,7 +279,32 @@ def vpn_toggle(config: VpnConfig, dir_: ToggleDir) -> None:
|
|||||||
print("new IP address ...")
|
print("new IP address ...")
|
||||||
subprocess.check_call(["sane-ip-check", "--no-upnp"])
|
subprocess.check_call(["sane-ip-check", "--no-upnp"])
|
||||||
|
|
||||||
def dns_toggle(dns: list[str], dir_: ToggleDir) -> None:
|
def dns_toggle_bind(dns: list[str], dir_: ToggleDir) -> None:
|
||||||
|
if dir_ == ToggleDir.Up:
|
||||||
|
formatted_nameservers = "\n".join(f"{ns};" for ns in dns)
|
||||||
|
text = f'''
|
||||||
|
zone . {{
|
||||||
|
//v XXX(2025-01-30): BIND9 doesn't allow dnssec-validation per-zone. put this in toplevel `options` instead.
|
||||||
|
// dnssec-validation no; // compatibility: many network-specific DNS servers fail DNSSEC, by design
|
||||||
|
type forward;
|
||||||
|
forward first;
|
||||||
|
forwarders {{
|
||||||
|
{formatted_nameservers}
|
||||||
|
}};
|
||||||
|
}};
|
||||||
|
'''
|
||||||
|
elif dir == ToggleDir.Down:
|
||||||
|
text = ""
|
||||||
|
|
||||||
|
if not os.path.isdir("/run/named/dhcp-configs"):
|
||||||
|
logger.info("not restarting bind because it appears to not be in use")
|
||||||
|
return
|
||||||
|
|
||||||
|
with open("/run/named/dhcp-configs/210-sane-vpn.conf", "w") as f:
|
||||||
|
f.write(text)
|
||||||
|
subprocess.check_call([ "systemctl", "restart", "bind" ])
|
||||||
|
|
||||||
|
def dns_toggle_hickory(dns: list[str], dir_: ToggleDir) -> None:
|
||||||
if dir_ == ToggleDir.Up:
|
if dir_ == ToggleDir.Up:
|
||||||
formatted_nameservers = ",\n".join(
|
formatted_nameservers = ",\n".join(
|
||||||
'{ socket_addr = "{ns}:53", protocol = "udp", trust_nx_responses = false }'.replace("{ns}", ns)
|
'{ socket_addr = "{ns}:53", protocol = "udp", trust_nx_responses = false }'.replace("{ns}", ns)
|
||||||
@@ -304,6 +329,10 @@ stores = {{ type = "forward", name_servers = [
|
|||||||
f.write(text)
|
f.write(text)
|
||||||
subprocess.check_call([ "systemctl", "restart", "hickory-dns-localhost" ])
|
subprocess.check_call([ "systemctl", "restart", "hickory-dns-localhost" ])
|
||||||
|
|
||||||
|
def dns_toggle(dns: list[str], dir_: ToggleDir) -> None:
|
||||||
|
dns_toggle_bind(dns, dir_)
|
||||||
|
dns_toggle_hickory(dns, dir_)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
|
Reference in New Issue
Block a user