225 lines
9.5 KiB
Nix
225 lines
9.5 KiB
Nix
{ config, pkgs, ... }:
|
|
let
|
|
# networkmanager = pkgs.networkmanager;
|
|
networkmanager = pkgs.networkmanager.overrideAttrs (upstream: {
|
|
src = pkgs.fetchFromGitea {
|
|
domain = "git.uninsane.org";
|
|
owner = "colin";
|
|
repo = "NetworkManager";
|
|
# patched to fix polkit permissions (with `nmcli`) when NetworkManager runs as user networkmanager
|
|
rev = "dev-sane-1.48.0";
|
|
hash = "sha256-vGmOKtwVItxjYioZJlb1og3K6u9s4rcmDnjAPLBC3ao=";
|
|
};
|
|
# patches = [];
|
|
});
|
|
# split the package into `daemon` and `nmcli` outputs, because the networkmanager *service*
|
|
# doesn't need `nmcli`/`nmtui` tooling
|
|
networkmanager-split = pkgs.networkmanager-split.override { inherit networkmanager; };
|
|
in {
|
|
networking.networkmanager.enable = true;
|
|
# plugins mostly add support for establishing different VPN connections.
|
|
# the default plugin set includes mostly proprietary VPNs:
|
|
# - fortisslvpn (Fortinet)
|
|
# - iodine (DNS tunnels)
|
|
# - l2tp
|
|
# - openconnect (Cisco Anyconnect / Juniper / ocserv)
|
|
# - openvpn
|
|
# - vpnc (Cisco VPN)
|
|
# - sstp
|
|
#
|
|
# i don't use these, and notably they drag in huge dependency sets and don't cross compile well.
|
|
# e.g. openconnect drags in webkitgtk (for SSO)!
|
|
# networking.networkmanager.plugins = lib.mkForce [];
|
|
networking.networkmanager.enableDefaultPlugins = false;
|
|
|
|
networking.networkmanager.package = networkmanager-split.daemon.overrideAttrs (upstream: {
|
|
# postPatch = (upstream.postPatch or "") + ''
|
|
# substituteInPlace src/{core/org.freedesktop.NetworkManager,nm-dispatcher/nm-dispatcher}.conf --replace-fail \
|
|
# 'user="root"' 'user="networkmanager"'
|
|
# '';
|
|
postInstall = (upstream.postInstall or "") + ''
|
|
# allow the bus to owned by either root or networkmanager users
|
|
# use the group here, that way ordinary users can be elevated to control networkmanager
|
|
# (via e.g. `nmcli`)
|
|
for f in org.freedesktop.NetworkManager.conf nm-dispatcher.conf ; do
|
|
substitute $out/share/dbus-1/system.d/$f \
|
|
$out/share/dbus-1/system.d/networkmanager-$f \
|
|
--replace-fail 'user="root"' 'group="networkmanager"'
|
|
done
|
|
|
|
# remove unused services to prevent any unexpected interactions
|
|
rm $out/etc/systemd/system/{nm-cloud-setup.service,nm-cloud-setup.timer,nm-priv-helper.service}
|
|
'';
|
|
});
|
|
|
|
# fixup the services to run as `networkmanager` and with less permissions
|
|
systemd.services.NetworkManager = {
|
|
serviceConfig.RuntimeDirectory = "NetworkManager"; #< tells systemd to create /run/NetworkManager
|
|
# serviceConfig.StateDirectory = "NetworkManager"; #< tells systemd to create /var/lib/NetworkManager
|
|
serviceConfig.User = "networkmanager";
|
|
serviceConfig.Group = "networkmanager";
|
|
serviceConfig.AmbientCapabilities = [
|
|
# "CAP_DAC_OVERRIDE"
|
|
"CAP_NET_ADMIN"
|
|
"CAP_NET_RAW" #< required, else `libndp: ndp_sock_open: Failed to create ICMP6 socket.`
|
|
"CAP_NET_BIND_SERVICE" #< this *does* seem to be necessary, though i don't understand why. DHCP?
|
|
# "CAP_SYS_MODULE"
|
|
# "CAP_AUDIT_WRITE" #< allow writing to the audit log (optional)
|
|
# "CAP_KILL"
|
|
];
|
|
serviceConfig.LockPersonality = true;
|
|
serviceConfig.PrivateDevices = true; # remount /dev with just the basics, syscall filter to block @raw-io
|
|
serviceConfig.PrivateIPC = true;
|
|
# serviceConfig.PrivateUsers = true; #< BREAKS NetworkManager (presumably, it causes a new user namespace, breaking CAP_NET_ADMIN & others). "platform-linux: do-change-link[3]: failure 1 (Operation not permitted)"
|
|
serviceConfig.ProtectClock = true; # syscall filter to prevent changing the RTC
|
|
serviceConfig.ProtectControlGroups = true;
|
|
serviceConfig.ProtectHome = true; # makes empty: /home, /root, /run/user
|
|
serviceConfig.ProtectHostname = true; # probably not upstreamable: prevents changing hostname
|
|
serviceConfig.ProtectKernelLogs = true; # disable /proc/kmsg, /dev/kmsg
|
|
serviceConfig.ProtectKernelModules = true; # syscall filter to prevent module calls (probably not upstreamable: NM will want to load modules like `ppp`)
|
|
serviceConfig.ProtectKernelTunables = true; # but NM might need to write /proc/sys/net/...
|
|
serviceConfig.ProtectSystem = "full"; # makes read-only: /boot, /etc/, /usr. TODO: "strict" would make all but /dev, /proc, /sys inaccessible.
|
|
serviceConfig.RestrictAddressFamilies = [
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
"AF_NETLINK" # breaks near DHCP without this
|
|
"AF_PACKET" # for DHCP
|
|
"AF_UNIX"
|
|
# AF_ALG ?
|
|
# AF_BLUETOOTH ?
|
|
# AF_BRIDGE ?
|
|
# AF_NETLINK ?
|
|
# AF_PACKET ?
|
|
];
|
|
serviceConfig.RestrictSUIDSGID = true;
|
|
serviceConfig.SystemCallArchitectures = "native"; # prevents e.g. aarch64 syscalls in the event that the kernel is multi-architecture.
|
|
# TODO: it needs these directories:
|
|
# - "/proc/net"
|
|
# - "/proc/sys/net"
|
|
# - "/run/NetworkManager"
|
|
# - "/run/systemd" # for trust-dns-nmhook
|
|
# - "/run/udev"
|
|
# - # "/run/wg-home.priv"
|
|
# - "/sys/class" #< TODO: specify this more precisely
|
|
# - "/sys/devices"
|
|
# - "/var/lib/NetworkManager"
|
|
# - "/var/lib/trust-dns" #< for trust-dns-nmhook
|
|
# - "/run/systemd"
|
|
};
|
|
|
|
systemd.services.NetworkManager-wait-online = {
|
|
serviceConfig.User = "networkmanager";
|
|
serviceConfig.Group = "networkmanager";
|
|
};
|
|
|
|
# fix NetworkManager-dispatcher to actually run as a daemon,
|
|
# and sandbox it a bit
|
|
systemd.services.NetworkManager-dispatcher = {
|
|
after = [ "trust-dns-localhost.service" ]; #< so that /var/lib/trust-dns will exist
|
|
# serviceConfig.ExecStart = [
|
|
# "" # first blank line is to clear the upstream `ExecStart` field.
|
|
# "${cfg.package}/libexec/nm-dispatcher --persist" # --persist is needed for it to actually run as a daemon
|
|
# ];
|
|
# serviceConfig.Restart = "always";
|
|
# serviceConfig.RestartSec = "1s";
|
|
serviceConfig.User = "networkmanager";
|
|
serviceConfig.Group = "networkmanager";
|
|
# TODO: it needs access only to the above mentioned directories
|
|
};
|
|
|
|
# harden wpa_supplicant (used by NetworkManager)
|
|
systemd.services.wpa_supplicant = {
|
|
serviceConfig.User = "networkmanager";
|
|
serviceConfig.Group = "networkmanager";
|
|
serviceConfig.AmbientCapabilities = [
|
|
"CAP_NET_ADMIN"
|
|
"CAP_NET_RAW"
|
|
];
|
|
# TODO: it needs only these paths:
|
|
# - "/dev/net"
|
|
# - "/dev/rfkill"
|
|
# - "/proc/sys/net"
|
|
# - "/sys/class/net"
|
|
# - "/sys/devices"
|
|
# - "/run/systemd"
|
|
};
|
|
|
|
networking.networkmanager.settings = {
|
|
# keyfile.path = where networkmanager should look for connection credentials
|
|
keyfile.path = "/var/lib/NetworkManager/system-connections";
|
|
|
|
# wifi.backend = "wpa_supplicant"; #< default
|
|
# wifi.scan-rand-mac-address = true; #< default
|
|
|
|
# logging.audit = false; #< default
|
|
logging.level = "INFO";
|
|
|
|
# main.dhcp = "internal"; #< default
|
|
main.dns = if config.services.resolved.enable then
|
|
"systemd-resolved"
|
|
else if config.sane.services.trust-dns.enable && config.sane.services.trust-dns.asSystemResolver then
|
|
"none"
|
|
else
|
|
"internal"
|
|
;
|
|
main.systemd-resolved = false;
|
|
};
|
|
environment.etc."NetworkManager/system-connections".source = "/var/lib/NetworkManager/system-connections";
|
|
|
|
# the default backend is "wpa_supplicant".
|
|
# wpa_supplicant reliably picks weak APs to connect to.
|
|
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
|
# iwd is an alternative that shouldn't have this problem
|
|
# docs:
|
|
# - <https://nixos.wiki/wiki/Iwd>
|
|
# - <https://iwd.wiki.kernel.org/networkmanager>
|
|
# - `man iwd.config` for global config
|
|
# - `man iwd.network` for per-SSID config
|
|
# use `iwctl` to control
|
|
# networking.networkmanager.wifi.backend = "iwd";
|
|
# networking.wireless.iwd.enable = true;
|
|
# networking.wireless.iwd.settings = {
|
|
# # auto-connect to a stronger network if signal drops below this value
|
|
# # bedroom -> bedroom connection is -35 to -40 dBm
|
|
# # bedroom -> living room connection is -60 dBm
|
|
# General.RoamThreshold = "-52"; # default -70
|
|
# General.RoamThreshold5G = "-52"; # default -76
|
|
# };
|
|
|
|
# allow networkmanager to control systemd-resolved,
|
|
# which it needs to do to apply new DNS settings when using systemd-resolved.
|
|
security.polkit.extraConfig = ''
|
|
polkit.addRule(function(action, subject) {
|
|
if (subject.isInGroup("networkmanager") && action.id.indexOf("org.freedesktop.resolve1.") == 0) {
|
|
return polkit.Result.YES;
|
|
}
|
|
});
|
|
'';
|
|
|
|
users.users.networkmanager = {
|
|
isSystemUser = true;
|
|
group = "networkmanager";
|
|
extraGroups = [ "trust-dns" ];
|
|
};
|
|
|
|
# there is, unfortunately, no proper interface by which to plumb wpa_supplicant into the NixOS service, except by overlay.
|
|
nixpkgs.overlays = [(self: super: {
|
|
wpa_supplicant = super.wpa_supplicant.overrideAttrs (upstream: {
|
|
# postPatch = (upstream.postPatch or "") + ''
|
|
# substituteInPlace wpa_supplicant/dbus/dbus-wpa_supplicant.conf --replace-fail \
|
|
# 'user="root"' 'user="networkmanager"'
|
|
# '';
|
|
postInstall = (upstream.postInstall or "") + ''
|
|
substitute $out/share/dbus-1/system.d/dbus-wpa_supplicant.conf \
|
|
$out/share/dbus-1/system.d/networkmanager-wpa_supplicant.conf \
|
|
--replace-fail 'user="root"' 'group="networkmanager"'
|
|
'';
|
|
|
|
postFixup = (upstream.postFixup or "") + ''
|
|
# remove unused services to avoid unexpected interactions
|
|
rm $out/etc/systemd/system/{wpa_supplicant-nl80211@,wpa_supplicant-wired@,wpa_supplicant@}.service
|
|
'';
|
|
});
|
|
})];
|
|
}
|