mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-02 04:50:21 +00:00
nixos/beszel.agent: expose GPU_COLLECTOR and refactor around it
Describe the supported GPU collectors in a single table and derive the service path, capabilities, syscall filter and device access from it. PrivateDevices is only relaxed when a selected collector actually needs device nodes, and the nodes are listed in DeviceAllow so /dev stays an allow-list, following the same pattern as ollama and the smartctl exporter. PrivateUsers only has to go when a collector needs capabilities, which are void on the host inside a user namespace; plain device access (nvidia-smi) keeps it. When smartmon relies on full /dev access no DeviceAllow is emitted at all, since any entry would turn DevicePolicy=auto into an allow-list and lock the disks out. amdgpu now defaults to amd_sysfs and intel to intel_sysfs (new in beszel 0.20.0). Both read sysfs directly and need neither a package nor /dev access; rocm-smi is deprecated upstream and intel_gpu_top is not used on the xe driver. An empty GPU_COLLECTOR is not exported, keeping upstream auto-detection. GPU_COLLECTOR still accepts upstream's comma-separated string, which is how it had to be set through the freeform environment until now.
This commit is contained in:
@@ -6,6 +6,76 @@
|
||||
}:
|
||||
let
|
||||
cfg = config.services.beszel.agent;
|
||||
|
||||
hasVideoDriver = driver: builtins.elem driver config.services.xserver.videoDrivers;
|
||||
|
||||
# Collector names must match `isValidCollectorSource` in upstream's agent/gpu.go.
|
||||
# macmon and powermetrics are macOS-only and omitted here.
|
||||
gpuCollectors = {
|
||||
# read sysfs directly, need no package or device access
|
||||
"amd_sysfs" = { };
|
||||
"intel_sysfs" = { };
|
||||
"intel_gpu_top" = {
|
||||
package = lib.getBin pkgs.intel-gpu-tools;
|
||||
deviceAllow = [ "char-drm rw" ];
|
||||
capabilities = [ "CAP_PERFMON" ];
|
||||
# perf_event_open is in @debug, not @system-service
|
||||
systemCalls = [ "perf_event_open" ];
|
||||
};
|
||||
"nvidia-smi" = {
|
||||
package = lib.getBin config.hardware.nvidia.package;
|
||||
deviceAllow = [ "char-nvidia* rw" ];
|
||||
};
|
||||
"nvml" = {
|
||||
deviceAllow = [ "char-nvidia* rw" ];
|
||||
};
|
||||
"nvtop" = {
|
||||
package = lib.getBin pkgs.nvtopPackages.full;
|
||||
deviceAllow = [
|
||||
"char-nvidia* rw"
|
||||
"char-drm rw"
|
||||
];
|
||||
};
|
||||
"rocm-smi" = {
|
||||
package = lib.getBin pkgs.rocmPackages.rocm-smi;
|
||||
deviceAllow = [
|
||||
"char-drm rw"
|
||||
"char-kfd rw"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
activeCollectors = lib.optionals (!cfg.environment.SKIP_GPU) cfg.environment.GPU_COLLECTOR;
|
||||
|
||||
collectorAttrs =
|
||||
attr: lib.unique (lib.concatMap (name: gpuCollectors.${name}.${attr} or [ ]) activeCollectors);
|
||||
|
||||
gpuPackages = map (name: gpuCollectors.${name}.package) (
|
||||
lib.filter (name: gpuCollectors.${name} ? package) activeCollectors
|
||||
);
|
||||
|
||||
gpuNeedsDevices = collectorAttrs "deviceAllow" != [ ];
|
||||
|
||||
# capabilities granted under PrivateUsers are void on the host, see
|
||||
# systemd.exec(5), so these collectors also need the user namespace disabled
|
||||
gpuNeedsCapabilities = collectorAttrs "capabilities" != [ ];
|
||||
|
||||
# Any explicit DeviceAllow turns DevicePolicy=auto into an allow-list, so the GPU
|
||||
# devices are omitted when smartmon relies on full /dev access.
|
||||
deviceAllowList =
|
||||
lib.optionals (cfg.smartmon.enable && cfg.smartmon.deviceAllow != [ ]) (
|
||||
map (device: "${device} r") cfg.smartmon.deviceAllow
|
||||
)
|
||||
++ lib.optionals (!cfg.smartmon.enable || cfg.smartmon.deviceAllow != [ ]) (
|
||||
collectorAttrs "deviceAllow"
|
||||
);
|
||||
|
||||
serviceCapabilities =
|
||||
lib.optionals cfg.smartmon.enable [
|
||||
"CAP_SYS_RAWIO"
|
||||
"CAP_SYS_ADMIN"
|
||||
]
|
||||
++ collectorAttrs "capabilities";
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
@@ -68,6 +138,37 @@ in
|
||||
Enabling this option will skip GPU tracking.
|
||||
'';
|
||||
};
|
||||
GPU_COLLECTOR = lib.mkOption {
|
||||
# upstream takes a comma-separated string, which used to be passed through as is
|
||||
type =
|
||||
with lib.types;
|
||||
coercedTo str (value: map lib.trim (lib.splitString "," value)) (
|
||||
listOf (enum (lib.attrNames gpuCollectors))
|
||||
);
|
||||
default =
|
||||
lib.optionals (hasVideoDriver "nvidia") [ "nvidia-smi" ]
|
||||
++ lib.optionals (hasVideoDriver "amdgpu") [ "amd_sysfs" ]
|
||||
++ lib.optionals (hasVideoDriver "intel") [ "intel_sysfs" ];
|
||||
defaultText = lib.literalMD ''
|
||||
derived from {option}`services.xserver.videoDrivers`
|
||||
'';
|
||||
example = [
|
||||
"nvidia-smi"
|
||||
"intel_gpu_top"
|
||||
];
|
||||
description = ''
|
||||
GPU collectors to use, in priority order. Overrides the agent's
|
||||
auto-detection; the packages needed by the selected collectors are added
|
||||
to the service path. If empty, the agent auto-detects available
|
||||
collectors. `rocm-smi` is deprecated upstream in favour of `amd_sysfs`,
|
||||
and `intel_gpu_top` is not used on the xe driver, where `intel_sysfs` is
|
||||
preferred.
|
||||
|
||||
Access to GPU device nodes is only granted for the collectors listed
|
||||
here, so a collector provided through
|
||||
{option}`services.beszel.agent.extraPath` has to be listed as well.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
@@ -137,25 +238,22 @@ in
|
||||
wants = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
|
||||
# drop empty lists so an unset GPU_COLLECTOR keeps upstream auto-detection
|
||||
environment = lib.mapAttrs (
|
||||
_: value: if lib.isBool value then (lib.boolToString value) else value
|
||||
) (cfg.environment // { DATA_DIR = cfg.dataDir; });
|
||||
_: value:
|
||||
if lib.isBool value then
|
||||
(lib.boolToString value)
|
||||
else if lib.isList value then
|
||||
lib.concatStringsSep "," value
|
||||
else
|
||||
value
|
||||
) (lib.filterAttrs (_: value: value != [ ]) (cfg.environment // { DATA_DIR = cfg.dataDir; }));
|
||||
|
||||
path =
|
||||
cfg.extraPath
|
||||
++ lib.optionals cfg.smartmon.enable [ cfg.smartmon.package ]
|
||||
++ lib.optionals config.boot.zfs.enabled [ config.boot.zfs.package ]
|
||||
++ lib.optionals (!cfg.environment.SKIP_GPU) (
|
||||
lib.optionals (builtins.elem "nvidia" config.services.xserver.videoDrivers) [
|
||||
(lib.getBin config.hardware.nvidia.package)
|
||||
]
|
||||
++ lib.optionals (builtins.elem "amdgpu" config.services.xserver.videoDrivers) [
|
||||
(lib.getBin pkgs.rocmPackages.rocm-smi)
|
||||
]
|
||||
++ lib.optionals (builtins.elem "intel" config.services.xserver.videoDrivers) [
|
||||
(lib.getBin pkgs.intel-gpu-tools)
|
||||
]
|
||||
);
|
||||
++ gpuPackages;
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = ''
|
||||
@@ -176,26 +274,21 @@ in
|
||||
DynamicUser = true;
|
||||
User = "beszel-agent";
|
||||
|
||||
# Capabilities needed for SMART monitoring
|
||||
AmbientCapabilities = lib.mkIf cfg.smartmon.enable [
|
||||
"CAP_SYS_RAWIO"
|
||||
"CAP_SYS_ADMIN"
|
||||
];
|
||||
CapabilityBoundingSet = lib.mkIf cfg.smartmon.enable [
|
||||
"CAP_SYS_RAWIO"
|
||||
"CAP_SYS_ADMIN"
|
||||
];
|
||||
# Capabilities needed for SMART monitoring and GPU performance counters
|
||||
AmbientCapabilities = serviceCapabilities;
|
||||
CapabilityBoundingSet = serviceCapabilities;
|
||||
|
||||
# Device access for SMART monitoring
|
||||
DeviceAllow = lib.mkIf (cfg.smartmon.enable && cfg.smartmon.deviceAllow != [ ]) (
|
||||
map (device: "${device} r") cfg.smartmon.deviceAllow
|
||||
);
|
||||
DeviceAllow = lib.mkIf (deviceAllowList != [ ]) deviceAllowList;
|
||||
|
||||
LockPersonality = true;
|
||||
NoNewPrivileges = !cfg.smartmon.enable;
|
||||
PrivateDevices = !cfg.smartmon.enable && !config.boot.zfs.enabled && cfg.environment.SKIP_GPU;
|
||||
PrivateDevices = !cfg.smartmon.enable && !config.boot.zfs.enabled && !gpuNeedsDevices;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = !cfg.smartmon.enable && !config.boot.zfs.enabled && !cfg.environment.SKIP_SYSTEMD;
|
||||
PrivateUsers =
|
||||
!cfg.smartmon.enable
|
||||
&& !config.boot.zfs.enabled
|
||||
&& !cfg.environment.SKIP_SYSTEMD
|
||||
&& !gpuNeedsCapabilities;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = "strict";
|
||||
ProtectHome = "read-only";
|
||||
@@ -210,7 +303,7 @@ in
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
SystemCallFilter = [ "@system-service" ];
|
||||
SystemCallFilter = [ "@system-service" ] ++ collectorAttrs "systemCalls";
|
||||
Type = "simple";
|
||||
UMask = 27;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user