Compare commits

...

13 Commits

Author SHA1 Message Date
5ec159bbf5 WIP: networkmanager: restrict (using systemd options) 2024-06-03 13:05:51 +00:00
f6725f60b9 networkmanager: re-introduce my polkit patches 2024-06-03 13:04:48 +00:00
2f1592376d document more sandbox limitations 2024-06-03 11:59:44 +00:00
42fed64b75 NetworkManager: split specific config options out of my main net/default.nix file 2024-06-03 11:24:38 +00:00
682143d47f NetworkManager: 1.46.0 -> 1.48.0
mostly so i can review the PR and get this update mainlined sooner :)
2024-06-03 11:23:33 +00:00
1448cb4444 sane-reboot: fix operation on servo 2024-06-03 09:33:35 +00:00
2d07ff966b health-check: mention failed systemd services 2024-06-03 09:30:02 +00:00
83404f6769 nixos/networkmanager patch: grab via PR 2024-06-03 07:49:53 +00:00
c6bb6e2e3c megapixels-next: fix broken eval of metadata 2024-06-03 05:35:09 +00:00
9d109644b7 nixpkgs: 2024-06-01 -> 2024-06-03; sops-nix -> 2024-06-02
```
• Updated input 'nixpkgs-next-unpatched':
    'github:nixos/nixpkgs/f7de25c01e4c073c06e0525226a0c2311d530cee' (2024-06-01)
  → 'github:nixos/nixpkgs/c987c730bbf2121264ebd68921b443db5bb28543' (2024-06-03)
• Updated input 'nixpkgs-unpatched':
    'github:nixos/nixpkgs/61c1d282153dbfcb5fe413c228d172d0fe7c2a7e' (2024-06-01)
  → 'github:nixos/nixpkgs/77a51024c0f953d503eb3ed364aa4bff378649f8' (2024-06-03)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/962797a8d7f15ed7033031731d0bb77244839960' (2024-05-26)
  → 'github:Mic92/sops-nix/ab2a43b0d21d1d37d4d5726a892f714eaeb4b075' (2024-06-02)
• Updated input 'sops-nix/nixpkgs-stable':
    'github:NixOS/nixpkgs/59a450646ec8ee0397f5fa54a08573e8240eb91f' (2024-05-25)
  → 'github:NixOS/nixpkgs/3b1b4895b2c5f9f5544d02132896aeb9ceea77bc' (2024-06-01)
```
2024-06-03 05:31:28 +00:00
0050403b31 scripts: add an update helper
someday i can extend this for updating packages and feeds too
2024-06-03 05:30:03 +00:00
e4bcbab224 hosts: networking: switch to using nixos NetworkManager/ModemManager/etc, just patched for hardening 2024-06-02 11:22:03 +00:00
1b85aa0441 networkmanager/modemmanager: get closer to nixpkgs upstream
i've seen enough, that there's a path toward getting nixos proper to sandbox this in a way i'm happy with -- in time
2024-06-02 08:56:38 +00:00
17 changed files with 350 additions and 433 deletions

View File

@@ -84,6 +84,8 @@
#### sudo-free world
- `systemctl restart FOO`: needs `sudo`
- `systemctl daemon-reload`: needs sudo
- `watch ifconfig`: needs `SANEBOX_DISABLE=1`
### user experience
- rofi: sort items case-insensitively

24
flake.lock generated
View File

@@ -167,11 +167,11 @@
},
"nixpkgs-next-unpatched": {
"locked": {
"lastModified": 1717243271,
"narHash": "sha256-M3VxP6DtREz5Lq6MKg1gQ2EeVdIbq6AEp/N1tDSrvoc=",
"lastModified": 1717372940,
"narHash": "sha256-fK1PJqC8kQOy8rD7B+qmJOTx9IV8AOmFtH5Z/ip7340=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "f7de25c01e4c073c06e0525226a0c2311d530cee",
"rev": "c987c730bbf2121264ebd68921b443db5bb28543",
"type": "github"
},
"original": {
@@ -183,11 +183,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1716655032,
"narHash": "sha256-kQ25DAiCGigsNR/Quxm3v+JGXAEXZ8I7RAF4U94bGzE=",
"lastModified": 1717265169,
"narHash": "sha256-IITcGd6xpNoyq9SZBigCkv4+qMHSqot0RDPR4xsZ2CA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "59a450646ec8ee0397f5fa54a08573e8240eb91f",
"rev": "3b1b4895b2c5f9f5544d02132896aeb9ceea77bc",
"type": "github"
},
"original": {
@@ -199,11 +199,11 @@
},
"nixpkgs-unpatched": {
"locked": {
"lastModified": 1717242134,
"narHash": "sha256-2X835ZESUaQ/KZEuG9HkoEB7h0USG5uvkSUmLzFkxAE=",
"lastModified": 1717392304,
"narHash": "sha256-i9Kh2ty++/xMj4GPTMI7vQrpH4jopjT4BUq2GKX1zug=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "61c1d282153dbfcb5fe413c228d172d0fe7c2a7e",
"rev": "77a51024c0f953d503eb3ed364aa4bff378649f8",
"type": "github"
},
"original": {
@@ -254,11 +254,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1716692524,
"narHash": "sha256-sALodaA7Zkp/JD6ehgwc0UCBrSBfB4cX66uFGTsqeFU=",
"lastModified": 1717297459,
"narHash": "sha256-cZC2f68w5UrJ1f+2NWGV9Gx0dEYmxwomWN2B0lx0QRA=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "962797a8d7f15ed7033031731d0bb77244839960",
"rev": "ab2a43b0d21d1d37d4d5726a892f714eaeb4b075",
"type": "github"
},
"original": {

View File

@@ -4,6 +4,8 @@
imports = [
./dns.nix
./hostnames.nix
./modemmanager.nix
./networkmanager.nix
./upnp.nix
./vpn.nix
];
@@ -24,41 +26,4 @@
# this is required separately by servo and by any `sane-vpn` users,
# however Nix requires this be set centrally, in only one location (i.e. here)
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# 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
# };
# 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 [];
# keyfile.path = where networkmanager should look for connection credentials
networking.networkmanager.settings.keyfile.path = "/var/lib/NetworkManager/system-connections";
}

View File

@@ -0,0 +1,60 @@
{ config, lib, pkgs, ... }:
{
networking.modemmanager.package = pkgs.modemmanager-split.daemon.overrideAttrs (upstream: {
# patch to allow the dbus endpoints to be owned by networkmanager user
postInstall = (upstream.postInstall or "") + ''
substitute $out/share/dbus-1/system.d/org.freedesktop.ModemManager1.conf \
$out/share/dbus-1/system.d/networkmanager-org.freedesktop.ModemManager1.conf \
--replace-fail 'user="root"' 'group="networkmanager"'
'';
});
systemd.services.ModemManager = {
# aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
# after = [ "polkit.service" ];
# requires = [ "polkit.service" ];
wantedBy = [ "network.target" ]; #< default is `multi-user.target`, somehow it doesn't auto-start with that...
# path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
# serviceConfig.Type = "dbus";
# serviceConfig.BusName = "org.freedesktop.ModemManager1";
# only if started with `--debug` does mmcli let us issue AT commands like
# `mmcli --modem any --command=<AT_CMD>`
serviceConfig.ExecStart = [
"" # first blank line is to clear the upstream `ExecStart` field.
"${lib.getExe' config.networking.modemmanager.package "ModemManager"} --debug"
];
# --debug sets DEBUG level logging: so reset
serviceConfig.ExecStartPost = "${lib.getExe config.sane.programs.mmcli.package} --set-logging=INFO";
# v this is what upstream ships
# serviceConfig.Restart = "on-abort";
# serviceConfig.StandardError = "null";
# serviceConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_NET_ADMIN";
# serviceConfig.ProtectSystem = true;
# serviceConfig.ProtectHome = true;
# serviceConfig.PrivateTmp = true;
# serviceConfig.RestrictAddressFamilies = "AF_NETLINK AF_UNIX AF_QIPCRTR";
# serviceConfig.NoNewPrivileges = true;
# TODO: sandbox more aggressively
# - CAP_NET_ADMIN *only*?
# it needs these paths:
# - # "/"
# - "/dev" #v modem-power + net are not enough
# - # "/dev/modem-power"
# - # "/dev/net"
# - "/proc"
# - # /run #v can likely be reduced more
# - "/run/dbus"
# - "/run/NetworkManager"
# - "/run/resolvconf"
# - "/run/systemd"
# - "/run/udev"
# - "/sys"
};
# so that ModemManager can discover when the modem appears
# services.udev.packages = lib.mkIf cfg.enabled [ cfg.package ];
}

View File

@@ -0,0 +1,213 @@
{ 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"
"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
# "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; # TODO: might break hooks?
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
# 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 = TODO
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
'';
});
})];
}

View File

@@ -71,12 +71,14 @@ in
"man-pages"
"man-pages-posix"
# "miniupnpc"
"mmcli"
"nano"
# "ncdu" # ncurses disk usage. doesn't cross compile (zig)
"neovim"
"netcat"
"nethogs"
"nmap"
"nmcli"
"nvme-cli" # nvme
# "openssl"
"parted"

View File

@@ -81,13 +81,11 @@
./mepo.nix
./mimeo
./mmcli.nix
./modemmanager.nix
./mopidy.nix
./mpv
./msmtp.nix
./nautilus.nix
./neovim.nix
./networkmanager.nix
./newsflash.nix
./nheko.nix
./nicotine-plus.nix
@@ -144,7 +142,6 @@
./wine.nix
./wireplumber.nix
./wireshark.nix
./wpa_supplicant.nix
./wvkbd.nix
./xarchiver.nix
./xdg-desktop-portal.nix

View File

@@ -4,7 +4,7 @@ let
in
{
sane.programs.eg25-control = {
suggestedPrograms = [ "modemmanager" ];
suggestedPrograms = [ "mmcli" ];
services.eg25-control-powered = {
description = "eg25-control-powered: power to the Qualcomm eg25 modem used by PinePhone";

View File

@@ -1,81 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.modemmanager;
in
{
sane.programs.modemmanager = {
packageUnwrapped = pkgs.modemmanager-split.daemon.overrideAttrs (upstream: {
postInstall = (upstream.postInstall or "") + ''
substitute $out/share/dbus-1/system.d/org.freedesktop.ModemManager1.conf \
$out/share/dbus-1/system.d/networkmanager-org.freedesktop.ModemManager1.conf \
--replace-fail 'user="root"' 'group="networkmanager"'
'';
meta = upstream.meta // {
mainProgram = "ModemManager";
};
});
# mmcli needs /run/current-system/sw/share/dbus-1 files to function
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
suggestedPrograms = [ "mmcli" ];
# bwrap sandboxing works, but requires the real user to be root.
# landlock sandboxing works, and allows the real user to be someone else (like `networkmanager`).
# non-root is very important, because of how many things in e.g. /dev are r/w based on uid=0.
# TODO: have the services run as `networkmanager` user
sandbox.method = "bwrap";
# sandbox.method = "landlock";
sandbox.wrapperType = "inplace"; #< .pc files, GIR files with absolute paths,
sandbox.net = "all"; #< needed for modem bringup
# sandbox.isolatePids = false;
sandbox.capabilities = [
"net_admin"
"net_raw"
];
sandbox.extraPaths = lib.warn "TODO: modemmanager: sandbox more aggressively" [
# "/"
"/dev" #v modem-power + net are not enough
# "/dev/modem-power"
# "/dev/net"
"/proc"
# /run #v can likely be reduced more
"/run/dbus"
"/run/NetworkManager"
"/run/resolvconf"
"/run/systemd"
"/run/udev"
"/sys"
# "/var"
];
};
systemd.services.ModemManager = lib.mkIf cfg.enabled {
aliases = [ "dbus-org.freedesktop.ModemManager1.service" ];
after = [ "polkit.service" ];
requires = [ "polkit.service" ];
wantedBy = [ "network.target" ];
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
serviceConfig.Type = "dbus";
serviceConfig.BusName = "org.freedesktop.ModemManager1";
# only if started with `--debug` does mmcli let us issue AT commands like
# `mmcli --modem any --command=<AT_CMD>`
serviceConfig.ExecStart = "${lib.getExe cfg.package} --debug";
# --debug sets DEBUG level logging: so reset
serviceConfig.ExecStartPost = "${lib.getExe config.sane.programs.mmcli.package} --set-logging=INFO";
serviceConfig.Restart = "on-abort";
serviceConfig.StandardError = "null";
serviceConfig.CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_NET_ADMIN";
serviceConfig.ProtectSystem = true;
serviceConfig.ProtectHome = true;
serviceConfig.PrivateTmp = true;
serviceConfig.RestrictAddressFamilies = "AF_NETLINK AF_UNIX AF_QIPCRTR";
serviceConfig.NoNewPrivileges = true;
};
# so that ModemManager can discover when the modem appears
services.udev.packages = lib.mkIf cfg.enabled [ cfg.package ];
}

View File

@@ -1,209 +0,0 @@
# Network Manager:
# i manage this myself because the nixos service is not flexible enough.
# - it unconditionally puts modemmanager onto the system path, preventing me from patching modemmanager's service file (without an overlay).
#
# XXX: it's normal to see error messages on an ethernet-only host, even when using nixos' official networkmanager service:
# - `Couldn't initialize supplicant interface: Failed to D-Bus activate wpa_supplicant service`
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.networkmanager;
networkmanager = pkgs.networkmanager.overrideAttrs (upstream: {
src = pkgs.fetchFromGitea {
domain = "git.uninsane.org";
owner = "colin";
repo = "NetworkManager";
rev = "dev-sane-1.46.0";
hash = "sha256-S5ZiOfCpwtVVVO+DP6OPodJqzSc/LW4waI42DRkT+RA=";
};
# patches = [];
});
networkmanager-split = pkgs.networkmanager-split.override { inherit networkmanager; };
in
{
config = lib.mkMerge [
{
sane.programs.networkmanager = {
packageUnwrapped = 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}
'';
});
suggestedPrograms = [ "nmcli" "wpa_supplicant" ];
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
# this contains both the NetworkManager service and the NetworkManager-dispatcher service
# 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.
sandbox.method = "landlock";
sandbox.capabilities = [
# "dac_override"
"net_admin"
"net_raw"
"net_bind_service" #< TODO: is this needed? why? (DNS?)
# "sys_module"
"audit_write" #< allow writing to the audit log
# "kill"
];
sandbox.extraPaths = [
"/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
];
sandbox.whitelistDbus = [ "system" ];
};
}
(lib.mkIf cfg.enabled {
# add to systemd.packages so we get the service file it ships, then override what we need to customize (taken from nixpkgs)
systemd.packages = [ cfg.package ];
systemd.services.NetworkManager = {
wantedBy = [ "network.target" ];
aliases = [ "dbus-org.freedesktop.NetworkManager.service" ];
path = [ "/run/current-system/sw" ]; #< so it can find `sanebox`
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"
"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
# "CAP_KILL"
];
};
systemd.services.NetworkManager-wait-online = {
path = [ "/run/current-system/sw" ]; #< so `nm-online` can find `sanebox`
wantedBy = [ "network-online.target" ];
serviceConfig.User = "networkmanager";
serviceConfig.Group = "networkmanager";
};
systemd.services.NetworkManager-dispatcher = {
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`
# to debug, add NM_DISPATCHER_DEBUG_LOG=1
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";
};
environment.etc = {
"NetworkManager/system-connections".source = "/var/lib/NetworkManager/system-connections";
"NetworkManager/NetworkManager.conf".text = ''
[device]
# wifi.backend: wpa_supplicant or iwd
wifi.backend=wpa_supplicant
wifi.scan-rand-mac-address=true
[logging]
audit=false
# level: TRACE, DEBUG, INFO, WARN, ERR, OFF
level=INFO
# domain=...
[main]
# dhcp:
# - `internal` (default)
# - `dhclient` (requires dhclient to be installed)
# - `dhcpcd` (requires dhcpcd to be installed)
dhcp=internal
# dns:
# - `default`: update /etc/resolv.conf with nameservers provided by the active connection
# - `none`: NM won't update /etc/resolv.conf
# - `systemd-resolved`: push DNS config to systemd-resolved
# - `dnsmasq`: run a local caching nameserver
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"
}
plugins=keyfile
# rc-manager: how NM should write to /etc/resolv.conf
# - regardless of this setting, NM will write /var/lib/NetworkManager/resolv.conf
rc-manager=unmanaged
# systemd-resolved: send DNS config to systemd-resolved?
# this setting has no effect if dns="systemd-resolved"; it's supplementary, not absolute.
systemd-resolved=false
# debug=... (see also: NM_DEBUG env var)
'';
};
hardware.wirelessRegulatoryDatabase = true;
networking.useDHCP = false;
services.udev.packages = [ cfg.package ];
security.polkit.enable = lib.mkDefault true;
security.polkit.extraConfig = lib.concatStringsSep "\n" [
# allow networkmanager unbounded control over modemmanager.
# i believe this was sourced from the default nixpkgs config.
''
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;
}
});
''
# allow networkmanager to control systemd-resolved,
# which it needs to do to apply new DNS settings when using systemd-resolved.
''
polkit.addRule(function(action, subject) {
if (subject.isInGroup("networkmanager") && action.id.indexOf("org.freedesktop.resolve1.") == 0) {
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)?
# TODO: NetworkManager-ensure-profiles?
})
];
}

View File

@@ -140,7 +140,6 @@ in
"fontconfig"
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
"networkmanager"
"pipewire"
"playerctl" # for waybar & particularly to have playerctld running
"rofi" # menu/launcher

View File

@@ -1,77 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.wpa_supplicant;
in
{
config = lib.mkMerge [
{
sane.programs.wpa_supplicant = {
packageUnwrapped = pkgs.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 "") + ''
# nixpkgs wpa_supplicant generates a dbus file which has a path like
# /nix/store/abc-wpa_supplicant/nix/store/abc-wpa_supplicant/sbin/...
# upstreaming status: <https://github.com/NixOS/nixpkgs/pull/315346>
substituteInPlace $out/share/dbus-1/system-services/* --replace-fail \
"$out$out" "$out"
# remove unused services to avoid unexpected interactions
rm $out/etc/systemd/system/{wpa_supplicant-nl80211@,wpa_supplicant-wired@,wpa_supplicant@}.service
'';
});
# bwrap sandboxing works, but requires the real user to be root.
# landlock sandboxing works, and allows the real user to be someone else (like `networkmanager`).
# non-root is very important, because of how many things in e.g. /dev are r/w based on uid=0.
# sandbox.method = "bwrap";
sandbox.method = "landlock";
sandbox.capabilities = [
# see also: <https://github.com/NixOS/nixpkgs/pull/305722>
"net_admin" "net_raw"
];
# sandbox.extraConfig = [ "--sanebox-keep-namespace" "all" ];
sandbox.net = "all";
sandbox.extraPaths = [
"/dev/net"
"/dev/rfkill"
"/proc/sys/net"
"/sys/class/net"
"/sys/devices"
];
sandbox.whitelistDbus = [ "system" ];
};
}
(lib.mkIf cfg.enabled {
services.udev.packages = [ cfg.package ];
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`
serviceConfig.User = "networkmanager";
serviceConfig.Group = "networkmanager";
serviceConfig.AmbientCapabilities = [
"CAP_NET_ADMIN"
"CAP_NET_RAW"
];
};
# systemd.services.wpa_supplicant = {
# aliases = [ "dbus-fi.w1.wpa_supplicant1.service" ];
# before = [ "network.target" ];
# wantedBy = [ "network.target" ];
# serviceConfig = {
# Type = "dbus";
# BusName = "fi.w1.wpa_supplicant1";
# ExecStart = "${cfg.package}/bin/wpa_supplicant -u";
# Restart = "always";
# };
# };
})
];
}

View File

@@ -33,17 +33,23 @@ in [
# and can be found with `nix-repl > :lf . > lastModifiedDate`
(fetchpatch' {
title = "trust-dns: rebrand as hickory-dns";
saneCommit = "a7613d50c58b5612a7b806ce1375d8bf0485ab55";
hash = "sha256-N4DfNhCeg9etVeTPVzJ73CwiZzf43SozK8nrfXIm0uQ=";
title = "networkmanager: 1.46.0 1.48.0";
prUrl = "https://github.com/NixOS/nixpkgs/pull/316417";
hash = "sha256-LfQyB3tzQa3UNyZl9HWCuoyBznIhinodlTL6TnPn0Uk=";
})
(fetchpatch' {
title = "libphonenumber: fix cross compilation";
prUrl = "https://github.com/NixOS/nixpkgs/pull/316094";
saneCommit = "151fb5ea9c43847dbd114cb80c04ce7118fef95d";
# hash = "sha256-dvgpuldhBlSrpwkqNEtgAHqhaGsrMwoAT+Q5J27NYrQ=";
hash = "sha256-N8ZA/93qTmrCE9eE8VPgkXD/ciTOHdD5zRz9JJLU5gA=";
title = "nixos/networkmanager: split ModemManager bits into own module";
prUrl = "https://github.com/NixOS/nixpkgs/pull/316824";
hash = "sha256-u0hd2VfEcqkhs4xgv1gF6sXqnVJYVRgmSKBrmYUw29c=";
# saneCommit = "23bfba9b76757ffc00fc2be810009dcf92e2eaf2";
# hash = "sha256-cn6ihwO3MyzdpVoJoQNKAHyo8GuGvFP6vr//7r9pzjE=";
})
(fetchpatch' {
title = "trust-dns: rebrand as hickory-dns";
saneCommit = "a7613d50c58b5612a7b806ce1375d8bf0485ab55";
hash = "sha256-N4DfNhCeg9etVeTPVzJ73CwiZzf43SozK8nrfXIm0uQ=";
})
(fetchpatch' {

View File

@@ -52,7 +52,6 @@ stdenv.mkDerivation {
meta = with lib; {
description = "The Linux-phone camera application";
homepage = "https://gitlab.com/megapixels-org/Megapixels";
changelog = "https://gitlab.com/megapixels-org/Megapixels/-/tags/${finalAttrs.version}";
license = licenses.gpl3Plus;
maintainers = with maintainers; [ colinsane ];
platforms = platforms.linux;

View File

@@ -7,8 +7,10 @@ shift
host="$(hostname)"
if [ "$host" = "$target" ]
then
# N.B.: anything other than just `reboot` with no args requires `sudo` privilegs (to write to /run/systemd/)
reboot "$@"
# N.B.: anything other than just `reboot` with no args requires `sudo` privileges (to write to /run/systemd/).
# `systemctl reboot -i` tells systemd to ignore inhibitors (i.e. other users logged in).
reboot "$@" || \
systemctl reboot -i "$@"
else
echo "WRONG MACHINE. you're on $host."
exit 1

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p curl -p dig -p iputils -p lftp
#!nix-shell -i bash -p curl -p dig -p iputils -p lftp -p openssh
echo "this script will check that uninsane.org is baseline operational"
echo "it doesn't check all services, just the most critical ones"
@@ -45,6 +45,10 @@ check "git.uninsane.org" git ls-remote origin --quiet
check "ftp://uninsane.org" lftpget ftp://uninsane.org/README.md && rm README.md
echo ""
echo "systemctl --failed:"
ssh uninsane.org systemctl -q --failed
echo ""
if [ $last_error -eq 0 ]; then
echo "SUCCESS"

35
scripts/update Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/sh
inputs=()
addInputs() {
case $1 in
(safe)
inputs+=(uninsane-dot-org nixpkgs-unpatched nixpkgs-next-unpatched sops-nix)
;;
(unsafe)
# these tend to break more frequently
inputs+=(mobile-nixos nixpkgs-wayland)
;;
(*)
echo "unknown input '$1'"
exit 1
;;
esac
}
case "$1" in
(all|"")
addInputs "safe"
addInputs "unsafe"
;;
(*)
addInputs "$1"
;;
esac
echo "updating:" "${inputs[@]}"
nixFlags=()
for i in "${inputs[@]}"; do
nixFlags+=("--update-input" "$i")
done
nix flake lock "${nixFlags[@]}"