nixpkgs/nixos/modules/services/networking/networkmanager.nix

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

584 lines
18 KiB
Nix
Raw Normal View History

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.networking.networkmanager;
2023-08-30 10:36:41 +00:00
delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != [ ];
enableIwd = cfg.wifi.backend == "iwd";
mkValue = v:
if v == true then "yes"
else if v == false then "no"
else if lib.isInt v then toString v
else v;
mkSection = name: attrs: ''
[${name}]
${
lib.concatStringsSep "\n"
(lib.mapAttrsToList
(k: v: "${k}=${mkValue v}")
(lib.filterAttrs
(k: v: v != null)
attrs))
}
'';
configFile = pkgs.writeText "NetworkManager.conf" (lib.concatStringsSep "\n" [
(mkSection "main" {
plugins = "keyfile";
dhcp = cfg.dhcp;
dns = cfg.dns;
# If resolvconf is disabled that means that resolv.conf is managed by some other module.
rc-manager =
if config.networking.resolvconf.enable then "resolvconf"
else "unmanaged";
firewall-backend = cfg.firewallBackend;
})
(mkSection "keyfile" {
unmanaged-devices =
2023-08-30 10:36:41 +00:00
if cfg.unmanaged == [ ] then null
else lib.concatStringsSep ";" cfg.unmanaged;
})
(mkSection "logging" {
audit = config.security.audit.enable;
level = cfg.logLevel;
})
(mkSection "connection" cfg.connectionConfig)
(mkSection "device" {
"wifi.scan-rand-mac-address" = cfg.wifi.scanRandMacAddress;
"wifi.backend" = cfg.wifi.backend;
})
cfg.extraConfig
]);
/*
[network-manager]
Identity=unix-group:networkmanager
Action=org.freedesktop.NetworkManager.*
ResultAny=yes
ResultInactive=no
ResultActive=yes
[modem-manager]
Identity=unix-group:networkmanager
2014-02-08 19:16:34 +00:00
Action=org.freedesktop.ModemManager*
ResultAny=yes
ResultInactive=no
ResultActive=yes
*/
polkitConf = ''
polkit.addRule(function(action, subject) {
if (
subject.isInGroup("networkmanager")
&& (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
2014-02-08 19:16:34 +00:00
|| action.id.indexOf("org.freedesktop.ModemManager") == 0
))
{ return polkit.Result.YES; }
});
'';
ns = xs: pkgs.writeText "nameservers" (
concatStrings (map (s: "nameserver ${s}\n") xs)
);
overrideNameserversScript = pkgs.writeScript "02overridedns" ''
#!/bin/sh
PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]}
tmp=$(mktemp)
sed '/nameserver /d' /etc/resolv.conf > $tmp
grep 'nameserver ' /etc/resolv.conf | \
grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns
cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf
rm -f $tmp $tmp.ns
'';
dispatcherTypesSubdirMap = {
2019-08-13 21:52:01 +00:00
basic = "";
pre-up = "pre-up.d/";
pre-down = "pre-down.d/";
};
macAddressOpt = mkOption {
2023-08-30 10:36:41 +00:00
type = types.either types.str (types.enum [ "permanent" "preserve" "random" "stable" ]);
default = "preserve";
example = "00:11:22:33:44:55";
description = lib.mdDoc ''
Set the MAC address of the interface.
- `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface
- `"permanent"`: Use the permanent MAC address of the device
- `"preserve"`: Dont change the MAC address of the device upon activation
- `"random"`: Generate a randomized value upon each connect
- `"stable"`: Generate a stable, hashed MAC address
'';
};
packages = [
pkgs.modemmanager
pkgs.networkmanager
]
++ cfg.plugins
++ lib.optionals (!delegateWireless && !enableIwd) [
pkgs.wpa_supplicant
];
2023-08-30 10:36:41 +00:00
in
{
meta = {
maintainers = teams.freedesktop.members;
};
###### interface
options = {
networking.networkmanager = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Whether to use NetworkManager to obtain an IP address and other
configuration for all network interfaces that are not manually
configured. If enabled, a group `networkmanager`
will be created. Add all users that should have permission
to change network settings to this group.
'';
};
connectionConfig = mkOption {
type = with types; attrsOf (nullOr (oneOf [
bool
int
str
]));
2023-08-30 10:36:41 +00:00
default = { };
description = lib.mdDoc ''
Configuration for the [connection] section of NetworkManager.conf.
Refer to
[
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#id-1.2.3.11
](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
or
{manpage}`NetworkManager.conf(5)`
for more information.
'';
};
2018-06-17 17:03:29 +00:00
extraConfig = mkOption {
type = types.lines;
default = "";
description = lib.mdDoc ''
Configuration appended to the generated NetworkManager.conf.
Refer to
[
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
or
{manpage}`NetworkManager.conf(5)`
for more information.
2018-06-17 17:03:29 +00:00
'';
};
unmanaged = mkOption {
type = types.listOf types.str;
2023-08-30 10:36:41 +00:00
default = [ ];
description = lib.mdDoc ''
List of interfaces that will not be managed by NetworkManager.
Interface name can be specified here, but if you need more fidelity,
refer to
[
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec
](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec)
or the "Device List Format" Appendix of
{manpage}`NetworkManager.conf(5)`.
'';
};
plugins = mkOption {
type =
let
networkManagerPluginPackage = types.package // {
description = "NetworkManager plug-in";
check =
p:
lib.assertMsg
(types.package.check p
&& p ? networkManagerPlugin
&& lib.isString p.networkManagerPlugin)
''
Package ${p.name}, is not a NetworkManager plug-in.
Those need to have a networkManagerPlugin attribute.
'';
};
in
types.listOf networkManagerPluginPackage;
default = [ ];
description = lib.mdDoc ''
List of NetworkManager plug-ins to enable.
Some plug-ins are enabled by the NetworkManager module by default.
'';
};
dhcp = mkOption {
type = types.enum [ "dhcpcd" "internal" ];
default = "internal";
description = lib.mdDoc ''
Which program (or internal library) should be used for DHCP.
'';
};
firewallBackend = mkOption {
type = types.enum [ "iptables" "nftables" "none" ];
default = "iptables";
description = lib.mdDoc ''
Which firewall backend should be used for configuring masquerading with shared mode.
If set to none, NetworkManager doesn't manage the configuration at all.
'';
};
logLevel = mkOption {
type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
default = "WARN";
description = lib.mdDoc ''
Set the default logging verbosity level.
'';
};
appendNameservers = mkOption {
2015-06-15 16:18:46 +00:00
type = types.listOf types.str;
2023-08-30 10:36:41 +00:00
default = [ ];
description = lib.mdDoc ''
A list of name servers that should be appended
to the ones configured in NetworkManager or received by DHCP.
'';
};
insertNameservers = mkOption {
2015-06-15 16:18:46 +00:00
type = types.listOf types.str;
2023-08-30 10:36:41 +00:00
default = [ ];
description = lib.mdDoc ''
A list of name servers that should be inserted before
the ones configured in NetworkManager or received by DHCP.
'';
};
ethernet.macAddress = macAddressOpt;
wifi = {
macAddress = macAddressOpt;
backend = mkOption {
type = types.enum [ "wpa_supplicant" "iwd" ];
default = "wpa_supplicant";
description = lib.mdDoc ''
Specify the Wi-Fi backend used for the device.
Currently supported are {option}`wpa_supplicant` or {option}`iwd` (experimental).
'';
};
powersave = mkOption {
type = types.nullOr types.bool;
default = null;
description = lib.mdDoc ''
Whether to enable Wi-Fi power saving.
'';
};
scanRandMacAddress = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Whether to enable MAC address randomization of a Wi-Fi device
during scanning.
'';
};
};
2018-05-03 12:05:43 +00:00
dns = mkOption {
type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ];
default = "default";
description = lib.mdDoc ''
Set the DNS (`resolv.conf`) processing mode.
A description of these modes can be found in the main section of
[
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
](https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html)
or in
{manpage}`NetworkManager.conf(5)`.
'';
};
dispatcherScripts = mkOption {
type = types.listOf (types.submodule {
options = {
source = mkOption {
type = types.path;
description = lib.mdDoc ''
Path to the hook script.
'';
};
type = mkOption {
type = types.enum (attrNames dispatcherTypesSubdirMap);
default = "basic";
description = lib.mdDoc ''
Dispatcher hook type. Look up the hooks described at
[https://developer.gnome.org/NetworkManager/stable/NetworkManager.html](https://developer.gnome.org/NetworkManager/stable/NetworkManager.html)
and choose the type depending on the output folder.
You should then filter the event type (e.g., "up"/"down") from within your script.
'';
};
};
});
2023-08-30 10:36:41 +00:00
default = [ ];
example = literalExpression ''
2023-08-30 10:36:41 +00:00
[ {
source = pkgs.writeText "upHook" '''
if [ "$2" != "up" ]; then
logger "exit: event $2 != up"
exit
fi
# coreutils and iproute are in PATH too
logger "Device $DEVICE_IFACE coming up"
''';
type = "basic";
} ]'';
description = lib.mdDoc ''
A list of scripts which will be executed in response to network events.
'';
};
enableStrongSwan = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable the StrongSwan plugin.
If you enable this option the
`networkmanager_strongswan` plugin will be added to
the {option}`networking.networkmanager.plugins` option
so you don't need to do that yourself.
'';
};
enableBundledFccUnlockScripts = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable FCC unlock procedures shipped with ModemManager.
Since release 1.18.4, the ModemManager daemon no longer
automatically performs the FCC unlock procedure by default. See
[the docs](https://modemmanager.org/docs/modemmanager/fcc-unlock/)
for more details.
'';
};
fccUnlockScripts = mkOption {
type = types.listOf (types.submodule {
options = {
id = mkOption {
type = types.str;
description = lib.mdDoc "vid:pid of either the PCI or USB vendor and product ID";
};
path = mkOption {
type = types.path;
description = lib.mdDoc "Path to the unlock script";
};
};
});
default = [ ];
example = literalExpression ''[{ name = "03f0:4e1d"; script = "''${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/03f0:4e1d"; }]'';
description = lib.mdDoc ''
List of FCC unlock scripts to enable on the system, behaving as described in
https://modemmanager.org/docs/modemmanager/fcc-unlock/#integration-with-third-party-fcc-unlock-tools.
'';
};
};
};
imports = [
(mkRenamedOptionModule
[ "networking" "networkmanager" "packages" ]
[ "networking" "networkmanager" "plugins" ])
(mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ])
(mkRenamedOptionModule [ "networking" "networkmanager" "enableFccUnlock" ] [ "networking" "networkmanager" "enableBundledFccUnlockScripts" ])
2023-08-30 10:36:41 +00:00
(mkRemovedOptionModule [ "networking" "networkmanager" "dynamicHosts" ] ''
This option was removed because allowing (multiple) regular users to
override host entries affecting the whole system opens up a huge attack
vector. There seem to be very rare cases where this might be useful.
Consider setting system-wide host entries using networking.hosts, provide
them via the DNS server in your network, or use environment.etc
to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir.
'')
];
###### implementation
config = mkIf cfg.enable {
assertions = [
2023-08-30 10:36:41 +00:00
{
assertion = config.networking.wireless.enable == true -> cfg.unmanaged != [ ];
message = ''
You can not use networking.networkmanager with networking.wireless.
Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager.
'';
}
];
2013-03-31 19:18:51 +00:00
hardware.wirelessRegulatoryDatabase = true;
environment.etc = {
2023-08-30 10:36:41 +00:00
"NetworkManager/NetworkManager.conf".source = configFile;
}
// builtins.listToAttrs (map
(pkg: nameValuePair "NetworkManager/${pkg.networkManagerPlugin}" {
source = "${pkg}/lib/NetworkManager/${pkg.networkManagerPlugin}";
2023-08-30 10:36:41 +00:00
})
cfg.plugins)
// builtins.listToAttrs (map
(e: nameValuePair "ModemManager/fcc-unlock.d/${e.id}" {
source = e.path;
})
cfg.fccUnlockScripts)
2023-08-30 10:36:41 +00:00
// optionalAttrs (cfg.appendNameservers != [ ] || cfg.insertNameservers != [ ])
{
"NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript;
}
// listToAttrs (lib.imap1
(i: s:
{
name = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}";
value = { mode = "0544"; inherit (s) source; };
})
cfg.dispatcherScripts);
environment.systemPackages = packages;
users.groups = {
networkmanager.gid = config.ids.gids.networkmanager;
nm-openvpn.gid = config.ids.gids.nm-openvpn;
};
users.users = {
nm-openvpn = {
uid = config.ids.uids.nm-openvpn;
group = "nm-openvpn";
extraGroups = [ "networkmanager" ];
};
nm-iodine = {
isSystemUser = true;
group = "networkmanager";
};
};
systemd.packages = packages;
systemd.tmpfiles.rules = [
"d /etc/NetworkManager/system-connections 0700 root root -"
"d /etc/ipsec.d 0700 root root -"
"d /var/lib/NetworkManager-fortisslvpn 0700 root root -"
"d /var/lib/misc 0755 root root -" # for dnsmasq.leases
nixos/networkmanager: create pppd lock directory I digged up some 3G stick, which uses ppp to set up the connection. It failed to spin up ppp, because ppp failed to find the directory it wants to create its lockfiles in: ``` Jul 22 16:47:49 tp ModemManager[926779]: <info> [modem1] state changed (connected -> disconnecting) Jul 22 16:47:49 tp ModemManager[926779]: <info> [modem1] simple connect started... Jul 22 16:47:49 tp ModemManager[926779]: <info> [modem1] simple connect state (4/10): wait to get fully enabled Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] state changed (disconnecting -> registered) Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] simple connect state (5/10): wait after enabled Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1/bearer0] connection #11 finished: duration 1s Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] simple connect state (6/10): register Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] simple connect state (7/10): wait to get packet service state attached Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] simple connect state (8/10): bearer Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] simple connect state (9/10): connect Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] state changed (registered -> connecting) Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] state changed (connecting -> connected) Jul 22 16:47:50 tp ModemManager[926779]: <info> [modem1] simple connect state (10/10): all done Jul 22 16:47:50 tp pppd[1576260]: Plugin /nix/store/yqdqzz6y6agcmrfj8b6pwqhjcjyb3ypr-networkmanager-1.42.6/lib/pppd/2.5.0/nm-pppd-plugin.so loaded. Jul 22 16:47:50 tp NetworkManager[1576260]: Plugin /nix/store/yqdqzz6y6agcmrfj8b6pwqhjcjyb3ypr-networkmanager-1.42.6/lib/pppd/2.5.0/nm-pppd-plugin.so loaded. Jul 22 16:47:50 tp pppd[1576260]: nm-ppp-plugin: initializing Jul 22 16:47:50 tp pppd[1576260]: pppd 2.5.0 started by root, uid 0 Jul 22 16:47:50 tp pppd[1576260]: Can't create lock file /var/run/pppd/lock/LCK..ttyUSB0: No such file or directory Jul 22 16:47:50 tp NetworkManager[1576260]: Can't create lock file /var/run/pppd/lock/LCK..ttyUSB0: No such file or directory Jul 22 16:47:50 tp pppd[1576260]: nm-ppp-plugin: status 2 / phase 'serial connection' Jul 22 16:47:50 tp pppd[1576260]: Exit. Jul 22 16:47:50 tp pppd[1576260]: nm-ppp-plugin: status 0 / phase 'dead' Jul 22 16:47:50 tp pppd[1576260]: nm-ppp-plugin: cleaning up ``` Creating the directories via tmpfiles.d got the connection to succeed, and might also fix other connections using PPP.
2023-07-22 13:50:37 +00:00
# ppp isn't able to mkdir that directory at runtime
"d /run/pppd/lock 0700 root root -"
];
2019-08-13 21:52:01 +00:00
systemd.services.NetworkManager = {
wantedBy = [ "network.target" ];
restartTriggers = [ configFile ];
aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
serviceConfig = {
StateDirectory = "NetworkManager";
StateDirectoryMode = 755; # not sure if this really needs to be 755
};
};
systemd.services.NetworkManager-wait-online = {
wantedBy = [ "network-online.target" ];
};
systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
2019-08-13 21:52:01 +00:00
systemd.services.NetworkManager-dispatcher = {
wantedBy = [ "network.target" ];
restartTriggers = [ configFile overrideNameserversScript ];
# useful binaries for user-specified hooks
2021-03-14 16:05:16 +00:00
path = [ pkgs.iproute2 pkgs.util-linux pkgs.coreutils ];
aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ];
};
# Turn off NixOS' network management when networking is managed entirely by NetworkManager
networking = mkMerge [
(mkIf (!delegateWireless) {
useDHCP = false;
})
{
networkmanager.plugins = with pkgs; [
networkmanager-fortisslvpn
networkmanager-iodine
networkmanager-l2tp
networkmanager-openconnect
networkmanager-openvpn
networkmanager-vpnc
networkmanager-sstp
];
}
# if cfg.enableBundledFccUnlockScripts is set, populate
# networking.networkmanager.fccUnlockScripts with the values from
# pkgs.modemmanager.passthru.fccUnlockScripts.
(mkIf cfg.enableBundledFccUnlockScripts {
networkmanager.fccUnlockScripts = lib.optionals cfg.enableBundledFccUnlockScripts
lib.mapAttrsToList
(id: path: { inherit id path; })
pkgs.modemmanager.passthru.fccUnlockScripts;
})
(mkIf cfg.enableStrongSwan {
networkmanager.plugins = [ pkgs.networkmanager_strongswan ];
})
(mkIf enableIwd {
wireless.iwd.enable = true;
})
{
networkmanager.connectionConfig = {
"ethernet.cloned-mac-address" = cfg.ethernet.macAddress;
"wifi.cloned-mac-address" = cfg.wifi.macAddress;
"wifi.powersave" =
if cfg.wifi.powersave == null then null
else if cfg.wifi.powersave then 3
else 2;
};
}
];
boot.kernelModules = [ "ctr" ];
security.polkit.enable = true;
security.polkit.extraConfig = polkitConf;
services.dbus.packages = packages
++ optional cfg.enableStrongSwan pkgs.strongswanNM
++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq;
services.udev.packages = packages;
};
}