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.
This commit is contained in:
@@ -10,9 +10,35 @@ let
|
||||
cfg = config.networking.wireless;
|
||||
opt = options.networking.wireless;
|
||||
|
||||
wpa3Protocols = [ "SAE" "FT-SAE" ];
|
||||
hasWPA3 = opts: !mutuallyExclusive opts.authProtocols wpa3Protocols;
|
||||
|
||||
# Gives a WPA3 network higher priority
|
||||
increaseWPA3Priority = opts:
|
||||
opts // optionalAttrs (hasWPA3 opts)
|
||||
{ priority = if opts.priority == null
|
||||
then 1
|
||||
else opts.priority + 1;
|
||||
};
|
||||
|
||||
# Creates a WPA2 fallback network
|
||||
mkWPA2Fallback = opts:
|
||||
opts // { authProtocols = subtractLists wpa3Protocols opts.authProtocols; };
|
||||
|
||||
# Networks attrset as a list
|
||||
networkList = mapAttrsToList (ssid: opts: opts // { inherit ssid; })
|
||||
cfg.networks;
|
||||
|
||||
# List of all networks (normal + generated fallbacks)
|
||||
allNetworks =
|
||||
if cfg.fallbackToWPA2
|
||||
then map increaseWPA3Priority networkList
|
||||
++ map mkWPA2Fallback (filter hasWPA3 networkList)
|
||||
else networkList;
|
||||
|
||||
# Content of wpa_supplicant.conf
|
||||
generatedConfig = concatStringsSep "\n" (
|
||||
(mapAttrsToList mkNetwork cfg.networks)
|
||||
(map mkNetwork allNetworks)
|
||||
++ optional cfg.userControlled.enable (concatStringsSep "\n"
|
||||
[ "ctrl_interface=/run/wpa_supplicant"
|
||||
"ctrl_interface_group=${cfg.userControlled.group}"
|
||||
@@ -34,7 +60,7 @@ let
|
||||
finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf'';
|
||||
|
||||
# Creates a network block for wpa_supplicant.conf
|
||||
mkNetwork = ssid: opts:
|
||||
mkNetwork = opts:
|
||||
let
|
||||
quote = x: ''"${x}"'';
|
||||
indent = x: " " + x;
|
||||
@@ -44,7 +70,7 @@ let
|
||||
else opts.pskRaw;
|
||||
|
||||
options = [
|
||||
"ssid=${quote ssid}"
|
||||
"ssid=${quote opts.ssid}"
|
||||
(if pskString != null || opts.auth != null
|
||||
then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
|
||||
else "key_mgmt=NONE")
|
||||
@@ -176,6 +202,18 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
fallbackToWPA2 = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to fall back to WPA2 authentication protocols if WPA3 failed.
|
||||
This allows old wireless cards (that lack recent features required by
|
||||
WPA3) to connect to mixed WPA2/WPA3 access points.
|
||||
|
||||
To avoid possible downgrade attacks, disable this options.
|
||||
'';
|
||||
};
|
||||
|
||||
environmentFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
|
||||
Reference in New Issue
Block a user