git.iko.soy/typeable/nixpkgs
on master help sign in
nixos/wireless: implement opportunistic WPA3

It turns out it's actually possible to fall back to WPA2 in case the authentication fails with WPA3. This was suggested to me in the hostapd mailing list: add another network block with only WPA2 and lower priority, for each network with WPA3. For clients with missing/broken WPA3, wpa_supplicant will: 1. try the network block with higher priority first 2. fail and temporarily disable the network block 3. try the fallback network block and connect This takes a little more time (still <5s) because wpa_supplicant retries a couple times before disabling the network block, but it allows old client to gracefully fall back to WPA2 on mixed WPA2/WPA3 networks. To avoid downgrade attacks, clients with proper WPA3 should disable this; in the future we may want to disable this option by default.

commit2eed89bbe1fd431079b6c824bbcb66a92d3bff7c authorrnhmjoj <rnhmjoj@inventati.org> dateTue 11 Jan 2022, 22:01 (5 years ago) parent2f5ced6
1 file changed+41−3
nixos/modules/services/networking/wpa_supplicant.nix+41−3
nixos/modules/services/networking/wpa_supplicant.nix+41−3
@@ -10,9 +10,35 @@ let
1010 cfg = config.networking.wireless;
1111 opt = options.networking.wireless;
1212
13+ wpa3Protocols = [ "SAE" "FT-SAE" ];
14+ hasWPA3 = opts: !mutuallyExclusive opts.authProtocols wpa3Protocols;
15+
16+ # Gives a WPA3 network higher priority
17+ increaseWPA3Priority = opts:
18+ opts // optionalAttrs (hasWPA3 opts)
19+ { priority = if opts.priority == null
20+ then 1
21+ else opts.priority + 1;
22+ };
23+
24+ # Creates a WPA2 fallback network
25+ mkWPA2Fallback = opts:
26+ opts // { authProtocols = subtractLists wpa3Protocols opts.authProtocols; };
27+
28+ # Networks attrset as a list
29+ networkList = mapAttrsToList (ssid: opts: opts // { inherit ssid; })
30+ cfg.networks;
31+
32+ # List of all networks (normal + generated fallbacks)
33+ allNetworks =
34+ if cfg.fallbackToWPA2
35+ then map increaseWPA3Priority networkList
36+ ++ map mkWPA2Fallback (filter hasWPA3 networkList)
37+ else networkList;
38+
1339 # Content of wpa_supplicant.conf
1440 generatedConfig = concatStringsSep "\n" (
15− (mapAttrsToList mkNetwork cfg.networks)
41+ (map mkNetwork allNetworks)
1642 ++ optional cfg.userControlled.enable (concatStringsSep "\n"
1743 [ "ctrl_interface=/run/wpa_supplicant"
1844 "ctrl_interface_group=${cfg.userControlled.group}"
@@ -34,7 +60,7 @@ let
3460 finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf'';
3561
3662 # Creates a network block for wpa_supplicant.conf
37− mkNetwork = ssid: opts:
63+ mkNetwork = opts:
3864 let
3965 quote = x: ''"${x}"'';
4066 indent = x: " " + x;
@@ -44,7 +70,7 @@ let
4470 else opts.pskRaw;
4571
4672 options = [
47− "ssid=${quote ssid}"
73+ "ssid=${quote opts.ssid}"
4874 (if pskString != null || opts.auth != null
4975 then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
5076 else "key_mgmt=NONE")
@@ -176,6 +202,18 @@ in {
176202 '';
177203 };
178204
205+ fallbackToWPA2 = mkOption {
206+ type = types.bool;
207+ default = true;
208+ description = ''
209+ Whether to fall back to WPA2 authentication protocols if WPA3 failed.
210+ This allows old wireless cards (that lack recent features required by
211+ WPA3) to connect to mixed WPA2/WPA3 access points.
212+
213+ To avoid possible downgrade attacks, disable this options.
214+ '';
215+ };
216+
179217 environmentFile = mkOption {
180218 type = types.nullOr types.path;
181219 default = null;