nixos/wireless: reimplement secrets using ext_password_backend (#180872)

This commit is contained in:
Michele Guerini Rocco 2024-09-11 19:58:36 +02:00 committed by GitHub
commit b94f259714
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 205 additions and 225 deletions

View file

@ -197,6 +197,9 @@
moved into the top level scope (i.e., `budgie.budgie-desktop` is now moved into the top level scope (i.e., `budgie.budgie-desktop` is now
`budgie-desktop`) `budgie-desktop`)
- The method to safely handle secrets in the `networking.wireless` module has been changed to benefit from a [new feature](https://w1.fi/cgit/hostap/commit/?id=e680a51e94a33591f61edb210926bcb71217a21a) of wpa_supplicant.
The syntax to refer to secrets has changed slightly and the option `networking.wireless.environmentFile` has been replaced by `networking.wireless.secretsFile`; see the description of the latter for how to upgrade.
- All Cinnamon and XApp packages have been moved to top-level (i.e., `cinnamon.nemo` is now `nemo`). - All Cinnamon and XApp packages have been moved to top-level (i.e., `cinnamon.nemo` is now `nemo`).
- All GNOME packages have been moved to top-level (i.e., `gnome.nautilus` is now `nautilus`). - All GNOME packages have been moved to top-level (i.e., `gnome.nautilus` is now `nautilus`).

View file

@ -45,20 +45,11 @@ let
"update_config=1" "update_config=1"
]) ])
++ [ "pmf=1" ] ++ [ "pmf=1" ]
++ optional (cfg.secretsFile != null)
"ext_password_backend=file:${cfg.secretsFile}"
++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"'' ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"''
++ optional (cfg.extraConfig != "") cfg.extraConfig); ++ optional (cfg.extraConfig != "") cfg.extraConfig);
configIsGenerated = with cfg;
networks != {} || extraConfig != "" || userControlled.enable;
# the original configuration file
configFile =
if configIsGenerated
then pkgs.writeText "wpa_supplicant.conf" generatedConfig
else "/etc/wpa_supplicant.conf";
# the config file with environment variables replaced
finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf'';
# Creates a network block for wpa_supplicant.conf # Creates a network block for wpa_supplicant.conf
mkNetwork = opts: mkNetwork = opts:
let let
@ -90,8 +81,8 @@ let
let let
deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device"; deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device";
configStr = if cfg.allowAuxiliaryImperativeNetworks configStr = if cfg.allowAuxiliaryImperativeNetworks
then "-c /etc/wpa_supplicant.conf -I ${finalConfig}" then "-c /etc/wpa_supplicant.conf -I ${pkgs.writeText "wpa_supplicant.conf" generatedConfig}"
else "-c ${finalConfig}"; else "-c /etc/wpa_supplicant.conf";
in { in {
description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}"; description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}";
@ -109,37 +100,14 @@ let
serviceConfig.UMask = "066"; serviceConfig.UMask = "066";
serviceConfig.RuntimeDirectory = "wpa_supplicant"; serviceConfig.RuntimeDirectory = "wpa_supplicant";
serviceConfig.RuntimeDirectoryMode = "700"; serviceConfig.RuntimeDirectoryMode = "700";
serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null)
(builtins.toString cfg.environmentFile);
script = script =
'' ''
${optionalString (configIsGenerated && !cfg.allowAuxiliaryImperativeNetworks) ''
if [ -f /etc/wpa_supplicant.conf ]; then
echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
fi
''}
# ensure wpa_supplicant.conf exists, or the daemon will fail to start # ensure wpa_supplicant.conf exists, or the daemon will fail to start
${optionalString cfg.allowAuxiliaryImperativeNetworks '' ${optionalString cfg.allowAuxiliaryImperativeNetworks ''
touch /etc/wpa_supplicant.conf touch /etc/wpa_supplicant.conf
''} ''}
# substitute environment variables
if [ -f "${configFile}" ]; then
${pkgs.gawk}/bin/awk '{
for(varname in ENVIRON) {
find = "@"varname"@"
repl = ENVIRON[varname]
if (i = index($0, find))
$0 = substr($0, 1, i-1) repl substr($0, i+length(find))
}
print
}' "${configFile}" > ${finalConfig}
else
touch ${finalConfig}
fi
iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}" iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}"
${if iface == null then '' ${if iface == null then ''
@ -231,36 +199,34 @@ in {
''; '';
}; };
environmentFile = mkOption { secretsFile = mkOption {
type = types.nullOr types.path; type = types.nullOr types.path;
default = null; default = null;
example = "/run/secrets/wireless.env"; example = "/run/secrets/wireless.conf";
description = '' description = ''
File consisting of lines of the form `varname=value` File consisting of lines of the form `varname=value`
to define variables for the wireless configuration. to define variables for the wireless configuration.
See section "EnvironmentFile=" in {manpage}`systemd.exec(5)` for a syntax reference.
Secrets (PSKs, passwords, etc.) can be provided without adding them to Secrets (PSKs, passwords, etc.) can be provided without adding them to
the world-readable Nix store by defining them in the environment file and the world-readable Nix store by defining them in the secrets file and
referring to them in option {option}`networking.wireless.networks` referring to them in option [](#opt-networking.wireless.networks)
with the syntax `@varname@`. Example: with the syntax `ext:secretname`. Example:
``` ```
# content of /run/secrets/wireless.env # content of /run/secrets/wireless.conf
PSK_HOME=mypassword psk_home=mypassword
PASS_WORK=myworkpassword psk_other=6a381cea59c7a2d6b30736ba0e6f397f7564a044bcdb7a327a1d16a1ed91b327
``` pass_work=myworkpassword
```
# wireless-related configuration # wireless-related configuration
networking.wireless.environmentFile = "/run/secrets/wireless.env"; networking.wireless.secretsFile = "/run/secrets/wireless.conf";
networking.wireless.networks = { networking.wireless.networks = {
home.psk = "@PSK_HOME@"; home.pskRaw = "ext:psk_home";
other.pskRaw = "ext:psk_other";
work.auth = ''' work.auth = '''
eap=PEAP eap=PEAP
identity="my-user@example.com" identity="my-user@example.com"
password="@PASS_WORK@" password=ext:pass_work
'''; ''';
}; };
``` ```
@ -271,15 +237,16 @@ in {
type = types.attrsOf (types.submodule { type = types.attrsOf (types.submodule {
options = { options = {
psk = mkOption { psk = mkOption {
type = types.nullOr types.str; type = types.nullOr (types.strMatching "[[:print:]]{8,63}");
default = null; default = null;
description = '' description = ''
The network's pre-shared key in plaintext defaulting The network's pre-shared key in plaintext defaulting
to being a network without any authentication. to being a network without any authentication.
::: {.warning} ::: {.warning}
Be aware that this will be written to the nix store Be aware that this will be written to the Nix store
in plaintext! Use an environment variable instead. in plaintext! Use {var}`pskRaw` with an external
reference to keep it safe.
::: :::
::: {.note} ::: {.note}
@ -289,19 +256,28 @@ in {
}; };
pskRaw = mkOption { pskRaw = mkOption {
type = types.nullOr types.str; type = types.nullOr
(types.strMatching "([[:xdigit:]]{64})|(ext:[^=]+)");
default = null; default = null;
example = "ext:name_of_the_secret_here";
description = '' description = ''
The network's pre-shared key in hex defaulting Either the raw pre-shared key in hexadecimal format
to being a network without any authentication. or the name of the secret (as defined inside
[](#opt-networking.wireless.secretsFile) and prefixed
with `ext:`) containing the network pre-shared key.
::: {.warning} ::: {.warning}
Be aware that this will be written to the nix store Be aware that this will be written to the Nix store
in plaintext! Use an environment variable instead. in plaintext! Always use an external reference.
::: :::
::: {.note} ::: {.note}
Mutually exclusive with {var}`psk`. The external secret can be either the plaintext
passphrase or the raw pre-shared key.
:::
::: {.note}
Mutually exclusive with {var}`psk` and {var}`auth`.
::: :::
''; '';
}; };
@ -354,22 +330,21 @@ in {
example = '' example = ''
eap=PEAP eap=PEAP
identity="user@example.com" identity="user@example.com"
password="@EXAMPLE_PASSWORD@" password=ext:example_password
''; '';
description = '' description = ''
Use this option to configure advanced authentication methods like EAP. Use this option to configure advanced authentication methods
See like EAP. See {manpage}`wpa_supplicant.conf(5)` for example
{manpage}`wpa_supplicant.conf(5)` configurations.
for example configurations.
::: {.warning} ::: {.warning}
Be aware that this will be written to the nix store Be aware that this will be written to the Nix store
in plaintext! Use an environment variable for secrets. in plaintext! Use an external reference like
`ext:secretname` for secrets.
::: :::
::: {.note} ::: {.note}
Mutually exclusive with {var}`psk` and Mutually exclusive with {var}`psk` and {var}`pskRaw`.
{var}`pskRaw`.
::: :::
''; '';
}; };
@ -393,13 +368,14 @@ in {
type = types.nullOr types.int; type = types.nullOr types.int;
default = null; default = null;
description = '' description = ''
By default, all networks will get same priority group (0). If some of the By default, all networks will get same priority group (0). If
networks are more desirable, this field can be used to change the order in some of the networks are more desirable, this field can be used
which wpa_supplicant goes through the networks when selecting a BSS. The to change the order in which wpa_supplicant goes through the
priority groups will be iterated in decreasing priority (i.e., the larger the networks when selecting a BSS. The priority groups will be
priority value, the sooner the network is matched against the scan results). iterated in decreasing priority (i.e., the larger the priority
Within each priority group, networks will be selected based on security value, the sooner the network is matched against the scan
policy, signal strength, etc. results). Within each priority group, networks will be selected
based on security policy, signal strength, etc.
''; '';
}; };
@ -411,9 +387,7 @@ in {
''; '';
description = '' description = ''
Extra configuration lines appended to the network block. Extra configuration lines appended to the network block.
See See {manpage}`wpa_supplicant.conf(5)` for available options.
{manpage}`wpa_supplicant.conf(5)`
for available options.
''; '';
}; };
@ -432,7 +406,7 @@ in {
}; };
echelon = { # safe version of the above: read PSK from the echelon = { # safe version of the above: read PSK from the
psk = "@PSK_ECHELON@"; # variable PSK_ECHELON, defined in environmentFile, pskRaw = "ext:psk_echelon"; # variable psk_echelon, defined in secretsFile,
}; # this won't leak into /nix/store }; # this won't leak into /nix/store
"echelon's AP" = { # SSID with spaces and/or special characters "echelon's AP" = { # SSID with spaces and/or special characters
@ -493,6 +467,31 @@ in {
}; };
}; };
imports = [
(mkRemovedOptionModule [ "networking" "wireless" "environmentFile" ]
''
Secrets are now handled by the `networking.wireless.secretsFile` and
`networking.wireless.networks.<name>.pskRaw` options.
The change is motivated by a mechanism recently added by wpa_supplicant
itself to separate secrets from configuration, making the previous
method obsolete.
The syntax of the `secretsFile` is the same as before, except the
values are interpreted literally, unlike environment variables.
To update, remove quotes or character escapes, if necessary, and
apply the following changes to your configuration:
{
home.psk = "@psk_home@"; home.pskRaw = "ext:psk_home";
other.pskRaw = "@psk_other@"; other.pskRaw = "ext:psk_other";
work.auth = '''
eap=PEAP
identity="my-user@example.com"
password=@pass_work@ password=ext:pass_work
''';
}
'')
];
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = flip mapAttrsToList cfg.networks (name: cfg: { assertions = flip mapAttrsToList cfg.networks (name: cfg: {
assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1; assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1;
@ -517,6 +516,9 @@ in {
hardware.wirelessRegulatoryDatabase = true; hardware.wirelessRegulatoryDatabase = true;
environment.etc."wpa_supplicant.conf" =
lib.mkIf (!cfg.allowAuxiliaryImperativeNetworks) { text = generatedConfig; };
environment.systemPackages = [ pkgs.wpa_supplicant ]; environment.systemPackages = [ pkgs.wpa_supplicant ];
services.dbus.packages = optional cfg.dbusControlled pkgs.wpa_supplicant; services.dbus.packages = optional cfg.dbusControlled pkgs.wpa_supplicant;

View file

@ -1101,7 +1101,7 @@ in {
without-nix = handleTest ./without-nix.nix {}; without-nix = handleTest ./without-nix.nix {};
wmderland = handleTest ./wmderland.nix {}; wmderland = handleTest ./wmderland.nix {};
workout-tracker = handleTest ./workout-tracker.nix {}; workout-tracker = handleTest ./workout-tracker.nix {};
wpa_supplicant = handleTest ./wpa_supplicant.nix {}; wpa_supplicant = import ./wpa_supplicant.nix { inherit pkgs runTest; };
wordpress = handleTest ./wordpress.nix {}; wordpress = handleTest ./wordpress.nix {};
wrappers = handleTest ./wrappers.nix {}; wrappers = handleTest ./wrappers.nix {};
writefreely = handleTest ./web-apps/writefreely.nix {}; writefreely = handleTest ./web-apps/writefreely.nix {};

View file

@ -1,17 +1,18 @@
import ./make-test-python.nix ({ pkgs, lib, ...}: { pkgs, runTest }:
{
name = "wpa_supplicant"; let
inherit (pkgs) lib;
meta = with lib.maintainers; { meta = with lib.maintainers; {
maintainers = [ oddlama rnhmjoj ]; maintainers = [ oddlama rnhmjoj ];
}; };
nodes = let runConnectionTest = name: extraConfig: runTest {
machineWithHostapd = extraConfigModule: { ... }: { name = "wpa_supplicant-${name}";
imports = [ inherit meta;
../modules/profiles/minimal.nix
extraConfigModule
];
nodes.machine = {
# add a virtual wlan interface # add a virtual wlan interface
boot.kernelModules = [ "mac80211_hwsim" ]; boot.kernelModules = [ "mac80211_hwsim" ];
@ -53,27 +54,49 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
}; };
# wireless client # wireless client
networking.wireless = { networking.wireless = lib.mkMerge [
# the override is needed because the wifi is {
# disabled with mkVMOverride in qemu-vm.nix. # the override is needed because the wifi is
enable = lib.mkOverride 0 true; # disabled with mkVMOverride in qemu-vm.nix.
userControlled.enable = true; enable = lib.mkOverride 0 true;
interfaces = [ "wlan1" ]; userControlled.enable = true;
fallbackToWPA2 = lib.mkDefault true; interfaces = [ "wlan1" ];
fallbackToWPA2 = lib.mkDefault true;
# networks will be added on-demand below for the specific # secrets
# network that should be tested secretsFile = pkgs.writeText "wpa-secrets" ''
psk_nixos_test=reproducibility
# secrets '';
environmentFile = pkgs.writeText "wpa-secrets" '' }
PSK_NIXOS_TEST="reproducibility" extraConfig
''; ];
};
}; };
in {
basic = { ... }: {
imports = [ ../modules/profiles/minimal.nix ];
testScript = ''
# save hostapd config file for manual inspection
machine.wait_for_unit("hostapd.service")
machine.copy_from_vm("/run/hostapd/wlan0.hostapd.conf")
with subtest("Daemon can connect to the access point"):
machine.wait_for_unit("wpa_supplicant-wlan1.service")
machine.wait_until_succeeds(
"wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
)
'';
};
in
{
# Test the basic setup:
# - automatic interface discovery
# - WPA2 fallbacks
# - connecting to the daemon
basic = runTest {
name = "wpa_supplicant-basic";
inherit meta;
nodes.machine = {
# add a virtual wlan interface # add a virtual wlan interface
boot.kernelModules = [ "mac80211_hwsim" ]; boot.kernelModules = [ "mac80211_hwsim" ];
@ -83,7 +106,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
# disabled with mkVMOverride in qemu-vm.nix. # disabled with mkVMOverride in qemu-vm.nix.
enable = lib.mkOverride 0 true; enable = lib.mkOverride 0 true;
userControlled.enable = true; userControlled.enable = true;
interfaces = [ "wlan1" ];
fallbackToWPA2 = true; fallbackToWPA2 = true;
networks = { networks = {
@ -96,28 +118,34 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
psk = "password"; psk = "password";
authProtocols = [ "SAE" ]; authProtocols = [ "SAE" ];
}; };
# secrets substitution test cases
test1.psk = "@PSK_VALID@"; # should be replaced
test2.psk = "@PSK_SPECIAL@"; # should be replaced
test3.psk = "@PSK_MISSING@"; # should not be replaced
test4.psk = "P@ssowrdWithSome@tSymbol"; # should not be replaced
test5.psk = "@PSK_AWK_REGEX@"; # should be replaced
}; };
# secrets
environmentFile = pkgs.writeText "wpa-secrets" ''
PSK_VALID="S0m3BadP4ssw0rd";
# taken from https://github.com/minimaxir/big-list-of-naughty-strings
PSK_SPECIAL=",./;'[]\/\-= <>?:\"{}|_+ !@#$%^&*()`~";
PSK_AWK_REGEX="PassowrdWith&symbol";
'';
}; };
}; };
imperative = { ... }: { testScript = ''
imports = [ ../modules/profiles/minimal.nix ]; config_file = "/etc/static/wpa_supplicant.conf"
with subtest("Daemon is running and accepting connections"):
machine.wait_for_unit("wpa_supplicant.service")
status = machine.wait_until_succeeds("wpa_cli status")
assert "Failed to connect" not in status, \
"Failed to connect to the daemon"
with subtest("WPA2 fallbacks have been generated"):
assert int(machine.succeed(f"grep -c sae-only {config_file}")) == 1
assert int(machine.succeed(f"grep -c mixed-wpa {config_file}")) == 2
# save file for manual inspection
machine.copy_from_vm(config_file)
'';
};
# Test configuring the daemon imperatively
imperative = runTest {
name = "wpa_supplicant-imperative";
inherit meta;
nodes.machine = {
# add a virtual wlan interface # add a virtual wlan interface
boot.kernelModules = [ "mac80211_hwsim" ]; boot.kernelModules = [ "mac80211_hwsim" ];
@ -130,108 +158,55 @@ import ./make-test-python.nix ({ pkgs, lib, ...}:
}; };
}; };
# Test connecting to the SAE-only hotspot using SAE testScript = ''
machineSae = machineWithHostapd {
networking.wireless = {
fallbackToWPA2 = false;
networks.nixos-test-sae = {
psk = "@PSK_NIXOS_TEST@";
authProtocols = [ "SAE" ];
};
};
};
# Test connecting to the SAE and WPA2 mixed hotspot using SAE
machineMixedUsingSae = machineWithHostapd {
networking.wireless = {
fallbackToWPA2 = false;
networks.nixos-test-mixed = {
psk = "@PSK_NIXOS_TEST@";
authProtocols = [ "SAE" ];
};
};
};
# Test connecting to the SAE and WPA2 mixed hotspot using WPA2
machineMixedUsingWpa2 = machineWithHostapd {
networking.wireless = {
fallbackToWPA2 = true;
networks.nixos-test-mixed = {
psk = "@PSK_NIXOS_TEST@";
authProtocols = [ "WPA-PSK-SHA256" ];
};
};
};
# Test connecting to the WPA2 legacy hotspot using WPA2
machineWpa2 = machineWithHostapd {
networking.wireless = {
fallbackToWPA2 = true;
networks.nixos-test-wpa2 = {
psk = "@PSK_NIXOS_TEST@";
authProtocols = [ "WPA-PSK-SHA256" ];
};
};
};
};
testScript =
''
config_file = "/run/wpa_supplicant/wpa_supplicant.conf"
with subtest("Configuration file is inaccessible to other users"):
basic.wait_for_file(config_file)
basic.fail(f"sudo -u nobody ls {config_file}")
with subtest("Secrets variables have been substituted"):
basic.fail(f"grep -q @PSK_VALID@ {config_file}")
basic.fail(f"grep -q @PSK_SPECIAL@ {config_file}")
basic.succeed(f"grep -q @PSK_MISSING@ {config_file}")
basic.succeed(f"grep -q P@ssowrdWithSome@tSymbol {config_file}")
basic.succeed(f"grep -q 'PassowrdWith&symbol' {config_file}")
with subtest("WPA2 fallbacks have been generated"):
assert int(basic.succeed(f"grep -c sae-only {config_file}")) == 1
assert int(basic.succeed(f"grep -c mixed-wpa {config_file}")) == 2
# save file for manual inspection
basic.copy_from_vm(config_file)
with subtest("Daemon is running and accepting connections"): with subtest("Daemon is running and accepting connections"):
basic.wait_for_unit("wpa_supplicant-wlan1.service") machine.wait_for_unit("wpa_supplicant-wlan1.service")
status = basic.succeed("wpa_cli -i wlan1 status") status = machine.wait_until_succeeds("wpa_cli -i wlan1 status")
assert "Failed to connect" not in status, \ assert "Failed to connect" not in status, \
"Failed to connect to the daemon" "Failed to connect to the daemon"
with subtest("Daemon can be configured imperatively"): with subtest("Daemon can be configured imperatively"):
imperative.wait_for_unit("wpa_supplicant-wlan1.service") machine.succeed("wpa_cli -i wlan1 add_network")
imperative.wait_until_succeeds("wpa_cli -i wlan1 status") machine.succeed("wpa_cli -i wlan1 set_network 0 ssid '\"nixos-test\"'")
imperative.succeed("wpa_cli -i wlan1 add_network") machine.succeed("wpa_cli -i wlan1 set_network 0 psk '\"reproducibility\"'")
imperative.succeed("wpa_cli -i wlan1 set_network 0 ssid '\"nixos-test\"'") machine.succeed("wpa_cli -i wlan1 save_config")
imperative.succeed("wpa_cli -i wlan1 set_network 0 psk '\"reproducibility\"'") machine.succeed("grep -q nixos-test /etc/wpa_supplicant.conf")
imperative.succeed("wpa_cli -i wlan1 save_config")
imperative.succeed("grep -q nixos-test /etc/wpa_supplicant.conf")
machineSae.wait_for_unit("hostapd.service")
machineSae.copy_from_vm("/run/hostapd/wlan0.hostapd.conf")
with subtest("Daemon can connect to the SAE access point using SAE"):
machineSae.wait_until_succeeds(
"wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
)
with subtest("Daemon can connect to the SAE and WPA2 mixed access point using SAE"):
machineMixedUsingSae.wait_until_succeeds(
"wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
)
with subtest("Daemon can connect to the SAE and WPA2 mixed access point using WPA2"):
machineMixedUsingWpa2.wait_until_succeeds(
"wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
)
with subtest("Daemon can connect to the WPA2 access point using WPA2"):
machineWpa2.wait_until_succeeds(
"wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
)
''; '';
}) };
# Test connecting to a SAE-only hotspot using SAE
saeOnly = runConnectionTest "sae-only" {
fallbackToWPA2 = false;
networks.nixos-test-sae = {
pskRaw = "ext:psk_nixos_test";
authProtocols = [ "SAE" ];
};
};
# Test connecting to a mixed SAE/WPA2 hotspot using SAE
mixedUsingSae = runConnectionTest "mixed-using-sae" {
fallbackToWPA2 = false;
networks.nixos-test-mixed = {
pskRaw = "ext:psk_nixos_test";
authProtocols = [ "SAE" ];
};
};
# Test connecting to a mixed SAE/WPA2 hotspot using WPA2
mixedUsingWpa2 = runConnectionTest "mixed-using-wpa2" {
fallbackToWPA2 = true;
networks.nixos-test-mixed = {
pskRaw = "ext:psk_nixos_test";
authProtocols = [ "WPA-PSK-SHA256" ];
};
};
# Test connecting to a legacy WPA2-only hotspot using WPA2
legacy = runConnectionTest "legacy" {
fallbackToWPA2 = true;
networks.nixos-test-wpa2 = {
pskRaw = "ext:psk_nixos_test";
authProtocols = [ "WPA-PSK-SHA256" ];
};
};
}