1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.dnscache;
9
10 dnscache-root = pkgs.runCommand "dnscache-root" { preferLocalBuild = true; } ''
11 mkdir -p $out/{servers,ip}
12
13 ${lib.concatMapStrings (ip: ''
14 touch "$out/ip/"${lib.escapeShellArg ip}
15 '') cfg.clientIps}
16
17 ${lib.concatStrings (
18 lib.mapAttrsToList (host: ips: ''
19 ${lib.concatMapStrings (ip: ''
20 echo ${lib.escapeShellArg ip} >> "$out/servers/"${lib.escapeShellArg host}
21 '') ips}
22 '') cfg.domainServers
23 )}
24
25 # if a list of root servers was not provided in config, copy it
26 # over. (this is also done by dnscache-conf, but we 'rm -rf
27 # /var/lib/dnscache/root' below & replace it wholesale with this,
28 # so we have to ensure servers/@ exists ourselves.)
29 if [ ! -e $out/servers/@ ]; then
30 # symlink does not work here, due chroot
31 cp ${pkgs.djbdns}/etc/dnsroots.global $out/servers/@;
32 fi
33 '';
34
35in
36{
37
38 ###### interface
39
40 options = {
41 services.dnscache = {
42
43 enable = lib.mkOption {
44 default = false;
45 type = lib.types.bool;
46 description = "Whether to run the dnscache caching dns server.";
47 };
48
49 ip = lib.mkOption {
50 default = "0.0.0.0";
51 type = lib.types.str;
52 description = "IP address on which to listen for connections.";
53 };
54
55 clientIps = lib.mkOption {
56 default = [ "127.0.0.1" ];
57 type = lib.types.listOf lib.types.str;
58 description = "Client IP addresses (or prefixes) from which to accept connections.";
59 example = [
60 "192.168"
61 "172.23.75.82"
62 ];
63 };
64
65 domainServers = lib.mkOption {
66 default = { };
67 type = lib.types.attrsOf (lib.types.listOf lib.types.str);
68 description = ''
69 Table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts).
70 If entry for @ is not specified predefined list of root servers is used.
71 '';
72 example = lib.literalExpression ''
73 {
74 "@" = ["8.8.8.8" "8.8.4.4"];
75 "example.com" = ["192.168.100.100"];
76 }
77 '';
78 };
79
80 forwardOnly = lib.mkOption {
81 default = false;
82 type = lib.types.bool;
83 description = ''
84 Whether to treat root servers (for @) as caching
85 servers, requesting addresses the same way a client does. This is
86 needed if you want to use e.g. Google DNS as your upstream DNS.
87 '';
88 };
89
90 };
91 };
92
93 ###### implementation
94
95 config = lib.mkIf config.services.dnscache.enable {
96 environment.systemPackages = [ pkgs.djbdns ];
97 users.users.dnscache = {
98 isSystemUser = true;
99 group = "dnscache";
100 };
101 users.groups.dnscache = { };
102
103 systemd.services.dnscache = {
104 description = "djbdns dnscache server";
105 wantedBy = [ "multi-user.target" ];
106 path = with pkgs; [
107 bash
108 daemontools
109 djbdns
110 ];
111 preStart = ''
112 rm -rf /var/lib/dnscache
113 dnscache-conf dnscache dnscache /var/lib/dnscache ${config.services.dnscache.ip}
114 rm -rf /var/lib/dnscache/root
115 ln -sf ${dnscache-root} /var/lib/dnscache/root
116 '';
117 script = ''
118 cd /var/lib/dnscache/
119 ${lib.optionalString cfg.forwardOnly "export FORWARDONLY=1"}
120 exec ./run
121 '';
122 };
123 };
124}