{ config, lib, pkgs, namespace, ... }: with lib; let cfg = config.${namespace}.network; profiles = let make = name: profile: nameValuePair "${name}" { connection = { id = name; type = profile.type; autoconnect = profile.autoconnect; autoconnect-retries = profile.autoconnect-retries; autoconnect-priority = profile.priority; interface-name = cfg.ipv4.interface; }; ipv4 = { method = cfg.ipv4.method; } // ( if (cfg.ipv4.method == "auto") then { } else { address = "${cfg.ipv4.address}\\24"; gateway = cfg.ipv4.gateway; dns = cfg.ipv4.dns; } ); ipv6 = { addr-gen-mode = "stable-privacy"; method = "auto"; }; wifi = mkIf (profile.type == "wifi") { mode = "infrastructure"; ssid = profile.ssid; }; wifi-security = mkIf (profile.type == "wifi") { key-mgmt = profile.keyMgmt; psk = profile.psk; }; }; in mapAttrs' make cfg.networkmanager.profiles; in { imports = [ ./options.nix ]; config = { assertions = [ { assertion = cfg.hostName != ""; message = "mjallen.network.hostName must be set to a non-empty string."; } { assertion = cfg.ipv4.method == "auto" || cfg.ipv4.method == "manual"; message = "mjallen.network.ipv4.method must be either \"auto\" or \"manual\" (got \"${cfg.ipv4.method}\")."; } { assertion = cfg.ipv4.method != "manual" || cfg.ipv4.interface != ""; message = "mjallen.network.ipv4.interface must be set when ipv4.method is \"manual\"."; } { assertion = cfg.ipv4.method != "manual" || cfg.ipv4.address != ""; message = "mjallen.network.ipv4.address must be set when ipv4.method is \"manual\"."; } { assertion = cfg.ipv4.method != "manual" || cfg.ipv4.gateway != ""; message = "mjallen.network.ipv4.gateway must be set when ipv4.method is \"manual\"."; } { assertion = cfg.nat.enable -> cfg.nat.externalInterface != ""; message = "mjallen.network.nat.externalInterface must be set when NAT is enabled."; } ]; systemd = { services = { NetworkManager-wait-online.enable = false; systemd-networkd-wait-online.enable = lib.mkForce false; systemd-networkd.stopIfChanged = false; systemd-resolved.stopIfChanged = false; }; network.wait-online.enable = false; }; networking = { hostName = lib.mkForce cfg.hostName; # Use networkd if enabled useNetworkd = lib.mkDefault true; # Set default gateway and nameservers if in manual mode defaultGateway = lib.mkIf (cfg.ipv4.method == "manual") { address = cfg.ipv4.gateway; interface = lib.mkIf (cfg.ipv4.interface != "") cfg.ipv4.interface; }; nameservers = lib.mkIf (cfg.ipv4.method == "manual") [ cfg.ipv4.dns ]; # Set hostId if provided hostId = lib.mkIf (cfg.hostId != "") cfg.hostId; # Configure NAT if enabled nat = lib.mkIf cfg.nat.enable { enable = true; internalInterfaces = cfg.nat.internalInterfaces; externalInterface = cfg.nat.externalInterface; enableIPv6 = cfg.nat.enableIPv6; }; # Configure firewall firewall = { enable = cfg.firewall.enable; allowPing = cfg.firewall.allowPing; allowedTCPPorts = cfg.firewall.allowedTCPPorts; allowedUDPPorts = cfg.firewall.allowedUDPPorts; trustedInterfaces = cfg.firewall.trustedInterfaces; # Default port ranges for KDE Connect allowedTCPPortRanges = [ { from = 1714; to = 1764; } ]; allowedUDPPortRanges = config.networking.firewall.allowedTCPPortRanges; # Extra firewall commands extraCommands = lib.mkIf (cfg.extraFirewallCommands != "") cfg.extraFirewallCommands; }; # Enable iwd daemon when requested. # When iwd is enabled alongside NetworkManager, iwd acts as the WiFi # backend for NM (iwd handles scanning/association; NM handles # connection management). They are not mutually exclusive. wireless.iwd = lib.mkIf cfg.iwd.enable { enable = true; settings = cfg.iwd.settings; }; # Configure NetworkManager when enabled networkmanager = mkIf cfg.networkmanager.enable { enable = true; # Use iwd as the WiFi backend when iwd is also enabled wifi.backend = mkIf cfg.iwd.enable "iwd"; wifi.powersave = cfg.networkmanager.powersave; settings.connectivity.uri = mkDefault "http://nmcheck.gnome.org/check_network_status.txt"; plugins = with pkgs; [ networkmanager-fortisslvpn networkmanager-iodine networkmanager-l2tp networkmanager-openconnect networkmanager-openvpn networkmanager-sstp networkmanager-strongswan networkmanager-vpnc ]; # Configure WiFi profiles if any are defined ensureProfiles = mkIf (cfg.networkmanager.profiles != { }) { environmentFiles = lib.optional (config.sops.secrets ? wifi) config.sops.secrets.wifi.path; profiles = profiles; }; }; }; }; }