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