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