-
-
Notifications
You must be signed in to change notification settings - Fork 13.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nixos/iwd: add networks
and interfaces
option
#75800
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,16 +4,132 @@ with lib; | |
|
||
let | ||
cfg = config.networking.wireless.iwd; | ||
|
||
encodeFileName = pkgs.writeScript "encode" '' | ||
#! ${pkgs.runtimeShell} -e | ||
echo "=$(printf "$1" | ${pkgs.unixtools.xxd}/bin/xxd -pu)" | ||
''; | ||
|
||
# If a network consists of alphanumeric chars and `_` and `-`, the network will be stored | ||
# as `/var/lib/iwd/<ssid>.<ext>`. In any other case a hex-encoded version of the SSID will | ||
# be stored with an `=` as prefix. | ||
encodeIfNeeded = file: ext: let | ||
allowedChars = stringToCharacters "01234556789abcdefghijklmnopqrstuvwxyz-_ "; | ||
skipEncoding = all (char: elem (toLower char) allowedChars) (stringToCharacters file); | ||
in | ||
"/var/lib/iwd/${if skipEncoding then file else "$(${encodeFileName} \"${file}\")"}.${ext}"; | ||
|
||
# The file extension of the network-config determines the type of the network. Currently, | ||
# we support public networks (`open`) and networks protected with a passphrase (`psk`). | ||
# Anything else, like 802.1x networks are not supported by this module. | ||
determineType = cfg: if cfg.psk != null || cfg.passphrase != null then "psk" else "open"; | ||
|
||
# Generates a name-value-pair with configurations for a SSID. For further information | ||
# about the configuration options, please refer to the upstream docs: | ||
# | ||
# https://iwd.wiki.kernel.org/networkconfigurationsettings | ||
predefinedNetworks = flip mapAttrs cfg.networks (name: cfg: { | ||
extension = determineType cfg; | ||
|
||
# Cannot use the SSID here: the hex-encode is done at build-time, not eval time | ||
# and several special chars in a file name would break `writeText`. | ||
file = pkgs.writeText "ssid" (generators.toINI {} ({ | ||
Settings.AutoConnect = boolToString cfg.autoConnect; | ||
Settings.Hidden = boolToString cfg.hidden; | ||
} // (optionalAttrs (cfg.psk != null) { | ||
Security.PreSharedKey = cfg.psk; | ||
}) // (optionalAttrs (cfg.passphrase != null) { | ||
Security.Passprase = cfg.passphrase; | ||
}))); | ||
}); | ||
in { | ||
options.networking.wireless.iwd.enable = mkEnableOption "iwd"; | ||
options.networking.wireless.iwd = { | ||
enable = mkEnableOption "iwd"; | ||
|
||
interfaces = mkOption { | ||
type = types.nullOr (types.listOf types.str); | ||
example = literalExample '' | ||
[ "wlp2s0" ] | ||
''; | ||
default = null; | ||
Ma27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
description = '' | ||
List of interfaces to use. | ||
''; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the interfaces change be done separately?
All said, what's the use for specifying interfaces explicitly anyway? Just curious :). And the rest is exciting and I think close to merge-ready, hence the suggestion for splitting off the interface-related bits. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It might be possible that I'm missing something, but iwd constantly tried to use wlan0 by default which doesn't even exist on my system. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dtzWill any updates here? |
||
|
||
networks = mkOption { | ||
default = {}; | ||
example = literalExample '' | ||
{ "karlsruhe.freifunk.net" = {}; | ||
"secured".passphrase = "12345678"; | ||
"network-with-psk".psk = "bf4c086e055e492373a0f59ff614f61fc2d597a56db94e17a165f9ef1d42c066"; | ||
}; | ||
''; | ||
|
||
description = '' | ||
Declarative configuration of wifi networks for | ||
<citerefentry><refentrytitle>iwd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. | ||
|
||
All networks will be stored in | ||
<literal>/var/lib/iwd/<name>.<type></literal>. | ||
|
||
Since each network is stored in its own file, declarative networks can be used in an | ||
environment with imperatively added networks via | ||
<citerefentry><refentrytitle>iwctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. | ||
''; | ||
|
||
type = types.attrsOf (types.submodule { | ||
options = { | ||
psk = mkOption { | ||
type = types.nullOr (types.fixedLengthString 64); | ||
default = null; | ||
description = '' | ||
WPA PSK for the currently defined network. This can be generated by using | ||
<citerefentry><refentrytitle>wpa_passphrase</refentrytitle><manvolnum>8</manvolnum></citerefentry>. | ||
''; | ||
}; | ||
|
||
passphrase = mkOption { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And manually constructing the INI file during runtime has the benefit that you can convert the Note that if people don't care about this, they can still do Furthermore, 802.1x support can be added similarly, as requested by @onny. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Relevant issue: #24288 |
||
type = types.nullOr (types.lengthCheckedString 8 63); | ||
default = null; | ||
description = '' | ||
Raw passphrase for the currently defined network. | ||
''; | ||
}; | ||
|
||
autoConnect = mkOption { | ||
default = true; | ||
type = types.bool; | ||
description = '' | ||
Disable automatic connection to the network. | ||
''; | ||
}; | ||
|
||
hidden = mkOption { | ||
default = false; | ||
type = types.bool; | ||
description = '' | ||
Used for hidden networks that don't respond to scans unless | ||
their SSID is explicitly specified. | ||
''; | ||
}; | ||
}; | ||
}); | ||
}; | ||
}; | ||
|
||
config = mkIf cfg.enable { | ||
assertions = [{ | ||
assertion = !config.networking.wireless.enable; | ||
message = '' | ||
Only one wireless daemon is allowed at the time: networking.wireless.enable and networking.wireless.iwd.enable are mutually exclusive. | ||
''; | ||
}]; | ||
}] ++ (flip mapAttrsToList cfg.networks (name: value: { | ||
assertion = value.psk != null -> value.passphrase == null; | ||
message = '' | ||
Cannot set WPA PSK and raw passphrase at the same time for network ${name}! | ||
''; | ||
})); | ||
|
||
# for iwctl | ||
environment.systemPackages = [ pkgs.iwd ]; | ||
|
@@ -22,8 +138,33 @@ in { | |
|
||
systemd.packages = [ pkgs.iwd ]; | ||
|
||
systemd.services.iwd.preStart = mkIf (cfg.networks != {}) (let | ||
ssids = flip mapAttrsToList predefinedNetworks (file: content: { | ||
inherit (content) file; | ||
local = encodeIfNeeded file content.extension; | ||
}); | ||
in '' | ||
# Remove all network-configs from `/var/lib/iwd` that are symlinks to a store-path, | ||
# but aren't declared in `cfg.networks` (i.e. all networks that were "removed" from | ||
# `cfg.networks`). | ||
find /var/lib/iwd -type l -lname '${builtins.storeDir}/*' ${optionalString (ssids != {}) '' | ||
-not \( ${concatMapStringsSep " -o " ({ local, ... }: | ||
"-name '${baseNameOf local}*'") | ||
ssids} \) \ | ||
''} -delete | ||
|
||
${concatMapStrings ({ file, local }: '' | ||
ln -sf '${file}' "${local}" | ||
'') ssids} | ||
''); | ||
|
||
systemd.services.iwd.serviceConfig.ExecStart = mkIf (cfg.interfaces != null) [ | ||
"" | ||
"${pkgs.iwd}/libexec/iwd -i ${concatStringsSep " -i " cfg.interfaces}" | ||
]; | ||
|
||
systemd.services.iwd.wantedBy = [ "multi-user.target" ]; | ||
}; | ||
|
||
meta.maintainers = with lib.maintainers; [ mic92 dtzWill ]; | ||
meta.maintainers = with lib.maintainers; [ mic92 dtzWill ma27 ]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
From bd0a222cca9973d7662a92c794ae04b40c29d914 Mon Sep 17 00:00:00 2001 | ||
From: Maximilian Bosch <maximilian@mbosch.me> | ||
Date: Thu, 30 Jul 2020 00:43:20 +0200 | ||
Subject: [PATCH] agent/network_connect_psk: check if the config-file for the | ||
SSID points to a store path | ||
|
||
If the network is declaratively configured by NixOS, it must not be | ||
modified imperatively. | ||
--- | ||
src/dbus.c | 6 ++++++ | ||
src/dbus.h | 1 + | ||
src/network.c | 14 +++++++++++++- | ||
3 files changed, 20 insertions(+), 1 deletion(-) | ||
|
||
diff --git a/src/dbus.c b/src/dbus.c | ||
index ceede5c..f8b8109 100644 | ||
--- a/src/dbus.c | ||
+++ b/src/dbus.c | ||
@@ -114,6 +114,12 @@ struct l_dbus_message *dbus_error_not_supported(struct l_dbus_message *msg) | ||
"Operation not supported"); | ||
} | ||
|
||
+struct l_dbus_message *dbus_error_file_is_a_nix_store_path(struct l_dbus_message *msg, char *ssid) | ||
+{ | ||
+ return l_dbus_message_new_error(msg, IWD_SERVICE ".NotSupportNixOS", | ||
+ "Cannot update SSID %s as it's configured in a file that points to a store path! Please update your NixOS config accordingly!", ssid); | ||
+} | ||
Ma27 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
+ | ||
struct l_dbus_message *dbus_error_no_agent(struct l_dbus_message *msg) | ||
{ | ||
return l_dbus_message_new_error(msg, IWD_SERVICE ".NoAgent", | ||
diff --git a/src/dbus.h b/src/dbus.h | ||
index ebae85d..adb0241 100644 | ||
--- a/src/dbus.h | ||
+++ b/src/dbus.h | ||
@@ -59,6 +59,7 @@ struct l_dbus_message *dbus_error_invalid_format(struct l_dbus_message *msg); | ||
struct l_dbus_message *dbus_error_already_exists(struct l_dbus_message *msg); | ||
struct l_dbus_message *dbus_error_not_found(struct l_dbus_message *msg); | ||
struct l_dbus_message *dbus_error_not_supported(struct l_dbus_message *msg); | ||
+struct l_dbus_message *dbus_error_file_is_a_nix_store_path(struct l_dbus_message *msg, char *ssid); | ||
struct l_dbus_message *dbus_error_no_agent(struct l_dbus_message *msg); | ||
struct l_dbus_message *dbus_error_not_connected(struct l_dbus_message *msg); | ||
struct l_dbus_message *dbus_error_not_configured(struct l_dbus_message *msg); | ||
diff --git a/src/network.c b/src/network.c | ||
index 70541b5..d510f5e 100644 | ||
--- a/src/network.c | ||
+++ b/src/network.c | ||
@@ -851,8 +851,20 @@ static struct l_dbus_message *network_connect_psk(struct network *network, | ||
|
||
l_debug("ask_passphrase: %s", | ||
network->ask_passphrase ? "true" : "false"); | ||
- | ||
if (network->ask_passphrase) { | ||
+ char store_dir[] = "@storeDir@"; | ||
+ char full_path[PATH_MAX]; | ||
+ char *cfgpath; | ||
+ if (network->info != NULL) { | ||
+ cfgpath = network->info->ops->get_file_path(network->info); | ||
+ int ret = readlink(cfgpath, full_path, sizeof(full_path)); | ||
+ l_free(cfgpath); | ||
+ | ||
+ if (ret != -1 && strncmp(full_path, store_dir, strlen(store_dir)) == 0) { | ||
+ return dbus_error_file_is_a_nix_store_path(message, network->ssid); | ||
+ } | ||
+ } | ||
+ | ||
network->ask_passphrase = false; | ||
|
||
network->agent_request = | ||
-- | ||
2.25.4 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since nothing actually needs these values at eval time, you can instead do this check during runtime in
preStart
directly.