at 24.11-pre 9.0 kB view raw
1{ config, lib, utils, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.networking.supplicant; 8 9 # We must escape interfaces due to the systemd interpretation 10 subsystemDevice = interface: 11 "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device"; 12 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)))}"; 17 18 # TODO: Use proper privilege separation for wpa_supplicant 19 supplicantService = iface: suppl: 20 let 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); 25 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"} 33 ${suppl.extraConf} 34 ''; 35 in 36 { description = "Supplicant ${iface}${optionalString (iface=="WLAN"||iface=="LAN") " %I"}"; 37 wantedBy = [ "multi-user.target" ] ++ deps; 38 wants = [ "network.target" ]; 39 bindsTo = deps; 40 after = deps; 41 before = [ "network.target" ]; 42 43 path = [ pkgs.coreutils ]; 44 45 preStart = '' 46 ${optionalString (suppl.configFile.path!=null && suppl.configFile.writable) '' 47 (umask 077 && touch -a "${suppl.configFile.path}") 48 ''} 49 ${optionalString suppl.userControlled.enable '' 50 install -dm770 -g "${suppl.userControlled.group}" "${suppl.userControlled.socketDir}" 51 ''} 52 ''; 53 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)}"; 55 56 }; 57 58 59in 60 61{ 62 63 ###### interface 64 65 options = { 66 67 networking.supplicant = mkOption { 68 type = with types; attrsOf (submodule { 69 options = { 70 71 configFile = { 72 73 path = mkOption { 74 type = types.nullOr types.path; 75 default = null; 76 example = literalExpression "/etc/wpa_supplicant.conf"; 77 description = '' 78 External `wpa_supplicant.conf` configuration file. 79 The configuration options defined declaratively within `networking.supplicant` have 80 precedence over options defined in `configFile`. 81 ''; 82 }; 83 84 writable = mkOption { 85 type = types.bool; 86 default = false; 87 description = '' 88 Whether the configuration file at `configFile.path` should be written to by 89 `wpa_supplicant`. 90 ''; 91 }; 92 93 }; 94 95 extraConf = mkOption { 96 type = types.lines; 97 default = ""; 98 example = '' 99 ap_scan=1 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 105 p2p_listen_channel=1 106 p2p_oper_reg_class=81 107 p2p_oper_channel=1 108 manufacturer=NixOS 109 model_name=NixOS_Unstable 110 model_number=2015 111 ''; 112 description = '' 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. 118 ''; 119 }; 120 121 extraCmdArgs = mkOption { 122 type = types.str; 123 default = ""; 124 example = "-e/run/wpa_supplicant/entropy.bin"; 125 description = "Command line arguments to add when executing `wpa_supplicant`."; 126 }; 127 128 driver = mkOption { 129 type = types.nullOr types.str; 130 default = "nl80211,wext"; 131 description = "Force a specific wpa_supplicant driver."; 132 }; 133 134 bridge = mkOption { 135 type = types.str; 136 default = ""; 137 description = "Name of the bridge interface that wpa_supplicant should listen at."; 138 }; 139 140 userControlled = { 141 142 enable = mkOption { 143 type = types.bool; 144 default = false; 145 description = '' 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 149 access points. 150 ''; 151 }; 152 153 socketDir = mkOption { 154 type = types.str; 155 default = "/run/wpa_supplicant"; 156 description = "Directory of sockets for controlling wpa_supplicant."; 157 }; 158 159 group = mkOption { 160 type = types.str; 161 default = "wheel"; 162 example = "network"; 163 description = "Members of this group can control wpa_supplicant."; 164 }; 165 166 }; 167 }; 168 }); 169 170 default = { }; 171 172 example = literalExpression '' 173 { "wlan0 wlan1" = { 174 configFile.path = "/etc/wpa_supplicant.conf"; 175 userControlled.group = "network"; 176 extraConf = ''' 177 ap_scan=1 178 p2p_disabled=1 179 '''; 180 extraCmdArgs = "-u -W"; 181 bridge = "br0"; 182 }; 183 } 184 ''; 185 186 description = '' 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. 190 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`. 201 ''; 202 203 }; 204 205 }; 206 207 208 ###### implementation 209 210 config = mkIf (cfg != {}) { 211 212 environment.systemPackages = [ pkgs.wpa_supplicant ]; 213 214 services.dbus.packages = [ pkgs.wpa_supplicant ]; 215 216 systemd.services = mapAttrs' (n: v: nameValuePair (serviceName n) (supplicantService n v)) cfg; 217 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"; 222 text = '' 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"''))} 226 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" 229 ''} 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" 232 ''} 233 ''; 234 })]; 235 236 }; 237 238} 239