nixos/wpa_supplicant: allow duplicate network blocks (#441410)
This commit is contained in:
@@ -250,6 +250,8 @@
|
||||
|
||||
- `services.gitea` supports sending notifications with sendmail again. To do this, activate the parameter `services.gitea.mailerUseSendmail` and configure SMTP server.
|
||||
|
||||
- `networking.wireless.networks.<name>` now has an option to specify SSID, hence allowing duplicated SSID setup. The BSSID option is added along side with this.
|
||||
|
||||
- Revamp of the ACME certificate acquisication and renewal process to help scale systems with lots (100+) of certificates.
|
||||
|
||||
Units and targets have been reshaped to better support more specific dependency propagation and avoid
|
||||
|
||||
@@ -37,7 +37,9 @@ let
|
||||
mkWPA2Fallback = opts: opts // { authProtocols = subtractLists wpa3Protocols opts.authProtocols; };
|
||||
|
||||
# Networks attrset as a list
|
||||
networkList = mapAttrsToList (ssid: opts: opts // { inherit ssid; }) cfg.networks;
|
||||
# We use the ssid from the options, which defaults to the ssid but can be overridden.
|
||||
# The ssid in attrNames is hence unused here.
|
||||
networkList = attrValues cfg.networks;
|
||||
|
||||
# List of all networks (normal + generated fallbacks)
|
||||
allNetworks =
|
||||
@@ -89,6 +91,7 @@ let
|
||||
"key_mgmt=NONE"
|
||||
)
|
||||
]
|
||||
++ optional (opts.bssid != null) "bssid=${opts.bssid}"
|
||||
++ optional opts.hidden "scan_ssid=1"
|
||||
++ optional (pskString != null) "psk=${pskString}"
|
||||
++ optionals (opts.auth != null) (filter (x: x != "") (splitString "\n" opts.auth))
|
||||
@@ -280,170 +283,195 @@ in
|
||||
|
||||
networks = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options = {
|
||||
psk = mkOption {
|
||||
type = types.nullOr (types.strMatching "[[:print:]]{8,63}");
|
||||
default = null;
|
||||
description = ''
|
||||
The network's pre-shared key in plaintext defaulting
|
||||
to being a network without any authentication.
|
||||
types.submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
ssid = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = ''
|
||||
You could use this field to override the network's ssid.
|
||||
This can be useful to, for example, specify two networks
|
||||
that share the same SSID but not the same password.
|
||||
Specifying the BSSID of the network can make two entries of
|
||||
the same SSID show up as different ones in wpa_cli.
|
||||
'';
|
||||
};
|
||||
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Use {var}`pskRaw` with an external
|
||||
reference to keep it safe.
|
||||
:::
|
||||
bssid = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "02:00:00:00:00:01";
|
||||
description = ''
|
||||
If set, this network block is used only when associating with
|
||||
the AP using the configured BSSID.
|
||||
'';
|
||||
};
|
||||
|
||||
::: {.note}
|
||||
Mutually exclusive with {var}`pskRaw`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
psk = mkOption {
|
||||
type = types.nullOr (types.strMatching "[[:print:]]{8,63}");
|
||||
default = null;
|
||||
description = ''
|
||||
The network's pre-shared key in plaintext defaulting
|
||||
to being a network without any authentication.
|
||||
|
||||
pskRaw = mkOption {
|
||||
type = types.nullOr (types.strMatching "([[:xdigit:]]{64})|(ext:[^=]+)");
|
||||
default = null;
|
||||
example = "ext:name_of_the_secret_here";
|
||||
description = ''
|
||||
Either the raw pre-shared key in hexadecimal format
|
||||
or the name of the secret (as defined inside
|
||||
[](#opt-networking.wireless.secretsFile) and prefixed
|
||||
with `ext:`) containing the network pre-shared key.
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Use {var}`pskRaw` with an external
|
||||
reference to keep it safe.
|
||||
:::
|
||||
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Always use an external reference.
|
||||
:::
|
||||
::: {.note}
|
||||
Mutually exclusive with {var}`pskRaw`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
::: {.note}
|
||||
The external secret can be either the plaintext
|
||||
passphrase or the raw pre-shared key.
|
||||
:::
|
||||
pskRaw = mkOption {
|
||||
type = types.nullOr (types.strMatching "([[:xdigit:]]{64})|(ext:[^=]+)");
|
||||
default = null;
|
||||
example = "ext:name_of_the_secret_here";
|
||||
description = ''
|
||||
Either the raw pre-shared key in hexadecimal format
|
||||
or the name of the secret (as defined inside
|
||||
[](#opt-networking.wireless.secretsFile) and prefixed
|
||||
with `ext:`) containing the network pre-shared key.
|
||||
|
||||
::: {.note}
|
||||
Mutually exclusive with {var}`psk` and {var}`auth`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Always use an external reference.
|
||||
:::
|
||||
|
||||
authProtocols = mkOption {
|
||||
default = [
|
||||
# WPA2 and WPA3
|
||||
"WPA-PSK"
|
||||
"WPA-EAP"
|
||||
"SAE"
|
||||
# 802.11r variants of the above
|
||||
"FT-PSK"
|
||||
"FT-EAP"
|
||||
"FT-SAE"
|
||||
];
|
||||
# The list can be obtained by running this command
|
||||
# awk '
|
||||
# /^# key_mgmt: /{ run=1 }
|
||||
# /^#$/{ run=0 }
|
||||
# /^# [A-Z0-9-]{2,}/{ if(run){printf("\"%s\"\n", $2)} }
|
||||
# ' /run/current-system/sw/share/doc/wpa_supplicant/wpa_supplicant.conf.example
|
||||
type = types.listOf (
|
||||
types.enum [
|
||||
::: {.note}
|
||||
The external secret can be either the plaintext
|
||||
passphrase or the raw pre-shared key.
|
||||
:::
|
||||
|
||||
::: {.note}
|
||||
Mutually exclusive with {var}`psk` and {var}`auth`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
authProtocols = mkOption {
|
||||
default = [
|
||||
# WPA2 and WPA3
|
||||
"WPA-PSK"
|
||||
"WPA-EAP"
|
||||
"IEEE8021X"
|
||||
"NONE"
|
||||
"WPA-NONE"
|
||||
"SAE"
|
||||
# 802.11r variants of the above
|
||||
"FT-PSK"
|
||||
"FT-EAP"
|
||||
"FT-EAP-SHA384"
|
||||
"WPA-PSK-SHA256"
|
||||
"WPA-EAP-SHA256"
|
||||
"SAE"
|
||||
"FT-SAE"
|
||||
"WPA-EAP-SUITE-B"
|
||||
"WPA-EAP-SUITE-B-192"
|
||||
"OSEN"
|
||||
"FILS-SHA256"
|
||||
"FILS-SHA384"
|
||||
"FT-FILS-SHA256"
|
||||
"FT-FILS-SHA384"
|
||||
"OWE"
|
||||
"DPP"
|
||||
]
|
||||
);
|
||||
description = ''
|
||||
The list of authentication protocols accepted by this network.
|
||||
This corresponds to the `key_mgmt` option in wpa_supplicant.
|
||||
'';
|
||||
];
|
||||
# The list can be obtained by running this command
|
||||
# awk '
|
||||
# /^# key_mgmt: /{ run=1 }
|
||||
# /^#$/{ run=0 }
|
||||
# /^# [A-Z0-9-]{2,}/{ if(run){printf("\"%s\"\n", $2)} }
|
||||
# ' /run/current-system/sw/share/doc/wpa_supplicant/wpa_supplicant.conf.example
|
||||
type = types.listOf (
|
||||
types.enum [
|
||||
"WPA-PSK"
|
||||
"WPA-EAP"
|
||||
"IEEE8021X"
|
||||
"NONE"
|
||||
"WPA-NONE"
|
||||
"FT-PSK"
|
||||
"FT-EAP"
|
||||
"FT-EAP-SHA384"
|
||||
"WPA-PSK-SHA256"
|
||||
"WPA-EAP-SHA256"
|
||||
"SAE"
|
||||
"FT-SAE"
|
||||
"WPA-EAP-SUITE-B"
|
||||
"WPA-EAP-SUITE-B-192"
|
||||
"OSEN"
|
||||
"FILS-SHA256"
|
||||
"FILS-SHA384"
|
||||
"FT-FILS-SHA256"
|
||||
"FT-FILS-SHA384"
|
||||
"OWE"
|
||||
"DPP"
|
||||
]
|
||||
);
|
||||
description = ''
|
||||
The list of authentication protocols accepted by this network.
|
||||
This corresponds to the `key_mgmt` option in wpa_supplicant.
|
||||
'';
|
||||
};
|
||||
|
||||
auth = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = ''
|
||||
eap=PEAP
|
||||
identity="user@example.com"
|
||||
password=ext:example_password
|
||||
'';
|
||||
description = ''
|
||||
Use this option to configure advanced authentication methods
|
||||
like EAP. See {manpage}`wpa_supplicant.conf(5)` for example
|
||||
configurations.
|
||||
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Use an external reference like
|
||||
`ext:secretname` for secrets.
|
||||
:::
|
||||
|
||||
::: {.note}
|
||||
Mutually exclusive with {var}`psk` and {var}`pskRaw`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
hidden = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Set this to `true` if the SSID of the network is hidden.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{ echelon = {
|
||||
hidden = true;
|
||||
psk = "abcdefgh";
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
By default, all networks will get same priority group (0). If
|
||||
some of the networks are more desirable, this field can be used
|
||||
to change the order in which wpa_supplicant goes through the
|
||||
networks when selecting a BSS. The priority groups will be
|
||||
iterated in decreasing priority (i.e., the larger the priority
|
||||
value, the sooner the network is matched against the scan
|
||||
results). Within each priority group, networks will be selected
|
||||
based on security policy, signal strength, etc.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = ''
|
||||
bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
|
||||
'';
|
||||
description = ''
|
||||
Extra configuration lines appended to the network block.
|
||||
See {manpage}`wpa_supplicant.conf(5)` for available options.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
auth = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = ''
|
||||
eap=PEAP
|
||||
identity="user@example.com"
|
||||
password=ext:example_password
|
||||
'';
|
||||
description = ''
|
||||
Use this option to configure advanced authentication methods
|
||||
like EAP. See {manpage}`wpa_supplicant.conf(5)` for example
|
||||
configurations.
|
||||
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Use an external reference like
|
||||
`ext:secretname` for secrets.
|
||||
:::
|
||||
|
||||
::: {.note}
|
||||
Mutually exclusive with {var}`psk` and {var}`pskRaw`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
hidden = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Set this to `true` if the SSID of the network is hidden.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{ echelon = {
|
||||
hidden = true;
|
||||
psk = "abcdefgh";
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
By default, all networks will get same priority group (0). If
|
||||
some of the networks are more desirable, this field can be used
|
||||
to change the order in which wpa_supplicant goes through the
|
||||
networks when selecting a BSS. The priority groups will be
|
||||
iterated in decreasing priority (i.e., the larger the priority
|
||||
value, the sooner the network is matched against the scan
|
||||
results). Within each priority group, networks will be selected
|
||||
based on security policy, signal strength, etc.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = ''
|
||||
bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
|
||||
'';
|
||||
description = ''
|
||||
Extra configuration lines appended to the network block.
|
||||
See {manpage}`wpa_supplicant.conf(5)` for available options.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
description = ''
|
||||
The network definitions to automatically connect to when
|
||||
|
||||
@@ -13,8 +13,31 @@ let
|
||||
|
||||
naughtyPassphrase = ''!,./;'[]\-=<>?:"{}|_+@$%^&*()`~ # ceci n'est pas un commentaire'';
|
||||
|
||||
runBssidTest =
|
||||
name: expectedBssid: extraConfig:
|
||||
runSimulatorTest name extraConfig ''
|
||||
with subtest("Daemon can connect to the right access point"):
|
||||
machine.wait_for_unit("wpa_supplicant-wlan1.service")
|
||||
machine.wait_until_succeeds(
|
||||
"wpa_cli -i wlan1 status | grep -q wpa_state=COMPLETED"
|
||||
)
|
||||
machine.wait_until_succeeds(
|
||||
"wpa_cli -i wlan1 status | grep -q bssid=${expectedBssid}"
|
||||
)
|
||||
'';
|
||||
|
||||
runConnectionTest =
|
||||
name: extraConfig:
|
||||
runSimulatorTest name extraConfig ''
|
||||
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"
|
||||
)
|
||||
'';
|
||||
|
||||
runSimulatorTest =
|
||||
name: extraConfig: extraTestScript:
|
||||
runTest {
|
||||
name = "wpa_supplicant-${name}";
|
||||
inherit meta;
|
||||
@@ -50,12 +73,22 @@ let
|
||||
bssid = "02:00:00:00:00:01";
|
||||
};
|
||||
wlan0-2 = {
|
||||
ssid = "nixos-test-mixed";
|
||||
authentication = {
|
||||
mode = "wpa3-sae-transition";
|
||||
saeAddToMacAllow = true;
|
||||
saePasswordsFile = pkgs.writeText "password" naughtyPassphrase;
|
||||
wpaPasswordFile = pkgs.writeText "password" naughtyPassphrase;
|
||||
};
|
||||
bssid = "02:00:00:00:00:02";
|
||||
};
|
||||
wlan0-3 = {
|
||||
ssid = "nixos-test-wpa2";
|
||||
authentication = {
|
||||
mode = "wpa2-sha256";
|
||||
wpaPassword = naughtyPassphrase;
|
||||
};
|
||||
bssid = "02:00:00:00:00:02";
|
||||
bssid = "02:00:00:00:00:03";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -85,11 +118,7 @@ let
|
||||
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"
|
||||
)
|
||||
${extraTestScript}
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -129,6 +158,18 @@ in
|
||||
psk = "password";
|
||||
authProtocols = [ "SAE" ];
|
||||
};
|
||||
|
||||
# Test duplicate SSID generation
|
||||
duplicate1 = {
|
||||
ssid = "duplicate";
|
||||
bssid = "00:00:00:00:00:01";
|
||||
psk = "password";
|
||||
};
|
||||
duplicate2 = {
|
||||
ssid = "duplicate";
|
||||
bssid = "00:00:00:00:00:02";
|
||||
psk = "password";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -148,6 +189,12 @@ in
|
||||
assert int(machine.succeed(f"grep -c sae-only {config_file}")) == 1
|
||||
assert int(machine.succeed(f"grep -c mixed-wpa {config_file}")) == 2
|
||||
|
||||
with subtest("Duplicate SSID network blocks have been generated"):
|
||||
# more duplication due to fallbacks
|
||||
assert int(machine.succeed(f"grep -c duplicate {config_file}")) == 4
|
||||
assert int(machine.succeed(f"grep -c bssid=00:00:00:00:00:01 {config_file}")) == 2
|
||||
assert int(machine.succeed(f"grep -c bssid=00:00:00:00:00:02 {config_file}")) == 2
|
||||
|
||||
# save file for manual inspection
|
||||
machine.copy_from_vm(config_file)
|
||||
'';
|
||||
@@ -222,4 +269,23 @@ in
|
||||
authProtocols = [ "WPA-PSK-SHA256" ];
|
||||
};
|
||||
};
|
||||
|
||||
# Test connection with the highest prio "matching" network block found.
|
||||
# "Matching" meaning with the right SSID and BSSID
|
||||
bssidGuard = runBssidTest "bssid-guard" "02:00:00:00:00:02" {
|
||||
networks = {
|
||||
"1_first" = {
|
||||
ssid = "nixos-test-mixed";
|
||||
bssid = "02:00:00:00:00:01";
|
||||
pskRaw = "ext:psk_nixos_test";
|
||||
};
|
||||
"2_second" = {
|
||||
ssid = "nixos-test-mixed";
|
||||
bssid = "02:00:00:00:00:02";
|
||||
pskRaw = "ext:psk_nixos_test";
|
||||
priority = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user