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 dispatcherTypesSubdirMap = {
75 "basic" = "";
76 "pre-up" = "pre-up.d/";
77 "pre-down" = "pre-down.d/";
78 };
79
80in {
81
82 ###### interface
83
84 options = {
85
86 networking.networkmanager = {
87
88 enable = mkOption {
89 type = types.bool;
90 default = false;
91 description = ''
92 Whether to use NetworkManager to obtain an IP address and other
93 configuration for all network interfaces that are not manually
94 configured. If enabled, a group <literal>networkmanager</literal>
95 will be created. Add all users that should have permission
96 to change network settings to this group.
97 '';
98 };
99
100 # Ugly hack for using the correct gnome3 packageSet
101 basePackages = mkOption {
102 type = types.attrsOf types.path;
103 default = { inherit networkmanager modemmanager wpa_supplicant
104 networkmanager_openvpn networkmanager_vpnc
105 networkmanager_openconnect
106 networkmanager_pptp networkmanager_l2tp; };
107 internal = true;
108 };
109
110 packages = mkOption {
111 type = types.listOf types.path;
112 default = [ ];
113 description = ''
114 Extra packages that provide NetworkManager plugins.
115 '';
116 apply = list: (attrValues cfg.basePackages) ++ list;
117 };
118
119 appendNameservers = mkOption {
120 type = types.listOf types.str;
121 default = [];
122 description = ''
123 A list of name servers that should be appended
124 to the ones configured in NetworkManager or received by DHCP.
125 '';
126 };
127
128 insertNameservers = mkOption {
129 type = types.listOf types.str;
130 default = [];
131 description = ''
132 A list of name servers that should be inserted before
133 the ones configured in NetworkManager or received by DHCP.
134 '';
135 };
136
137 dispatcherScripts = mkOption {
138 type = types.listOf (types.submodule {
139 options = {
140 source = mkOption {
141 type = types.str;
142 description = ''
143 A script source.
144 '';
145 };
146
147 type = mkOption {
148 type = types.enum (attrNames dispatcherTypesSubdirMap);
149 default = "basic";
150 description = ''
151 Dispatcher hook type. Only basic hooks are currently available.
152 '';
153 };
154 };
155 });
156 default = [];
157 description = ''
158 A list of scripts which will be executed in response to network events.
159 '';
160 };
161 };
162 };
163
164
165 ###### implementation
166
167 config = mkIf cfg.enable {
168
169 assertions = [{
170 assertion = config.networking.wireless.enable == false;
171 message = "You can not use networking.networkmanager with services.networking.wireless";
172 }];
173
174 boot.kernelModules = [ "ppp_mppe" ]; # Needed for most (all?) PPTP VPN connections.
175
176 environment.etc = with cfg.basePackages; [
177 { source = ipUpScript;
178 target = "NetworkManager/dispatcher.d/01nixos-ip-up";
179 }
180 { source = configFile;
181 target = "NetworkManager/NetworkManager.conf";
182 }
183 { source = "${networkmanager_openvpn}/etc/NetworkManager/VPN/nm-openvpn-service.name";
184 target = "NetworkManager/VPN/nm-openvpn-service.name";
185 }
186 { source = "${networkmanager_vpnc}/etc/NetworkManager/VPN/nm-vpnc-service.name";
187 target = "NetworkManager/VPN/nm-vpnc-service.name";
188 }
189 { source = "${networkmanager_openconnect}/etc/NetworkManager/VPN/nm-openconnect-service.name";
190 target = "NetworkManager/VPN/nm-openconnect-service.name";
191 }
192 { source = "${networkmanager_pptp}/etc/NetworkManager/VPN/nm-pptp-service.name";
193 target = "NetworkManager/VPN/nm-pptp-service.name";
194 }
195 { source = "${networkmanager_l2tp}/etc/NetworkManager/VPN/nm-l2tp-service.name";
196 target = "NetworkManager/VPN/nm-l2tp-service.name";
197 }
198 ] ++ optional (cfg.appendNameservers == [] || cfg.insertNameservers == [])
199 { source = overrideNameserversScript;
200 target = "NetworkManager/dispatcher.d/02overridedns";
201 }
202 ++ lib.imap (i: s: {
203 text = s.source;
204 target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
205 }) cfg.dispatcherScripts;
206
207 environment.systemPackages = cfg.packages;
208
209 users.extraGroups = singleton {
210 name = "networkmanager";
211 gid = config.ids.gids.networkmanager;
212 };
213
214 systemd.packages = cfg.packages;
215
216 # Create an initialisation service that both starts
217 # NetworkManager when network.target is reached,
218 # and sets up necessary directories for NM.
219 systemd.services."networkmanager-init" = {
220 description = "NetworkManager initialisation";
221 wantedBy = [ "network.target" ];
222 wants = [ "network-manager.service" ];
223 before = [ "network-manager.service" ];
224 script = ''
225 mkdir -m 700 -p /etc/NetworkManager/system-connections
226 mkdir -m 755 -p ${stateDirs}
227 '';
228 serviceConfig.Type = "oneshot";
229 };
230
231 # Turn off NixOS' network management
232 networking = {
233 useDHCP = false;
234 wireless.enable = false;
235 };
236
237 powerManagement.resumeCommands = ''
238 ${config.systemd.package}/bin/systemctl restart network-manager
239 '';
240
241 security.polkit.extraConfig = polkitConf;
242
243 services.dbus.packages = cfg.packages;
244
245 services.udev.packages = cfg.packages;
246 };
247}