{ config, lib, pkgs, ... }: let cfg = config.sane.programs.modemmanager; in { sane.programs.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; sandbox.method = "bwrap"; #< landlock also works 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=` serviceConfig.ExecStart = "${cfg.package}/bin/ModemManager --debug"; # --debug sets DEBUG level logging: so reset serviceConfig.ExecStartPost = "${cfg.package}/bin/mmcli --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 ]; }