at 24.11-pre 5.3 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.dnsmasq; 7 dnsmasq = cfg.package; 8 stateDir = "/var/lib/dnsmasq"; 9 10 # True values are just put as `name` instead of `name=true`, and false values 11 # are turned to comments (false values are expected to be overrides e.g. 12 # mkForce) 13 formatKeyValue = 14 name: value: 15 if value == true 16 then name 17 else if value == false 18 then "# setting `${name}` explicitly set to false" 19 else generators.mkKeyValueDefault { } "=" name value; 20 21 settingsFormat = pkgs.formats.keyValue { 22 mkKeyValue = formatKeyValue; 23 listsAsDuplicateKeys = true; 24 }; 25 26 # Because formats.generate is outputting a file, we use of conf-file. Once 27 # `extraConfig` is deprecated we can just use 28 # `dnsmasqConf = format.generate "dnsmasq.conf" cfg.settings` 29 dnsmasqConf = pkgs.writeText "dnsmasq.conf" '' 30 conf-file=${settingsFormat.generate "dnsmasq.conf" cfg.settings} 31 ${cfg.extraConfig} 32 ''; 33 34in 35 36{ 37 38 imports = [ 39 (mkRenamedOptionModule [ "services" "dnsmasq" "servers" ] [ "services" "dnsmasq" "settings" "server" ]) 40 ]; 41 42 ###### interface 43 44 options = { 45 46 services.dnsmasq = { 47 48 enable = mkOption { 49 type = types.bool; 50 default = false; 51 description = '' 52 Whether to run dnsmasq. 53 ''; 54 }; 55 56 package = mkPackageOption pkgs "dnsmasq" {}; 57 58 resolveLocalQueries = mkOption { 59 type = types.bool; 60 default = true; 61 description = '' 62 Whether dnsmasq should resolve local queries (i.e. add 127.0.0.1 to 63 /etc/resolv.conf). 64 ''; 65 }; 66 67 alwaysKeepRunning = mkOption { 68 type = types.bool; 69 default = false; 70 description = '' 71 If enabled, systemd will always respawn dnsmasq even if shut down manually. The default, disabled, will only restart it on error. 72 ''; 73 }; 74 75 settings = mkOption { 76 type = types.submodule { 77 78 freeformType = settingsFormat.type; 79 80 options.server = mkOption { 81 type = types.listOf types.str; 82 default = [ ]; 83 example = [ "8.8.8.8" "8.8.4.4" ]; 84 description = '' 85 The DNS servers which dnsmasq should query. 86 ''; 87 }; 88 89 }; 90 default = { }; 91 description = '' 92 Configuration of dnsmasq. Lists get added one value per line (empty 93 lists and false values don't get added, though false values get 94 turned to comments). Gets merged with 95 96 { 97 dhcp-leasefile = "${stateDir}/dnsmasq.leases"; 98 conf-file = optional cfg.resolveLocalQueries "/etc/dnsmasq-conf.conf"; 99 resolv-file = optional cfg.resolveLocalQueries "/etc/dnsmasq-resolv.conf"; 100 } 101 ''; 102 example = literalExpression '' 103 { 104 domain-needed = true; 105 dhcp-range = [ "192.168.0.2,192.168.0.254" ]; 106 } 107 ''; 108 }; 109 110 extraConfig = mkOption { 111 type = types.lines; 112 default = ""; 113 description = '' 114 Extra configuration directives that should be added to 115 `dnsmasq.conf`. 116 117 This option is deprecated, please use {option}`settings` instead. 118 ''; 119 }; 120 121 }; 122 123 }; 124 125 126 ###### implementation 127 128 config = mkIf cfg.enable { 129 130 warnings = lib.optional (cfg.extraConfig != "") "Text based config is deprecated, dnsmasq now supports `services.dnsmasq.settings` for an attribute-set based config"; 131 132 services.dnsmasq.settings = { 133 dhcp-leasefile = mkDefault "${stateDir}/dnsmasq.leases"; 134 conf-file = mkDefault (optional cfg.resolveLocalQueries "/etc/dnsmasq-conf.conf"); 135 resolv-file = mkDefault (optional cfg.resolveLocalQueries "/etc/dnsmasq-resolv.conf"); 136 }; 137 138 networking.nameservers = 139 optional cfg.resolveLocalQueries "127.0.0.1"; 140 141 services.dbus.packages = [ dnsmasq ]; 142 143 users.users.dnsmasq = { 144 isSystemUser = true; 145 group = "dnsmasq"; 146 description = "Dnsmasq daemon user"; 147 }; 148 users.groups.dnsmasq = {}; 149 150 networking.resolvconf = mkIf cfg.resolveLocalQueries { 151 useLocalResolver = mkDefault true; 152 153 extraConfig = '' 154 dnsmasq_conf=/etc/dnsmasq-conf.conf 155 dnsmasq_resolv=/etc/dnsmasq-resolv.conf 156 ''; 157 }; 158 159 systemd.services.dnsmasq = { 160 description = "Dnsmasq Daemon"; 161 after = [ "network.target" "systemd-resolved.service" ]; 162 wantedBy = [ "multi-user.target" ]; 163 path = [ dnsmasq ]; 164 preStart = '' 165 mkdir -m 755 -p ${stateDir} 166 touch ${stateDir}/dnsmasq.leases 167 chown -R dnsmasq ${stateDir} 168 touch /etc/dnsmasq-{conf,resolv}.conf 169 dnsmasq --test 170 ''; 171 serviceConfig = { 172 Type = "dbus"; 173 BusName = "uk.org.thekelleys.dnsmasq"; 174 ExecStart = "${dnsmasq}/bin/dnsmasq -k --enable-dbus --user=dnsmasq -C ${dnsmasqConf}"; 175 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 176 PrivateTmp = true; 177 ProtectSystem = true; 178 ProtectHome = true; 179 Restart = if cfg.alwaysKeepRunning then "always" else "on-failure"; 180 }; 181 restartTriggers = [ config.environment.etc.hosts.source ]; 182 }; 183 }; 184 185 meta.doc = ./dnsmasq.md; 186}