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 (replaceStrings [" "] ["-"] 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-${replaceStrings [" "] ["-"] 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";
78 External `wpa_supplicant.conf` configuration file.
79 The configuration options defined declaratively within `networking.supplicant` have
80 precedence over options defined in `configFile`.
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
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";
125 description = "Command line arguments to add when executing `wpa_supplicant`.";
129 type = types.nullOr types.str;
130 default = "nl80211,wext";
131 description = "Force a specific wpa_supplicant driver.";
137 description = "Name of the bridge interface that wpa_supplicant should listen at.";
146 Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli.
147 This is useful for laptop users that switch networks a lot and don't want
148 to depend on a large package such as NetworkManager just to pick nearby
153 socketDir = mkOption {
155 default = "/run/wpa_supplicant";
156 description = "Directory of sockets for controlling wpa_supplicant.";
163 description = "Members of this group can control wpa_supplicant.";
172 example = literalExpression ''
174 configFile.path = "/etc/wpa_supplicant.conf";
175 userControlled.group = "network";
180 extraCmdArgs = "-u -W";
187 Interfaces for which to start {command}`wpa_supplicant`.
188 The supplicant is used to scan for and associate with wireless networks,
189 or to authenticate with 802.1x capable network switches.
191 The value of this option is an attribute set. Each attribute configures a
192 {command}`wpa_supplicant` service, where the attribute name specifies
193 the name of the interface that {command}`wpa_supplicant` operates on.
194 The attribute name can be a space separated list of interfaces.
195 The attribute names `WLAN`, `LAN` and `DBUS`
196 have a special meaning. `WLAN` and `LAN` are
197 configurations for universal {command}`wpa_supplicant` service that is
198 started for each WLAN interface or for each LAN interface, respectively.
199 `DBUS` defines a device-unrelated {command}`wpa_supplicant`
200 service that can be accessed through `D-Bus`.
208 ###### implementation
210 config = mkIf (cfg != {}) {
212 environment.systemPackages = [ pkgs.wpa_supplicant ];
214 services.dbus.packages = [ pkgs.wpa_supplicant ];
216 systemd.services = mapAttrs' (n: v: nameValuePair (serviceName n) (supplicantService n v)) cfg;
218 services.udev.packages = [
219 (pkgs.writeTextFile {
220 name = "99-zzz-60-supplicant.rules";
221 destination = "/etc/udev/rules.d/99-zzz-60-supplicant.rules";
223 ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface:
224 flip (concatMapStringsSep "\n") (splitString " " iface) (i: ''
225 ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceStrings [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
227 ${optionalString (hasAttr "WLAN" cfg) ''
228 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"
230 ${optionalString (hasAttr "LAN" cfg) ''
231 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"