NetworkManager: run as user instead of root
This commit is contained in:
parent
fb7bcbb5f5
commit
1ee21c4795
|
@ -4,6 +4,9 @@
|
||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
# partially supported in nixpkgs <repo:nixos/nixpkgs:nixos/modules/misc/ids.nix>
|
||||||
|
sane.ids.networkmanager.uid = 57; #< nixpkgs unofficially reserves this, to match networkmanager's gid
|
||||||
|
|
||||||
# legacy servo users, some are inconvenient to migrate
|
# legacy servo users, some are inconvenient to migrate
|
||||||
sane.ids.dhcpcd.gid = 991;
|
sane.ids.dhcpcd.gid = 991;
|
||||||
sane.ids.dhcpcd.uid = 992;
|
sane.ids.dhcpcd.uid = 992;
|
||||||
|
|
|
@ -508,9 +508,8 @@ in
|
||||||
ethtool.sandbox.capabilities = [ "net_admin" ];
|
ethtool.sandbox.capabilities = [ "net_admin" ];
|
||||||
|
|
||||||
# eza `ls` replacement
|
# eza `ls` replacement
|
||||||
# landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo
|
|
||||||
# eza.sandbox.method = "landlock";
|
# eza.sandbox.method = "landlock";
|
||||||
eza.sandbox.method = "bwrap";
|
eza.sandbox.method = "bwrap"; #< note that bwrap causes `/proc` files to be listed differently (e.g. `eza /proc/sys/net/ipv6/conf/`)
|
||||||
eza.sandbox.autodetectCliPaths = "existing";
|
eza.sandbox.autodetectCliPaths = "existing";
|
||||||
eza.sandbox.whitelistPwd = true;
|
eza.sandbox.whitelistPwd = true;
|
||||||
eza.sandbox.extraHomePaths = [
|
eza.sandbox.extraHomePaths = [
|
||||||
|
|
|
@ -12,15 +12,26 @@ in
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
{
|
{
|
||||||
sane.programs.networkmanager = {
|
sane.programs.networkmanager = {
|
||||||
|
packageUnwrapped = pkgs.networkmanager.overrideAttrs (upstream: {
|
||||||
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
|
substituteInPlace src/{core/org.freedesktop.NetworkManager,nm-dispatcher/nm-dispatcher}.conf --replace-fail \
|
||||||
|
'user="root"' 'user="networkmanager"'
|
||||||
|
'';
|
||||||
|
# remove unused services to prevent any unexpected interactions
|
||||||
|
postFixup = (upstream.postFixup or "") + ''
|
||||||
|
rm $out/etc/systemd/system/{nm-cloud-setup.service,nm-cloud-setup.timer,nm-priv-helper.service}
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
suggestedPrograms = [ "wpa_supplicant" ];
|
suggestedPrograms = [ "wpa_supplicant" ];
|
||||||
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
|
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
|
||||||
|
|
||||||
# TODO: this contains both the NetworkManager service and the NetworkManager-dispatcher service
|
# this contains both the NetworkManager service and the NetworkManager-dispatcher service
|
||||||
# the latter of which calls a lot of user code.
|
# the latter of which calls a lot of user code.
|
||||||
# as a result, this needs all the perms which my hook in modules/services/trust-dns/trust-dns-nmhook needs
|
# as a result, this needs all the perms which my hook in modules/services/trust-dns/trust-dns-nmhook needs.
|
||||||
sandbox.method = "landlock";
|
sandbox.method = "landlock";
|
||||||
sandbox.capabilities = [
|
sandbox.capabilities = [
|
||||||
"dac_override" #< TODO: remove this! it's needed so that trust-dns-nmhook can write to trust-dns's state; instead i should add networkmanager to the trust-dns group.
|
# "dac_override"
|
||||||
"net_admin"
|
"net_admin"
|
||||||
"net_raw"
|
"net_raw"
|
||||||
"net_bind_service" #< TODO: is this needed? why? (DNS?)
|
"net_bind_service" #< TODO: is this needed? why? (DNS?)
|
||||||
|
@ -41,7 +52,7 @@ in
|
||||||
"/var/lib/trust-dns" #< for trust-dns-nmhook
|
"/var/lib/trust-dns" #< for trust-dns-nmhook
|
||||||
];
|
];
|
||||||
|
|
||||||
sandbox.whitelistDbus = [ "system" ]; #< apparently not actually needed?
|
sandbox.whitelistDbus = [ "system" ];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,34 +64,41 @@ in
|
||||||
aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
|
aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
|
||||||
|
|
||||||
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
||||||
serviceConfig = {
|
serviceConfig.RuntimeDirectory = "NetworkManager"; #< tells systemd to create /run/NetworkManager
|
||||||
ExecStartPre = [
|
serviceConfig.StateDirectory = "NetworkManager"; #< tells systemd to create /var/lib/NetworkManager
|
||||||
"${pkgs.coreutils}/bin/mkdir -p /run/NetworkManager"
|
serviceConfig.User = "networkmanager";
|
||||||
];
|
serviceConfig.Group = "networkmanager";
|
||||||
StateDirectory = "NetworkManager";
|
serviceConfig.AmbientCapabilities = [
|
||||||
StateDirectoryMode = 755; # not sure if this really needs to be 755
|
# "CAP_DAC_OVERRIDE"
|
||||||
};
|
"CAP_NET_ADMIN"
|
||||||
|
"CAP_NET_RAW"
|
||||||
|
"CAP_NET_BIND_SERVICE" #< TODO: is this needed? why? (DNS?)
|
||||||
|
# "CAP_SYS_MODULE"
|
||||||
|
"CAP_AUDIT_WRITE" #< allow writing to the audit log
|
||||||
|
# "CAP_KILL"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.NetworkManager-wait-online = {
|
systemd.services.NetworkManager-wait-online = {
|
||||||
path = [ "/run/current-system/sw" ]; #< so `nm-online` can find `sanebox`
|
path = [ "/run/current-system/sw" ]; #< so `nm-online` can find `sanebox`
|
||||||
wantedBy = [ "network-online.target" ];
|
wantedBy = [ "network-online.target" ];
|
||||||
|
serviceConfig.User = "networkmanager";
|
||||||
|
serviceConfig.Group = "networkmanager";
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.NetworkManager-dispatcher = {
|
systemd.services.NetworkManager-dispatcher = {
|
||||||
wantedBy = [ "NetworkManager.service" ];
|
wantedBy = [ "NetworkManager.service" ];
|
||||||
|
after = [ "trust-dns-localhost.service" ]; #< so that /var/lib/trust-dns will exist
|
||||||
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
||||||
# to debug, add NM_DISPATCHER_DEBUG_LOG=1
|
# to debug, add NM_DISPATCHER_DEBUG_LOG=1
|
||||||
serviceConfig.ExecStart = [
|
serviceConfig.ExecStart = [
|
||||||
"" # first blank line is to clear the upstream `ExecStart` field.
|
"" # 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
|
"${cfg.package}/libexec/nm-dispatcher --persist" # --persist is needed for it to actually run as a daemon
|
||||||
];
|
];
|
||||||
serviceConfig.ExecStartPre = [
|
|
||||||
# TODO: establish the trust-dns dependency more idiomatically than this
|
|
||||||
"${pkgs.coreutils}/bin/mkdir -p /var/lib/trust-dns && chown trust-dns:trust-dns /var/lib/trust-dns"
|
|
||||||
];
|
|
||||||
serviceConfig.Restart = "always";
|
serviceConfig.Restart = "always";
|
||||||
serviceConfig.RestartSec = "1s";
|
serviceConfig.RestartSec = "1s";
|
||||||
|
serviceConfig.User = "networkmanager";
|
||||||
|
serviceConfig.Group = "networkmanager";
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.etc = {
|
environment.etc = {
|
||||||
|
@ -125,24 +143,34 @@ in
|
||||||
# debug=... (see also: NM_DEBUG env var)
|
# debug=... (see also: NM_DEBUG env var)
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
hardware.wirelessRegulatoryDatabase = true;
|
hardware.wirelessRegulatoryDatabase = true;
|
||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
users.groups.networkmanager.gid = config.ids.gids.networkmanager;
|
|
||||||
services.udev.packages = [ cfg.package ];
|
services.udev.packages = [ cfg.package ];
|
||||||
security.polkit.enable = true;
|
security.polkit.enable = lib.mkDefault true;
|
||||||
|
# allow networkmanager unbounded control over modemmanager.
|
||||||
|
# i believe this was sourced from the default nixpkgs config.
|
||||||
security.polkit.extraConfig = ''
|
security.polkit.extraConfig = ''
|
||||||
polkit.addRule(function(action, subject) {
|
polkit.addRule(function(action, subject) {
|
||||||
if (
|
if (subject.isInGroup("networkmanager")
|
||||||
subject.isInGroup("networkmanager")
|
&& (
|
||||||
&& (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
|
action.id.indexOf("org.freedesktop.NetworkManager.") == 0
|
||||||
|| action.id.indexOf("org.freedesktop.ModemManager") == 0
|
|| action.id.indexOf("org.freedesktop.ModemManager") == 0
|
||||||
))
|
)
|
||||||
{ return polkit.Result.YES; }
|
) {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
users.groups.networkmanager.gid = config.ids.gids.networkmanager;
|
||||||
|
users.users.networkmanager = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "networkmanager";
|
||||||
|
extraGroups = [ "trust-dns" ];
|
||||||
|
};
|
||||||
|
|
||||||
boot.kernelModules = [ "ctr" ]; #< TODO: needed (what even is this)?
|
boot.kernelModules = [ "ctr" ]; #< TODO: needed (what even is this)?
|
||||||
# TODO: polkit?
|
|
||||||
# TODO: NetworkManager-ensure-profiles?
|
# TODO: NetworkManager-ensure-profiles?
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
|
@ -7,6 +7,10 @@ in
|
||||||
{
|
{
|
||||||
sane.programs.wpa_supplicant = {
|
sane.programs.wpa_supplicant = {
|
||||||
packageUnwrapped = pkgs.wpa_supplicant.overrideAttrs (upstream: {
|
packageUnwrapped = pkgs.wpa_supplicant.overrideAttrs (upstream: {
|
||||||
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
|
substituteInPlace wpa_supplicant/dbus/dbus-wpa_supplicant.conf --replace-fail \
|
||||||
|
'user="root"' 'user="networkmanager"'
|
||||||
|
'';
|
||||||
# nixpkgs wpa_supplicant generates a dbus file which has a path like
|
# nixpkgs wpa_supplicant generates a dbus file which has a path like
|
||||||
# /nix/store/abc-wpa_supplicant/nix/store/abc-wpa_supplicant/sbin/...
|
# /nix/store/abc-wpa_supplicant/nix/store/abc-wpa_supplicant/sbin/...
|
||||||
# upstreaming status: <https://github.com/NixOS/nixpkgs/pull/315346>
|
# upstreaming status: <https://github.com/NixOS/nixpkgs/pull/315346>
|
||||||
|
@ -15,6 +19,7 @@ in
|
||||||
"$out$out" "$out"
|
"$out$out" "$out"
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
|
# sandbox.enable = false; #< TODO: re-enable
|
||||||
sandbox.method = "landlock"; #< 'bwrap' (likely) can't work, because it needs to manipulate net interfaces in the root namespace
|
sandbox.method = "landlock"; #< 'bwrap' (likely) can't work, because it needs to manipulate net interfaces in the root namespace
|
||||||
sandbox.capabilities = [
|
sandbox.capabilities = [
|
||||||
# see also: <https://github.com/NixOS/nixpkgs/pull/305722>
|
# see also: <https://github.com/NixOS/nixpkgs/pull/305722>
|
||||||
|
@ -33,7 +38,15 @@ in
|
||||||
(lib.mkIf cfg.enabled {
|
(lib.mkIf cfg.enabled {
|
||||||
services.udev.packages = [ cfg.package ];
|
services.udev.packages = [ cfg.package ];
|
||||||
systemd.packages = [ cfg.package ]; #< needs to be on systemd.packages so we get its service file
|
systemd.packages = [ cfg.package ]; #< needs to be on systemd.packages so we get its service file
|
||||||
systemd.services.wpa_supplicant.path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
systemd.services.wpa_supplicant = {
|
||||||
|
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
|
||||||
|
serviceConfig.User = "networkmanager";
|
||||||
|
serviceConfig.Group = "networkmanager";
|
||||||
|
serviceConfig.AmbientCapabilities = [
|
||||||
|
"CAP_NET_ADMIN"
|
||||||
|
"CAP_NET_RAW"
|
||||||
|
];
|
||||||
|
};
|
||||||
# systemd.services.wpa_supplicant = {
|
# systemd.services.wpa_supplicant = {
|
||||||
# aliases = [ "dbus-fi.w1.wpa_supplicant1.service" ];
|
# aliases = [ "dbus-fi.w1.wpa_supplicant1.service" ];
|
||||||
# before = [ "network.target" ];
|
# before = [ "network.target" ];
|
||||||
|
|
|
@ -132,6 +132,9 @@ let
|
||||||
# this might not exist on other systems,
|
# this might not exist on other systems,
|
||||||
# so just bind the deepest path which is guaranteed to exist.
|
# so just bind the deepest path which is guaranteed to exist.
|
||||||
ReadOnlyPaths = [ "/var/lib" ];
|
ReadOnlyPaths = [ "/var/lib" ];
|
||||||
|
} // lib.optionalAttrs cfg.asSystemResolver {
|
||||||
|
# allow the group to write trust-dns state (needed by NetworkManager hook)
|
||||||
|
StateDirectoryMode = "775";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
@ -214,10 +217,23 @@ in
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# run a hook whenever networking details change, so the DNS zone can be updated to reflect this
|
||||||
environment.etc."NetworkManager/dispatcher.d/60-trust-dns-nmhook" = lib.mkIf cfg.asSystemResolver {
|
environment.etc."NetworkManager/dispatcher.d/60-trust-dns-nmhook" = lib.mkIf cfg.asSystemResolver {
|
||||||
source = "${trust-dns-nmhook}/bin/trust-dns-nmhook";
|
source = "${trust-dns-nmhook}/bin/trust-dns-nmhook";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# allow NetworkManager (via trust-dns-nmhook) to restart trust-dns when necessary
|
||||||
|
# - source: <https://stackoverflow.com/questions/61480914/using-policykit-to-allow-non-root-users-to-start-and-stop-a-service>
|
||||||
|
security.polkit.extraConfig = lib.mkIf cfg.asSystemResolver ''
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if (subject.isInGroup("trust-dns") &&
|
||||||
|
action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||||
|
action.lookup("unit") == "trust-dns-localhost.service") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
'';
|
||||||
|
|
||||||
sane.services.trust-dns.instances.localhost = lib.mkIf cfg.asSystemResolver {
|
sane.services.trust-dns.instances.localhost = lib.mkIf cfg.asSystemResolver {
|
||||||
listenAddrsIpv4 = [ "127.0.0.1" ];
|
listenAddrsIpv4 = [ "127.0.0.1" ];
|
||||||
listenAddrsIpv6 = [ "::1" ];
|
listenAddrsIpv6 = [ "::1" ];
|
||||||
|
|
Loading…
Reference in New Issue
Block a user