at 17.09-beta 9.8 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 (replaceChars [" "] ["-"] 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-${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"} 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 # Receive restart event after resume 43 partOf = [ "post-resume.target" ]; 44 45 path = [ pkgs.coreutils ]; 46 47 preStart = '' 48 ${optionalString (suppl.configFile.path!=null) '' 49 touch -a ${suppl.configFile.path} 50 chmod 600 ${suppl.configFile.path} 51 ''} 52 ${optionalString suppl.userControlled.enable '' 53 if ! test -e ${suppl.userControlled.socketDir}; then 54 mkdir -m 0770 -p ${suppl.userControlled.socketDir} 55 chgrp ${suppl.userControlled.group} ${suppl.userControlled.socketDir} 56 fi 57 58 if test "$(stat --printf '%G' ${suppl.userControlled.socketDir})" != "${suppl.userControlled.group}"; then 59 echo "ERROR: bad ownership on ${suppl.userControlled.socketDir}" >&2 60 exit 1 61 fi 62 ''} 63 ''; 64 65 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)}"; 66 67 }; 68 69 70in 71 72{ 73 74 ###### interface 75 76 options = { 77 78 networking.supplicant = mkOption { 79 type = with types; attrsOf (submodule { 80 options = { 81 82 configFile = { 83 84 path = mkOption { 85 type = types.nullOr types.path; 86 default = null; 87 example = literalExample "/etc/wpa_supplicant.conf"; 88 description = '' 89 External <literal>wpa_supplicant.conf</literal> configuration file. 90 The configuration options defined declaratively within <literal>networking.supplicant</literal> have 91 precedence over options defined in <literal>configFile</literal>. 92 ''; 93 }; 94 95 writable = mkOption { 96 type = types.bool; 97 default = false; 98 description = '' 99 Whether the configuration file at <literal>configFile.path</literal> should be written to by 100 <literal>wpa_supplicant</literal>. 101 ''; 102 }; 103 104 }; 105 106 extraConf = mkOption { 107 type = types.lines; 108 default = ""; 109 example = '' 110 ap_scan=1 111 device_name=My-NixOS-Device 112 device_type=1-0050F204-1 113 driver_param=use_p2p_group_interface=1 114 disable_scan_offload=1 115 p2p_listen_reg_class=81 116 p2p_listen_channel=1 117 p2p_oper_reg_class=81 118 p2p_oper_channel=1 119 manufacturer=NixOS 120 model_name=NixOS_Unstable 121 model_number=2015 122 ''; 123 description = '' 124 Configuration options for <literal>wpa_supplicant.conf</literal>. 125 Options defined here have precedence over options in <literal>configFile</literal>. 126 NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will 127 be world-readable in the <literal>nix-store</literal>. For sensitive information 128 use the <literal>configFile</literal> instead. 129 ''; 130 }; 131 132 extraCmdArgs = mkOption { 133 type = types.str; 134 default = ""; 135 example = "-e/var/run/wpa_supplicant/entropy.bin"; 136 description = 137 "Command line arguments to add when executing <literal>wpa_supplicant</literal>."; 138 }; 139 140 driver = mkOption { 141 type = types.nullOr types.str; 142 default = "nl80211,wext"; 143 description = "Force a specific wpa_supplicant driver."; 144 }; 145 146 bridge = mkOption { 147 type = types.str; 148 default = ""; 149 description = "Name of the bridge interface that wpa_supplicant should listen at."; 150 }; 151 152 userControlled = { 153 154 enable = mkOption { 155 type = types.bool; 156 default = false; 157 description = '' 158 Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. 159 This is useful for laptop users that switch networks a lot and don't want 160 to depend on a large package such as NetworkManager just to pick nearby 161 access points. 162 ''; 163 }; 164 165 socketDir = mkOption { 166 type = types.str; 167 default = "/var/run/wpa_supplicant"; 168 description = "Directory of sockets for controlling wpa_supplicant."; 169 }; 170 171 group = mkOption { 172 type = types.str; 173 default = "wheel"; 174 example = "network"; 175 description = "Members of this group can control wpa_supplicant."; 176 }; 177 178 }; 179 }; 180 }); 181 182 default = { }; 183 184 example = { 185 "wlan0 wlan1" = { 186 configFile = "/etc/wpa_supplicant"; 187 userControlled.group = "network"; 188 extraConf = '' 189 ap_scan=1 190 p2p_disabled=1 191 ''; 192 extraCmdArgs = "-u -W"; 193 bridge = "br0"; 194 }; 195 }; 196 197 description = '' 198 Interfaces for which to start <command>wpa_supplicant</command>. 199 The supplicant is used to scan for and associate with wireless networks, 200 or to authenticate with 802.1x capable network switches. 201 202 The value of this option is an attribute set. Each attribute configures a 203 <command>wpa_supplicant</command> service, where the attribute name specifies 204 the name of the interface that <command>wpa_supplicant</command> operates on. 205 The attribute name can be a space separated list of interfaces. 206 The attribute names <literal>WLAN</literal>, <literal>LAN</literal> and <literal>DBUS</literal> 207 have a special meaning. <literal>WLAN</literal> and <literal>LAN</literal> are 208 configurations for universal <command>wpa_supplicant</command> service that is 209 started for each WLAN interface or for each LAN interface, respectively. 210 <literal>DBUS</literal> defines a device-unrelated <command>wpa_supplicant</command> 211 service that can be accessed through <literal>D-Bus</literal>. 212 ''; 213 214 }; 215 216 }; 217 218 219 ###### implementation 220 221 config = mkIf (cfg != {}) { 222 223 environment.systemPackages = [ pkgs.wpa_supplicant ]; 224 225 services.dbus.packages = [ pkgs.wpa_supplicant ]; 226 227 systemd.services = mapAttrs' (n: v: nameValuePair (serviceName n) (supplicantService n v)) cfg; 228 229 services.udev.packages = [ 230 (pkgs.writeTextFile { 231 name = "99-zzz-60-supplicant.rules"; 232 destination = "/etc/udev/rules.d/99-zzz-60-supplicant.rules"; 233 text = '' 234 ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface: 235 flip (concatMapStringsSep "\n") (splitString " " iface) (i: '' 236 ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))} 237 238 ${optionalString (hasAttr "WLAN" cfg) '' 239 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service" 240 ''} 241 ${optionalString (hasAttr "LAN" cfg) '' 242 ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service" 243 ''} 244 ''; 245 })]; 246 247 }; 248 249} 250