mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-14 21:49:34 +03:00
nixos/wireless: reimplement secrets using ext_password_backend (#180872)
This commit is contained in:
commit
b94f259714
4 changed files with 205 additions and 225 deletions
|
@ -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`).
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue