1 { config, lib, utils, pkgs, ... }:
7 cfg = config.networking.supplicant;
9 # We must escape interfaces due to the systemd interpretation
10 subsystemDevice = interface:
11 "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device";
13 serviceName = iface: "supplicant-${if (iface=="WLAN") then "wlan@" else (
14 if (iface=="LAN") then "lan@" else (
15 if (iface=="DBUS") then "dbus"
16 else (replaceChars [" "] ["-"] iface)))}";
18 # TODO: Use proper privilege separation for wpa_supplicant
19 supplicantService = iface: suppl:
21 deps = (if (iface=="WLAN"||iface=="LAN") then ["sys-subsystem-net-devices-%i.device"] else (
22 if (iface=="DBUS") then ["dbus.service"]
23 else (map subsystemDevice (splitString " " iface))))
24 ++ optional (suppl.bridge!="") (subsystemDevice suppl.bridge);
26 ifaceArg = concatStringsSep " -N " (map (i: "-i${i}") (splitString " " iface));
27 driverArg = optionalString (suppl.driver != null) "-D${suppl.driver}";
28 bridgeArg = optionalString (suppl.bridge!="") "-b${suppl.bridge}";
29 confFileArg = optionalString (suppl.configFile.path!=null) "-c${suppl.configFile.path}";
30 extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceChars [" "] ["-"] iface}" ''
31 ${optionalString suppl.userControlled.enable "ctrl_interface=DIR=${suppl.userControlled.socketDir} GROUP=${suppl.userControlled.group}"}
32 ${optionalString suppl.configFile.writable "update_config=1"}
36 { description = "Supplicant ${iface}${optionalString (iface=="WLAN"||iface=="LAN") " %I"}";
37 wantedBy = [ "multi-user.target" ] ++ deps;
38 wants = [ "network.target" ];
41 before = [ "network.target" ];
43 path = [ pkgs.coreutils ];
46 ${optionalString (suppl.configFile.path!=null && suppl.configFile.writable) ''
47 (umask 077 && touch -a "${suppl.configFile.path}")
49 ${optionalString suppl.userControlled.enable ''
50 install -dm770 -g "${suppl.userControlled.group}" "${suppl.userControlled.socketDir}"
54 serviceConfig.ExecStart = "${pkgs.wpa_supplicant}/bin/wpa_supplicant -s ${driverArg} ${confFileArg} -I${extraConfFile} ${bridgeArg} ${suppl.extraCmdArgs} ${if (iface=="WLAN"||iface=="LAN") then "-i%I" else (if (iface=="DBUS") then "-u" else ifaceArg)}";
67 networking.supplicant = mkOption {
68 type = with types; attrsOf (submodule {
74 type = types.nullOr types.path;
76 example = literalExpression "/etc/wpa_supplicant.conf";
77 description = lib.mdDoc ''
78 External `wpa_supplicant.conf` configuration file.
79 The configuration options defined declaratively within `networking.supplicant` have
80 precedence over options defined in `configFile`.
87 description = lib.mdDoc ''
88 Whether the configuration file at `configFile.path` should be written to by
95 extraConf = mkOption {
100 device_name=My-NixOS-Device
101 device_type=1-0050F204-1
102 driver_param=use_p2p_group_interface=1
103 disable_scan_offload=1
104 p2p_listen_reg_class=81
106 p2p_oper_reg_class=81
109 model_name=NixOS_Unstable
112 description = lib.mdDoc ''
113 Configuration options for `wpa_supplicant.conf`.
114 Options defined here have precedence over options in `configFile`.
115 NOTE: Do not write sensitive data into `extraConf` as it will
116 be world-readable in the `nix-store`. For sensitive information
117 use the `configFile` instead.
121 extraCmdArgs = mkOption {
124 example = "-e/run/wpa_supplicant/entropy.bin";
126 lib.mdDoc "Command line arguments to add when executing `wpa_supplicant`.";
130 type = types.nullOr types.str;
131 default = "nl80211,wext";
132 description = lib.mdDoc "Force a specific wpa_supplicant driver.";
138 description = lib.mdDoc "Name of the bridge interface that wpa_supplicant should listen at.";
146 description = lib.mdDoc ''
147 Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
148 This is useful for laptop users that switch networks a lot and don't want
149 to depend on a large package such as NetworkManager just to pick nearby
154 socketDir = mkOption {
156 default = "/run/wpa_supplicant";
157 description = lib.mdDoc "Directory of sockets for controlling wpa_supplicant.";
164 description = lib.mdDoc "Members of this group can control wpa_supplicant.";
173 example = literalExpression ''
175 configFile.path = "/etc/wpa_supplicant.conf";
176 userControlled.group = "network";
181 extraCmdArgs = "-u -W";
187 description = lib.mdDoc ''
188 Interfaces for which to start {command}`wpa_supplicant`.
189 The supplicant is used to scan for and associate with wireless networks,
190 or to authenticate with 802.1x capable network switches.
192 The value of this option is an attribute set. Each attribute configures a
193 {command}`wpa_supplicant` service, where the attribute name specifies
194 the name of the interface that {command}`wpa_supplicant` operates on.
195 The attribute name can be a space separated list of interfaces.
196 The attribute names `WLAN`, `LAN` and `DBUS`
197 have a special meaning. `WLAN` and `LAN` are
198 configurations for universal {command}`wpa_supplicant` service that is
199 started for each WLAN interface or for each LAN interface, respectively.
200 `DBUS` defines a device-unrelated {command}`wpa_supplicant`
201 service that can be accessed through `D-Bus`.
209 ###### implementation
211 config = mkIf (cfg != {}) {
213 environment.systemPackages = [ pkgs.wpa_supplicant ];
215 services.dbus.packages = [ pkgs.wpa_supplicant ];
217 systemd.services = mapAttrs' (n: v: nameValuePair (serviceName n) (supplicantService n v)) cfg;
219 services.udev.packages = [
220 (pkgs.writeTextFile {
221 name = "99-zzz-60-supplicant.rules";
222 destination = "/etc/udev/rules.d/99-zzz-60-supplicant.rules";
224 ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface:
225 flip (concatMapStringsSep "\n") (splitString " " iface) (i: ''
226 ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
228 ${optionalString (hasAttr "WLAN" cfg) ''
229 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service"
231 ${optionalString (hasAttr "LAN" cfg) ''
232 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service"