1{ config, lib, pkgs, ... }:
2
3with pkgs;
4with lib;
5
6let
7 cfg = config.networking.networkmanager;
8
9 # /var/lib/misc is for dnsmasq.leases.
10 stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc";
11
12 configFile = writeText "NetworkManager.conf" ''
13 [main]
14 plugins=keyfile
15
16 [keyfile]
17 ${optionalString (config.networking.hostName != "")
18 ''hostname=${config.networking.hostName}''}
19 ${optionalString (cfg.unmanaged != [])
20 ''unmanaged-devices=${lib.concatStringsSep ";" cfg.unmanaged}''}
21
22 [logging]
23 level=WARN
24
25 [connection]
26 ipv6.ip6-privacy=2
27 '';
28
29 /*
30 [network-manager]
31 Identity=unix-group:networkmanager
32 Action=org.freedesktop.NetworkManager.*
33 ResultAny=yes
34 ResultInactive=no
35 ResultActive=yes
36
37 [modem-manager]
38 Identity=unix-group:networkmanager
39 Action=org.freedesktop.ModemManager*
40 ResultAny=yes
41 ResultInactive=no
42 ResultActive=yes
43 */
44 polkitConf = ''
45 polkit.addRule(function(action, subject) {
46 if (
47 subject.isInGroup("networkmanager")
48 && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
49 || action.id.indexOf("org.freedesktop.ModemManager") == 0
50 ))
51 { return polkit.Result.YES; }
52 });
53 '';
54
55 ipUpScript = writeScript "01nixos-ip-up" ''
56 #!/bin/sh
57 if test "$2" = "up"; then
58 ${config.systemd.package}/bin/systemctl start ip-up.target
59 ${config.systemd.package}/bin/systemctl start network-online.target
60 fi
61 '';
62
63 ns = xs: writeText "nameservers" (
64 concatStrings (map (s: "nameserver ${s}\n") xs)
65 );
66
67 overrideNameserversScript = writeScript "02overridedns" ''
68 #!/bin/sh
69 tmp=`${coreutils}/bin/mktemp`
70 ${gnused}/bin/sed '/nameserver /d' /etc/resolv.conf > $tmp
71 ${gnugrep}/bin/grep 'nameserver ' /etc/resolv.conf | \
72 ${gnugrep}/bin/grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
73 ${optionalString (cfg.appendNameservers != []) "${coreutils}/bin/cat $tmp $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf"}
74 ${optionalString (cfg.insertNameservers != []) "${coreutils}/bin/cat $tmp ${ns cfg.insertNameservers} $tmp.ns > /etc/resolv.conf"}
75 ${coreutils}/bin/rm -f $tmp $tmp.ns
76 '';
77
78 dispatcherTypesSubdirMap = {
79 "basic" = "";
80 "pre-up" = "pre-up.d/";
81 "pre-down" = "pre-down.d/";
82 };
83
84in {
85
86 ###### interface
87
88 options = {
89
90 networking.networkmanager = {
91
92 enable = mkOption {
93 type = types.bool;
94 default = false;
95 description = ''
96 Whether to use NetworkManager to obtain an IP address and other
97 configuration for all network interfaces that are not manually
98 configured. If enabled, a group <literal>networkmanager</literal>
99 will be created. Add all users that should have permission
100 to change network settings to this group.
101 '';
102 };
103
104 unmanaged = mkOption {
105 type = types.listOf types.string;
106 default = [];
107 description = ''
108 List of interfaces that will not be managed by NetworkManager.
109 Interface name can be specified here, but if you need more fidelity
110 see "Device List Format" in NetworkManager.conf man page.
111 '';
112 };
113
114 # Ugly hack for using the correct gnome3 packageSet
115 basePackages = mkOption {
116 type = types.attrsOf types.package;
117 default = { inherit networkmanager modemmanager wpa_supplicant
118 networkmanager_openvpn networkmanager_vpnc
119 networkmanager_openconnect
120 networkmanager_pptp networkmanager_l2tp; };
121 internal = true;
122 };
123
124 packages = mkOption {
125 type = types.listOf types.path;
126 default = [ ];
127 description = ''
128 Extra packages that provide NetworkManager plugins.
129 '';
130 apply = list: (attrValues cfg.basePackages) ++ list;
131 };
132
133 appendNameservers = mkOption {
134 type = types.listOf types.str;
135 default = [];
136 description = ''
137 A list of name servers that should be appended
138 to the ones configured in NetworkManager or received by DHCP.
139 '';
140 };
141
142 insertNameservers = mkOption {
143 type = types.listOf types.str;
144 default = [];
145 description = ''
146 A list of name servers that should be inserted before
147 the ones configured in NetworkManager or received by DHCP.
148 '';
149 };
150
151 dispatcherScripts = mkOption {
152 type = types.listOf (types.submodule {
153 options = {
154 source = mkOption {
155 type = types.str;
156 description = ''
157 A script source.
158 '';
159 };
160
161 type = mkOption {
162 type = types.enum (attrNames dispatcherTypesSubdirMap);
163 default = "basic";
164 description = ''
165 Dispatcher hook type. Only basic hooks are currently available.
166 '';
167 };
168 };
169 });
170 default = [];
171 description = ''
172 A list of scripts which will be executed in response to network events.
173 '';
174 };
175 };
176 };
177
178
179 ###### implementation
180
181 config = mkIf cfg.enable {
182
183 assertions = [{
184 assertion = config.networking.wireless.enable == false;
185 message = "You can not use networking.networkmanager with services.networking.wireless";
186 }];
187
188 boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
189
190 environment.etc = with cfg.basePackages; [
191 { source = ipUpScript;
192 target = "NetworkManager/dispatcher.d/01nixos-ip-up";
193 }
194 { source = configFile;
195 target = "NetworkManager/NetworkManager.conf";
196 }
197 { source = "${networkmanager_openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
198 target = "NetworkManager/VPN/nm-openvpn-service.name";
199 }
200 { source = "${networkmanager_vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
201 target = "NetworkManager/VPN/nm-vpnc-service.name";
202 }
203 { source = "${networkmanager_openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
204 target = "NetworkManager/VPN/nm-openconnect-service.name";
205 }
206 { source = "${networkmanager_pptp}/etc/NetworkManager/VPN/nm-pptp-service.name";
207 target = "NetworkManager/VPN/nm-pptp-service.name";
208 }
209 { source = "${networkmanager_l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
210 target = "NetworkManager/VPN/nm-l2tp-service.name";
211 }
212 ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
213 { source = overrideNameserversScript;
214 target = "NetworkManager/dispatcher.d/02overridedns";
215 }
216 ++ lib.imap (i: s: {
217 text = s.source;
218 target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
219 }) cfg.dispatcherScripts;
220
221 environment.systemPackages = cfg.packages;
222
223 users.extraGroups = [{
224 name = "networkmanager";
225 gid = config.ids.gids.networkmanager;
226 }
227 {
228 name = "nm-openvpn";
229 gid = config.ids.gids.nm-openvpn;
230 }];
231 users.extraUsers = [{
232 name = "nm-openvpn";
233 uid = config.ids.uids.nm-openvpn;
234 }];
235
236 systemd.packages = cfg.packages;
237
238 systemd.services."network-manager" = {
239 wantedBy = [ "network.target" ];
240
241 preStart = ''
242 mkdir -m 700 -p /etc/NetworkManager/system-connections
243 mkdir -m 755 -p ${stateDirs}
244 '';
245 };
246
247 # Turn off NixOS' network management
248 networking = {
249 useDHCP = false;
250 wireless.enable = false;
251 };
252
253 powerManagement.resumeCommands = ''
254 ${config.systemd.package}/bin/systemctl restart network-manager
255 '';
256
257 security.polkit.extraConfig = polkitConf;
258
259 services.dbus.packages = cfg.packages;
260
261 services.udev.packages = cfg.packages;
262 };
263}