summary refs log tree commit diff
path: root/nixos/modules/services/networking/networkmanager.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/networking/networkmanager.nix')
-rw-r--r--nixos/modules/services/networking/networkmanager.nix186
1 files changed, 160 insertions, 26 deletions
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index f4c4adcaaeb8..b0bc1c83d6b7 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -6,21 +6,17 @@ with lib;
 let
   cfg = config.networking.networkmanager;
 
+  dynamicHostsEnabled =
+    cfg.dynamicHosts.enable && cfg.dynamicHosts.hostsDirs != {};
+
   # /var/lib/misc is for dnsmasq.leases.
   stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc";
 
-  dns =
-    if cfg.dns == "none" then "none"
-    else if cfg.dns == "dnsmasq" then "dnsmasq"
-    else if config.services.resolved.enable then "systemd-resolved"
-    else if config.services.unbound.enable then "unbound"
-    else "default";
-
   configFile = writeText "NetworkManager.conf" ''
     [main]
     plugins=keyfile
     dhcp=${cfg.dhcp}
-    dns=${dns}
+    dns=${cfg.dns}
 
     [keyfile]
     ${optionalString (cfg.unmanaged != [])
@@ -38,6 +34,8 @@ let
 
     [device]
     wifi.scan-rand-mac-address=${if cfg.wifi.scanRandMacAddress then "yes" else "no"}
+
+    ${cfg.extraConfig}
   '';
 
   /*
@@ -120,6 +118,14 @@ in {
         '';
       };
 
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Configuration appended to the generated NetworkManager.conf.
+        '';
+      };
+
       unmanaged = mkOption {
         type = types.listOf types.string;
         default = [];
@@ -207,19 +213,73 @@ in {
       };
 
       dns = mkOption {
-        type = types.enum [ "auto" "dnsmasq" "none" ];
-        default = "auto";
+        type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ];
+        default = "default";
         description = ''
+          Set the DNS (<literal>resolv.conf</literal>) processing mode.
+          </para>
+          <para>
           Options:
-            - auto: Check for systemd-resolved, unbound, or use default.
-            - dnsmasq:
-              Enable NetworkManager's dnsmasq integration. NetworkManager will run
-              dnsmasq as a local caching nameserver, using a "split DNS"
-              configuration if you are connected to a VPN, and then update
-              resolv.conf to point to the local nameserver.
-            - none:
-              Disable NetworkManager's DNS integration completely.
-              It will not touch your /etc/resolv.conf.
+          <variablelist>
+          <varlistentry>
+            <term><literal>"default"</literal></term>
+            <listitem><para>
+              NetworkManager will update <literal>/etc/resolv.conf</literal> to
+              reflect the nameservers provided by currently active connections.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><literal>"dnsmasq"</literal></term>
+            <listitem>
+              <para>
+                Enable NetworkManager's dnsmasq integration. NetworkManager will
+                run dnsmasq as a local caching nameserver, using a "split DNS"
+                configuration if you are connected to a VPN, and then update
+                <literal>resolv.conf</literal> to point to the local nameserver.
+              </para>
+              <para>
+                It is possible to pass custom options to the dnsmasq instance by
+                adding them to files in the
+                <literal>/etc/NetworkManager/dnsmasq.d/</literal> directory.
+              </para>
+              <para>
+                When multiple upstream servers are available, dnsmasq will
+                initially contact them in parallel and then use the fastest to
+                respond, probing again other servers after some time.  This
+                behavior can be modified passing the
+                <literal>all-servers</literal> or <literal>strict-order</literal>
+                options to dnsmasq (see the manual page for more details).
+              </para>
+              <para>
+                Note that this option causes NetworkManager to launch and manage
+                its own instance of the dnsmasq daemon, which is
+                <emphasis>not</emphasis> the same as setting
+                <literal>services.dnsmasq.enable = true;</literal>.
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><literal>"unbound"</literal></term>
+            <listitem><para>
+              NetworkManager will talk to unbound and dnssec-triggerd,
+              providing a "split DNS" configuration with DNSSEC support.
+              <literal>/etc/resolv.conf</literal> will be managed by
+              dnssec-trigger daemon.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><literal>"systemd-resolved"</literal></term>
+            <listitem><para>
+              NetworkManager will push the DNS configuration to systemd-resolved.
+            </para></listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><literal>"none"</literal></term>
+            <listitem><para>
+              NetworkManager will not modify resolv.conf.
+            </para></listitem>
+          </varlistentry>
+          </variablelist>
         '';
       };
 
@@ -260,6 +320,52 @@ in {
           so you don't need to to that yourself.
         '';
       };
+
+      dynamicHosts = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enabling this option requires the
+            <option>networking.networkmanager.dns</option> option to be
+            set to <literal>dnsmasq</literal>. If enabled, the directories
+            defined by the
+            <option>networking.networkmanager.dynamicHosts.hostsDirs</option>
+            option will be set up when the service starts. The dnsmasq instance
+            managed by NetworkManager will then watch those directories for
+            hosts files (see the <literal>--hostsdir</literal> option of
+            dnsmasq). This way a non-privileged user can add or override DNS
+            entries on the local system (depending on what hosts directories
+            that are configured)..
+          '';
+        };
+        hostsDirs = mkOption {
+          type = with types; attrsOf (submodule {
+            options = {
+              user = mkOption {
+                type = types.str;
+                default = "root";
+                description = ''
+                  The user that will own the hosts directory.
+                '';
+              };
+              group = mkOption {
+                type = types.str;
+                default = "root";
+                description = ''
+                  The group that will own the hosts directory.
+                '';
+              };
+            };
+          });
+          default = {};
+          description = ''
+            Defines a set of directories (relative to
+            <literal>/run/NetworkManager/hostdirs</literal>) that dnsmasq will
+            watch for hosts files.
+          '';
+        };
+      };
     };
   };
 
@@ -268,10 +374,17 @@ in {
 
   config = mkIf cfg.enable {
 
-    assertions = [{
-      assertion = config.networking.wireless.enable == false;
-      message = "You can not use networking.networkmanager with networking.wireless";
-    }];
+    assertions = [
+      { assertion = config.networking.wireless.enable == false;
+        message = "You can not use networking.networkmanager with networking.wireless";
+      }
+      { assertion = !dynamicHostsEnabled || (dynamicHostsEnabled && cfg.dns == "dnsmasq");
+        message = ''
+          To use networking.networkmanager.dynamicHosts you also need to set
+          networking.networkmanager.dns = "dnsmasq"
+        '';
+      }
+    ];
 
     environment.etc = with cfg.basePackages; [
       { source = configFile;
@@ -305,11 +418,17 @@ in {
       ++ lib.imap1 (i: s: {
         inherit (s) source;
         target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
-      }) cfg.dispatcherScripts;
+      }) cfg.dispatcherScripts
+      ++ optional (dynamicHostsEnabled)
+           { target = "NetworkManager/dnsmasq.d/dyndns.conf";
+             text = concatMapStrings (n: ''
+               hostsdir=/run/NetworkManager/hostsdirs/${n}
+             '') (attrNames cfg.dynamicHosts.hostsDirs);
+           };
 
     environment.systemPackages = cfg.packages;
 
-    users.extraGroups = [{
+    users.groups = [{
       name = "networkmanager";
       gid = config.ids.gids.networkmanager;
     }
@@ -317,7 +436,7 @@ in {
       name = "nm-openvpn";
       gid = config.ids.gids.nm-openvpn;
     }];
-    users.extraUsers = [{
+    users.users = [{
       name = "nm-openvpn";
       uid = config.ids.uids.nm-openvpn;
       extraGroups = [ "networkmanager" ];
@@ -341,6 +460,21 @@ in {
       '';
     };
 
+    systemd.services.nm-setup-hostsdirs = mkIf dynamicHostsEnabled {
+      wantedBy = [ "network-manager.service" ];
+      before = [ "network-manager.service" ];
+      partOf = [ "network-manager.service" ];
+      script = concatStrings (mapAttrsToList (n: d: ''
+        mkdir -p "/run/NetworkManager/hostsdirs/${n}"
+        chown "${d.user}:${d.group}" "/run/NetworkManager/hostsdirs/${n}"
+        chmod 0775 "/run/NetworkManager/hostsdirs/${n}"
+      '') cfg.dynamicHosts.hostsDirs);
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExist = true;
+      };
+    };
+
     # Turn off NixOS' network management
     networking = {
       useDHCP = false;