From da4b908c01a8f7f256a5fb70491706fbaeca5927 Mon Sep 17 00:00:00 2001 From: Isabella Bastaja Date: Tue, 9 Jun 2026 13:44:59 +0200 Subject: Implement iwd-ext module --- share/nixos-modules/iwd-ext.nix | 123 +++++++++++++++++++++++++++++++++++----- share/system/client-common.nix | 1 + 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/share/nixos-modules/iwd-ext.nix b/share/nixos-modules/iwd-ext.nix index 8d07b57..faa52c0 100644 --- a/share/nixos-modules/iwd-ext.nix +++ b/share/nixos-modules/iwd-ext.nix @@ -11,20 +11,75 @@ let mkOption types recursiveUpdate + literalExpression + optionalAttrs + ; + + inherit (pkgs) + concatTextFile + ; + + inherit (lib.strings) + concatStringsSep + ; + + inherit (lib.attrsets) + mapAttrs' ; cfg = config.networking.wireless.iwd-ext; - ini = pkgs.formats.ini { }; - defaults = { - # without UseDefaultInterface, sometimes wlan0 simply goes AWOL with - # NetworkManager - # https://iwd.wiki.kernel.org/interface_lifecycle#interface_management_in_iwd - General.UseDefaultInterface = - with config.networking.networkmanager; - (enable && (wifi.backend == "iwd")); + + ini = pkgs.formats.ini { + listToValue = l: concatStringsSep "," (map toString l); }; + + defaults = + with config.networking.networkmanager; + optionalAttrs (enable && (wifi.backend == "iwd")) { + # without DefaultInterface, sometimes wlan0 simply goes AWOL with NetworkManager + # https://iwd.wiki.kernel.org/interface_lifecycle#interface_management_in_iwd + DriverQuirks.DefaultInterface = "?*"; + }; configFile = ini.generate "main.conf" (recursiveUpdate defaults cfg.settings); + networkTemplates = mapAttrs' ( + k: v: + let + name = "${k}.${v.networkType}"; + generatedConfig = ini.generate "${name}" v.config; + in + { + inherit name; + value = concatTextFile { + name = "${k}-network"; + files = [ generatedConfig ]; + destination = "/var/lib/iwd/${name}"; + }; + } + ) cfg.networks; + + networkTemplateDir = pkgs.symlinkJoin { + name = "iwd-network-templates"; + paths = builtins.attrValues networkTemplates; + }; + + networkSubmodule = types.submodule { + options = { + networkType = mkOption { + type = types.enum [ + "open" + "psk" + "8021x" + ]; + default = "psk"; + defaultText = literalExpression "psk"; + description = '' + Network type according to security method. See iwd.network (5) + ''; + }; + config = mkOption { type = ini.type; }; + }; + }; in { options.networking.wireless.iwd-ext = { @@ -60,14 +115,15 @@ in }; networks = mkOption { - type = types.attrsOf ini.type; - default = { }; + type = types.attrsOf networkSubmodule; example = { HomeNetwork = { - Security.PreSharedKey = "totallysecurepsk"; + networkType = "psk"; + config.Security.Passphrase = "totallysecurepsk"; }; - "@NETWORK_2_SSID@" = { - Security.PreSharedKey = "@NETWORK_2_PSK@"; + SomeOtherNetworkSSID = { + networkType = "psk"; + config.Security.Passphrase = "@NETWORK_2_PSK@"; }; }; @@ -77,6 +133,15 @@ in supported options. ''; }; + + secretsEnvFile = mkOption { + type = types.path; + example = "/run/secrets/network-secrets.env"; + description = lib.mdDoc '' + dotenv-formatted file containing secrets that get injected into + cfg.networks settings. + ''; + }; }; # end of options.networking.wireless.iwd-ext config = mkIf cfg.enable { @@ -90,7 +155,7 @@ in ''; } { - assertion = !config.networking.iwd.enable; + assertion = !config.networking.wireless.iwd.enable; message = '' Only one wireless daemon is allowed at the time: networking.wireless.iwd.enable and networking.wireless.iwd-ext.enable are @@ -111,8 +176,38 @@ in }; systemd.services.iwd = { + path = [ config.networking.resolvconf.package ]; wantedBy = [ "multi-user.target" ]; restartTriggers = [ configFile ]; + serviceConfig.ReadWritePaths = "-/etc/resolv.conf"; + }; + + systemd.services."prepopulate-iwd-netowks" = { + enable = true; + enableStrictShellChecks = true; + description = "Pre-populates network configs for iwd"; + before = [ "iwd.service" ]; + wantedBy = [ "iwd.service" ]; + after = [ "sops-install-secrets.service" ]; + + serviceConfig = { + EnvironmentFile = "${cfg.secretsEnvFile}"; + StateDirectory = "iwd"; + StateDirectoryMode = 0700; + }; + + path = [ pkgs.envsubst ]; + script = '' + set -xeu + + rm "$STATE_DIRECTORY/*.{open,psk,8021x}" + + for f in ${networkTemplateDir}/var/lib/iwd/*; do + cat "$f" | + sed -E 's/@([A-Za-z_][A-Za-z0-9_]*)@/$\{\1}/g' | + envsubst > "$STATE_DIRECTORY/$(basename "$f")" + done + ''; }; }; } diff --git a/share/system/client-common.nix b/share/system/client-common.nix index ac9fb80..18f84f5 100644 --- a/share/system/client-common.nix +++ b/share/system/client-common.nix @@ -8,6 +8,7 @@ { imports = [ ./steam.nix + ../nixos-modules/iwd-ext.nix ]; nixpkgs = { overlays = [ -- cgit v1.3.1