mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-08 11:35:37 +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
|
@ -45,20 +45,11 @@ let
|
|||
"update_config=1"
|
||||
])
|
||||
++ [ "pmf=1" ]
|
||||
++ optional (cfg.secretsFile != null)
|
||||
"ext_password_backend=file:${cfg.secretsFile}"
|
||||
++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"''
|
||||
++ 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
|
||||
mkNetwork = opts:
|
||||
let
|
||||
|
@ -90,8 +81,8 @@ let
|
|||
let
|
||||
deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device";
|
||||
configStr = if cfg.allowAuxiliaryImperativeNetworks
|
||||
then "-c /etc/wpa_supplicant.conf -I ${finalConfig}"
|
||||
else "-c ${finalConfig}";
|
||||
then "-c /etc/wpa_supplicant.conf -I ${pkgs.writeText "wpa_supplicant.conf" generatedConfig}"
|
||||
else "-c /etc/wpa_supplicant.conf";
|
||||
in {
|
||||
description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}";
|
||||
|
||||
|
@ -109,37 +100,14 @@ let
|
|||
serviceConfig.UMask = "066";
|
||||
serviceConfig.RuntimeDirectory = "wpa_supplicant";
|
||||
serviceConfig.RuntimeDirectoryMode = "700";
|
||||
serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null)
|
||||
(builtins.toString cfg.environmentFile);
|
||||
|
||||
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
|
||||
${optionalString cfg.allowAuxiliaryImperativeNetworks ''
|
||||
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}"
|
||||
|
||||
${if iface == null then ''
|
||||
|
@ -231,36 +199,34 @@ in {
|
|||
'';
|
||||
};
|
||||
|
||||
environmentFile = mkOption {
|
||||
secretsFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/run/secrets/wireless.env";
|
||||
example = "/run/secrets/wireless.conf";
|
||||
description = ''
|
||||
File consisting of lines of the form `varname=value`
|
||||
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
|
||||
the world-readable Nix store by defining them in the environment file and
|
||||
referring to them in option {option}`networking.wireless.networks`
|
||||
with the syntax `@varname@`. Example:
|
||||
the world-readable Nix store by defining them in the secrets file and
|
||||
referring to them in option [](#opt-networking.wireless.networks)
|
||||
with the syntax `ext:secretname`. Example:
|
||||
|
||||
```
|
||||
# content of /run/secrets/wireless.env
|
||||
PSK_HOME=mypassword
|
||||
PASS_WORK=myworkpassword
|
||||
```
|
||||
# content of /run/secrets/wireless.conf
|
||||
psk_home=mypassword
|
||||
psk_other=6a381cea59c7a2d6b30736ba0e6f397f7564a044bcdb7a327a1d16a1ed91b327
|
||||
pass_work=myworkpassword
|
||||
|
||||
```
|
||||
# wireless-related configuration
|
||||
networking.wireless.environmentFile = "/run/secrets/wireless.env";
|
||||
networking.wireless.secretsFile = "/run/secrets/wireless.conf";
|
||||
networking.wireless.networks = {
|
||||
home.psk = "@PSK_HOME@";
|
||||
home.pskRaw = "ext:psk_home";
|
||||
other.pskRaw = "ext:psk_other";
|
||||
work.auth = '''
|
||||
eap=PEAP
|
||||
identity="my-user@example.com"
|
||||
password="@PASS_WORK@"
|
||||
password=ext:pass_work
|
||||
''';
|
||||
};
|
||||
```
|
||||
|
@ -271,15 +237,16 @@ in {
|
|||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
psk = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
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.
|
||||
|
||||
::: {.warning}
|
||||
Be aware that this will be written to the nix store
|
||||
in plaintext! Use an environment variable instead.
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Use {var}`pskRaw` with an external
|
||||
reference to keep it safe.
|
||||
:::
|
||||
|
||||
::: {.note}
|
||||
|
@ -289,19 +256,28 @@ in {
|
|||
};
|
||||
|
||||
pskRaw = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
type = types.nullOr
|
||||
(types.strMatching "([[:xdigit:]]{64})|(ext:[^=]+)");
|
||||
default = null;
|
||||
example = "ext:name_of_the_secret_here";
|
||||
description = ''
|
||||
The network's pre-shared key in hex defaulting
|
||||
to being a network without any authentication.
|
||||
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 an environment variable instead.
|
||||
Be aware that this will be written to the Nix store
|
||||
in plaintext! Always use an external reference.
|
||||
:::
|
||||
|
||||
::: {.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 = ''
|
||||
eap=PEAP
|
||||
identity="user@example.com"
|
||||
password="@EXAMPLE_PASSWORD@"
|
||||
password=ext:example_password
|
||||
'';
|
||||
description = ''
|
||||
Use this option to configure advanced authentication methods like EAP.
|
||||
See
|
||||
{manpage}`wpa_supplicant.conf(5)`
|
||||
for example configurations.
|
||||
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 environment variable for secrets.
|
||||
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`.
|
||||
Mutually exclusive with {var}`psk` and {var}`pskRaw`.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
@ -393,13 +368,14 @@ in {
|
|||
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.
|
||||
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.
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -411,9 +387,7 @@ in {
|
|||
'';
|
||||
description = ''
|
||||
Extra configuration lines appended to the network block.
|
||||
See
|
||||
{manpage}`wpa_supplicant.conf(5)`
|
||||
for available options.
|
||||
See {manpage}`wpa_supplicant.conf(5)` for available options.
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -432,7 +406,7 @@ in {
|
|||
};
|
||||
|
||||
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
|
||||
|
||||
"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 {
|
||||
assertions = flip mapAttrsToList cfg.networks (name: cfg: {
|
||||
assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1;
|
||||
|
@ -517,6 +516,9 @@ in {
|
|||
|
||||
hardware.wirelessRegulatoryDatabase = true;
|
||||
|
||||
environment.etc."wpa_supplicant.conf" =
|
||||
lib.mkIf (!cfg.allowAuxiliaryImperativeNetworks) { text = generatedConfig; };
|
||||
|
||||
environment.systemPackages = [ pkgs.wpa_supplicant ];
|
||||
services.dbus.packages = optional cfg.dbusControlled pkgs.wpa_supplicant;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue