at 25.11-pre 8.0 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.hickory-dns; 9 toml = pkgs.formats.toml { }; 10 11 zoneType = lib.types.submodule ( 12 { config, ... }: 13 { 14 freeformType = toml.type; 15 options = with lib; { 16 zone = mkOption { 17 type = types.str; 18 description = '' 19 Zone name, like "example.com", "localhost", or "0.0.127.in-addr.arpa". 20 ''; 21 }; 22 zone_type = mkOption { 23 type = types.enum [ 24 "Primary" 25 "Secondary" 26 "External" 27 ]; 28 default = "Primary"; 29 description = '' 30 One of: 31 - "Primary" (the master, authority for the zone). 32 - "Secondary" (the slave, replicated from the primary). 33 - "External" (a cached zone that queries other nameservers). 34 35 For more details about these zone types, consult the documentation for BIND, 36 though note that hickory-dns supports only a subset of BIND's zone types: 37 <https://bind9.readthedocs.io/en/v9_18_4/reference.html#type> 38 ''; 39 }; 40 file = mkOption { 41 type = types.nullOr (types.either types.path types.str); 42 default = if config.zone_type != "External" then "${config.zone}.zone" else null; 43 defaultText = literalExpression ''if config.zone_type != "External" then "''${config.zone}.zone" else null''; 44 description = '' 45 Path to the .zone file. 46 If not fully-qualified, this path will be interpreted relative to the `directory` option. 47 If omitted, defaults to the value of the `zone` option suffixed with ".zone" when `zone_type` isn't External; otherwise, defaults to `null`. 48 ''; 49 }; 50 }; 51 } 52 ); 53in 54{ 55 meta.maintainers = with lib.maintainers; [ colinsane ]; 56 57 imports = with lib; [ 58 (mkRenamedOptionModule [ "services" "trust-dns" "enable" ] [ "services" "hickory-dns" "enable" ]) 59 (mkRenamedOptionModule [ "services" "trust-dns" "package" ] [ "services" "hickory-dns" "package" ]) 60 (mkRenamedOptionModule 61 [ "services" "trust-dns" "settings" ] 62 [ "services" "hickory-dns" "settings" ] 63 ) 64 (mkRenamedOptionModule [ "services" "trust-dns" "quiet" ] [ "services" "hickory-dns" "quiet" ]) 65 (mkRenamedOptionModule [ "services" "trust-dns" "debug" ] [ "services" "hickory-dns" "debug" ]) 66 ]; 67 68 options = { 69 services.hickory-dns = with lib; { 70 enable = mkEnableOption "hickory-dns"; 71 package = mkPackageOption pkgs "hickory-dns" { 72 extraDescription = '' 73 ::: {.note} 74 The package must provide `meta.mainProgram` which names the server binary; any other utilities (client, resolver) are not needed. 75 ::: 76 ''; 77 }; 78 quiet = mkOption { 79 type = types.bool; 80 default = false; 81 description = '' 82 Log ERROR level messages only. 83 This option is mutually exclusive with the `debug` option. 84 If neither `quiet` nor `debug` are enabled, logging defaults to the INFO level. 85 ''; 86 }; 87 debug = mkOption { 88 type = types.bool; 89 default = false; 90 description = '' 91 Log DEBUG, INFO, WARN and ERROR messages. 92 This option is mutually exclusive with the `debug` option. 93 If neither `quiet` nor `debug` are enabled, logging defaults to the INFO level. 94 ''; 95 }; 96 configFile = mkOption { 97 type = types.path; 98 default = toml.generate "hickory-dns.toml" ( 99 lib.mapAttrs ( 100 _: v: 101 if builtins.isList v then 102 map (v: if builtins.isAttrs v then lib.filterAttrs (_: v: v != null) v else v) v 103 else 104 v 105 ) (lib.filterAttrsRecursive (_: v: v != null) cfg.settings) 106 ); 107 defaultText = lib.literalExpression '' 108 let toml = pkgs.formats.toml { }; in toml.generate "hickory-dns.toml" cfg.settings 109 ''; 110 description = '' 111 Path to an existing toml file to configure hickory-dns with. 112 113 This can usually be left unspecified, in which case it will be 114 generated from the values in `settings`. 115 If manually specified, then the options in `settings` are ignored. 116 ''; 117 }; 118 settings = mkOption { 119 description = '' 120 Settings for hickory-dns. The options enumerated here are not exhaustive. 121 Refer to upstream documentation for all available options: 122 - [Example settings](https://github.com/hickory-dns/hickory-dns/blob/main/tests/test-data/test_configs/example.toml) 123 ''; 124 type = types.submodule { 125 freeformType = toml.type; 126 options = { 127 listen_addrs_ipv4 = mkOption { 128 type = types.listOf types.str; 129 default = [ "0.0.0.0" ]; 130 description = '' 131 List of ipv4 addresses on which to listen for DNS queries. 132 ''; 133 }; 134 listen_addrs_ipv6 = mkOption { 135 type = types.listOf types.str; 136 default = lib.optional config.networking.enableIPv6 "::0"; 137 defaultText = literalExpression ''lib.optional config.networking.enableIPv6 "::0"''; 138 description = '' 139 List of ipv6 addresses on which to listen for DNS queries. 140 ''; 141 }; 142 listen_port = mkOption { 143 type = types.port; 144 default = 53; 145 description = '' 146 Port to listen on (applies to all listen addresses). 147 ''; 148 }; 149 directory = mkOption { 150 type = types.str; 151 default = "/var/lib/hickory-dns"; 152 description = '' 153 The directory in which hickory-dns should look for .zone files, 154 whenever zones aren't specified by absolute path. 155 ''; 156 }; 157 zones = mkOption { 158 description = "List of zones to serve."; 159 default = [ ]; 160 type = types.listOf (types.coercedTo types.str (zone: { inherit zone; }) zoneType); 161 }; 162 }; 163 }; 164 }; 165 }; 166 }; 167 168 config = lib.mkIf cfg.enable { 169 systemd.services.hickory-dns = { 170 description = "hickory-dns Domain Name Server"; 171 unitConfig.Documentation = "https://hickory-dns.org/"; 172 serviceConfig = { 173 ExecStart = 174 let 175 flags = (lib.optional cfg.debug "--debug") ++ (lib.optional cfg.quiet "--quiet"); 176 flagsStr = builtins.concatStringsSep " " flags; 177 in 178 '' 179 ${lib.getExe cfg.package} --config ${cfg.configFile} ${flagsStr} 180 ''; 181 Type = "simple"; 182 Restart = "on-failure"; 183 RestartSec = "10s"; 184 DynamicUser = true; 185 186 StateDirectory = "hickory-dns"; 187 ReadWritePaths = [ cfg.settings.directory ]; 188 189 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; 190 CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; 191 LockPersonality = true; 192 MemoryDenyWriteExecute = true; 193 NoNewPrivileges = true; 194 PrivateDevices = true; 195 PrivateMounts = true; 196 PrivateTmp = true; 197 ProtectClock = true; 198 ProtectControlGroups = true; 199 ProtectHome = true; 200 ProtectHostname = true; 201 ProtectKernelLogs = true; 202 ProtectKernelModules = true; 203 ProtectKernelTunables = true; 204 ProtectProc = "invisible"; 205 ProtectSystem = "full"; 206 RemoveIPC = true; 207 RestrictAddressFamilies = [ "AF_INET AF_INET6" ]; 208 RestrictNamespaces = true; 209 RestrictSUIDSGID = true; 210 SystemCallArchitectures = "native"; 211 SystemCallFilter = [ 212 "@system-service" 213 "~@privileged" 214 "~@resources" 215 ]; 216 }; 217 after = [ "network.target" ]; 218 wantedBy = [ "multi-user.target" ]; 219 }; 220 }; 221}