mirror of
https://github.com/SebastianWendel/nixpkgs.git
synced 2024-11-06 02:06:46 +01:00
e05c1e3431
as of 1.18.0, modemmanager defaults to strict when no other options are passed, so we can just use the upstream unit as is
551 lines
18 KiB
Nix
551 lines
18 KiB
Nix
{ config, lib, pkgs, ... }:
|
||
|
||
with lib;
|
||
|
||
let
|
||
cfg = config.networking.networkmanager;
|
||
|
||
basePackages = with pkgs; [
|
||
modemmanager
|
||
networkmanager
|
||
networkmanager-fortisslvpn
|
||
networkmanager-iodine
|
||
networkmanager-l2tp
|
||
networkmanager-openconnect
|
||
networkmanager-openvpn
|
||
networkmanager-vpnc
|
||
networkmanager-sstp
|
||
] ++ optional (!delegateWireless && !enableIwd) wpa_supplicant;
|
||
|
||
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 =
|
||
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
|
||
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
|
||
|| 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 = {
|
||
basic = "";
|
||
pre-up = "pre-up.d/";
|
||
pre-down = "pre-down.d/";
|
||
};
|
||
|
||
macAddressOpt = mkOption {
|
||
type = types.either types.str (types.enum ["permanent" "preserve" "random" "stable"]);
|
||
default = "preserve";
|
||
example = "00:11:22:33:44:55";
|
||
description = ''
|
||
Set the MAC address of the interface.
|
||
<variablelist>
|
||
<varlistentry>
|
||
<term>"XX:XX:XX:XX:XX:XX"</term>
|
||
<listitem><para>MAC address of the interface</para></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><literal>"permanent"</literal></term>
|
||
<listitem><para>Use the permanent MAC address of the device</para></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><literal>"preserve"</literal></term>
|
||
<listitem><para>Don’t change the MAC address of the device upon activation</para></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><literal>"random"</literal></term>
|
||
<listitem><para>Generate a randomized value upon each connect</para></listitem>
|
||
</varlistentry>
|
||
<varlistentry>
|
||
<term><literal>"stable"</literal></term>
|
||
<listitem><para>Generate a stable, hashed MAC address</para></listitem>
|
||
</varlistentry>
|
||
</variablelist>
|
||
'';
|
||
};
|
||
|
||
in {
|
||
|
||
meta = {
|
||
maintainers = teams.freedesktop.members;
|
||
};
|
||
|
||
###### interface
|
||
|
||
options = {
|
||
|
||
networking.networkmanager = {
|
||
|
||
enable = mkOption {
|
||
type = types.bool;
|
||
default = false;
|
||
description = ''
|
||
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 <literal>networkmanager</literal>
|
||
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
|
||
]));
|
||
default = {};
|
||
description = ''
|
||
Configuration for the [connection] section of NetworkManager.conf.
|
||
Refer to
|
||
<link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
|
||
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#id-1.2.3.11
|
||
</link>
|
||
or
|
||
<citerefentry>
|
||
<refentrytitle>NetworkManager.conf</refentrytitle>
|
||
<manvolnum>5</manvolnum>
|
||
</citerefentry>
|
||
for more information.
|
||
'';
|
||
};
|
||
|
||
extraConfig = mkOption {
|
||
type = types.lines;
|
||
default = "";
|
||
description = ''
|
||
Configuration appended to the generated NetworkManager.conf.
|
||
Refer to
|
||
<link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
|
||
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
|
||
</link>
|
||
or
|
||
<citerefentry>
|
||
<refentrytitle>NetworkManager.conf</refentrytitle>
|
||
<manvolnum>5</manvolnum>
|
||
</citerefentry>
|
||
for more information.
|
||
'';
|
||
};
|
||
|
||
unmanaged = mkOption {
|
||
type = types.listOf types.str;
|
||
default = [];
|
||
description = ''
|
||
List of interfaces that will not be managed by NetworkManager.
|
||
Interface name can be specified here, but if you need more fidelity,
|
||
refer to
|
||
<link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec">
|
||
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec
|
||
</link>
|
||
or the "Device List Format" Appendix of
|
||
<citerefentry>
|
||
<refentrytitle>NetworkManager.conf</refentrytitle>
|
||
<manvolnum>5</manvolnum>
|
||
</citerefentry>.
|
||
'';
|
||
};
|
||
|
||
packages = mkOption {
|
||
type = types.listOf types.package;
|
||
default = [ ];
|
||
description = ''
|
||
Extra packages that provide NetworkManager plugins.
|
||
'';
|
||
apply = list: basePackages ++ list;
|
||
};
|
||
|
||
dhcp = mkOption {
|
||
type = types.enum [ "dhclient" "dhcpcd" "internal" ];
|
||
default = "internal";
|
||
description = ''
|
||
Which program (or internal library) should be used for DHCP.
|
||
'';
|
||
};
|
||
|
||
firewallBackend = mkOption {
|
||
type = types.enum [ "iptables" "nftables" "none" ];
|
||
default = "iptables";
|
||
description = ''
|
||
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 = ''
|
||
Set the default logging verbosity level.
|
||
'';
|
||
};
|
||
|
||
appendNameservers = mkOption {
|
||
type = types.listOf types.str;
|
||
default = [];
|
||
description = ''
|
||
A list of name servers that should be appended
|
||
to the ones configured in NetworkManager or received by DHCP.
|
||
'';
|
||
};
|
||
|
||
insertNameservers = mkOption {
|
||
type = types.listOf types.str;
|
||
default = [];
|
||
description = ''
|
||
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 = ''
|
||
Specify the Wi-Fi backend used for the device.
|
||
Currently supported are <option>wpa_supplicant</option> or <option>iwd</option> (experimental).
|
||
'';
|
||
};
|
||
|
||
powersave = mkOption {
|
||
type = types.nullOr types.bool;
|
||
default = null;
|
||
description = ''
|
||
Whether to enable Wi-Fi power saving.
|
||
'';
|
||
};
|
||
|
||
scanRandMacAddress = mkOption {
|
||
type = types.bool;
|
||
default = true;
|
||
description = ''
|
||
Whether to enable MAC address randomization of a Wi-Fi device
|
||
during scanning.
|
||
'';
|
||
};
|
||
};
|
||
|
||
dns = mkOption {
|
||
type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ];
|
||
default = "default";
|
||
description = ''
|
||
Set the DNS (<literal>resolv.conf</literal>) processing mode.
|
||
</para>
|
||
<para>
|
||
A description of these modes can be found in the main section of
|
||
<link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html">
|
||
https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html
|
||
</link>
|
||
or in
|
||
<citerefentry>
|
||
<refentrytitle>NetworkManager.conf</refentrytitle>
|
||
<manvolnum>5</manvolnum>
|
||
</citerefentry>.
|
||
'';
|
||
};
|
||
|
||
dispatcherScripts = mkOption {
|
||
type = types.listOf (types.submodule {
|
||
options = {
|
||
source = mkOption {
|
||
type = types.path;
|
||
description = ''
|
||
Path to the hook script.
|
||
'';
|
||
};
|
||
|
||
type = mkOption {
|
||
type = types.enum (attrNames dispatcherTypesSubdirMap);
|
||
default = "basic";
|
||
description = ''
|
||
Dispatcher hook type. Look up the hooks described at
|
||
<link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.html">https://developer.gnome.org/NetworkManager/stable/NetworkManager.html</link>
|
||
and choose the type depending on the output folder.
|
||
You should then filter the event type (e.g., "up"/"down") from within your script.
|
||
'';
|
||
};
|
||
};
|
||
});
|
||
default = [];
|
||
example = literalExample ''
|
||
[ {
|
||
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 = ''
|
||
A list of scripts which will be executed in response to network events.
|
||
'';
|
||
};
|
||
|
||
enableStrongSwan = mkOption {
|
||
type = types.bool;
|
||
default = false;
|
||
description = ''
|
||
Enable the StrongSwan plugin.
|
||
</para><para>
|
||
If you enable this option the
|
||
<literal>networkmanager_strongswan</literal> plugin will be added to
|
||
the <option>networking.networkmanager.packages</option> option
|
||
so you don't need to to that yourself.
|
||
'';
|
||
};
|
||
};
|
||
};
|
||
|
||
imports = [
|
||
(mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ])
|
||
(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 = [
|
||
{ 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.
|
||
'';
|
||
}
|
||
];
|
||
|
||
hardware.wirelessRegulatoryDatabase = true;
|
||
|
||
environment.etc = with pkgs; {
|
||
"NetworkManager/NetworkManager.conf".source = configFile;
|
||
|
||
"NetworkManager/VPN/nm-openvpn-service.name".source =
|
||
"${networkmanager-openvpn}/lib/NetworkManager/VPN/nm-openvpn-service.name";
|
||
|
||
"NetworkManager/VPN/nm-vpnc-service.name".source =
|
||
"${networkmanager-vpnc}/lib/NetworkManager/VPN/nm-vpnc-service.name";
|
||
|
||
"NetworkManager/VPN/nm-openconnect-service.name".source =
|
||
"${networkmanager-openconnect}/lib/NetworkManager/VPN/nm-openconnect-service.name";
|
||
|
||
"NetworkManager/VPN/nm-fortisslvpn-service.name".source =
|
||
"${networkmanager-fortisslvpn}/lib/NetworkManager/VPN/nm-fortisslvpn-service.name";
|
||
|
||
"NetworkManager/VPN/nm-l2tp-service.name".source =
|
||
"${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name";
|
||
|
||
"NetworkManager/VPN/nm-iodine-service.name".source =
|
||
"${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name";
|
||
|
||
"NetworkManager/VPN/nm-sstp-service.name".source =
|
||
"${networkmanager-sstp}/lib/NetworkManager/VPN/nm-sstp-service.name";
|
||
}
|
||
// optionalAttrs (cfg.appendNameservers != [] || cfg.insertNameservers != [])
|
||
{
|
||
"NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript;
|
||
}
|
||
// optionalAttrs cfg.enableStrongSwan
|
||
{
|
||
"NetworkManager/VPN/nm-strongswan-service.name".source =
|
||
"${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name";
|
||
}
|
||
// 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 = cfg.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;
|
||
extraGroups = [ "networkmanager" ];
|
||
};
|
||
nm-iodine = {
|
||
isSystemUser = true;
|
||
group = "networkmanager";
|
||
};
|
||
};
|
||
|
||
systemd.packages = cfg.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/dhclient 0755 root root -"
|
||
"d /var/lib/misc 0755 root root -" # for dnsmasq.leases
|
||
];
|
||
|
||
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" ];
|
||
|
||
systemd.services.NetworkManager-dispatcher = {
|
||
wantedBy = [ "network.target" ];
|
||
restartTriggers = [ configFile overrideNameserversScript ];
|
||
|
||
# useful binaries for user-specified hooks
|
||
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;
|
||
})
|
||
|
||
(mkIf cfg.enableStrongSwan {
|
||
networkmanager.packages = [ pkgs.networkmanager_strongswan ];
|
||
})
|
||
|
||
(mkIf enableIwd {
|
||
wireless.iwd.enable = true;
|
||
})
|
||
|
||
{
|
||
networkmanager.connectionConfig = {
|
||
"ipv6.ip6-privacy" = 2;
|
||
"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.extraConfig = polkitConf;
|
||
|
||
services.dbus.packages = cfg.packages
|
||
++ optional cfg.enableStrongSwan pkgs.strongswanNM
|
||
++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq;
|
||
|
||
services.udev.packages = cfg.packages;
|
||
};
|
||
}
|