Files
nixos-exporter/module.nix
Torjus Håkestad d4b9577070 fix: allow AF_UNIX sockets for nix daemon communication
The flake collector needs Unix domain sockets to communicate with the
nix daemon. The RestrictAddressFamilies hardening was blocking this.
Also trim trailing newlines from stderr in error messages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 23:33:15 +01:00

138 lines
3.8 KiB
Nix

{ self }:
{ config, lib, pkgs, ... }:
let
cfg = config.services.prometheus.exporters.nixos;
in
{
options.services.prometheus.exporters.nixos = {
enable = lib.mkEnableOption "NixOS Prometheus exporter";
port = lib.mkOption {
type = lib.types.port;
default = 9971;
description = "Port to listen on.";
};
listenAddress = lib.mkOption {
type = lib.types.str;
default = "0.0.0.0";
description = "Address to listen on.";
};
flake = {
enable = lib.mkEnableOption "flake collector";
url = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Flake URL for revision comparison.
Required if flake collector is enabled.
'';
};
checkInterval = lib.mkOption {
type = lib.types.str;
default = "1h";
description = "Interval between remote flake checks.";
};
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open the firewall for the exporter port.";
};
user = lib.mkOption {
type = lib.types.str;
default = "nixos-exporter";
description = "User to run the exporter as.";
};
group = lib.mkOption {
type = lib.types.str;
default = "nixos-exporter";
description = "Group to run the exporter as.";
};
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.system}.default;
description = "The nixos-exporter package to use.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.flake.enable -> cfg.flake.url != "";
message = "services.prometheus.exporters.nixos.flake.url must be set when flake collector is enabled";
}
];
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
description = "NixOS exporter user";
};
users.groups.${cfg.group} = { };
systemd.services.prometheus-nixos-exporter = {
description = "Prometheus NixOS Exporter";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
# nix is required for flake collector, git for git+https:// URLs
path = lib.mkIf cfg.flake.enable [
config.nix.package
pkgs.git
];
serviceConfig = {
User = cfg.user;
Group = cfg.group;
ExecStart = lib.concatStringsSep " " ([
"${cfg.package}/bin/nixos-exporter"
"--listen=${cfg.listenAddress}:${toString cfg.port}"
] ++ lib.optionals cfg.flake.enable [
"--collector.flake"
"--flake.url=${cfg.flake.url}"
"--flake.check-interval=${cfg.flake.checkInterval}"
]);
Restart = "on-failure";
RestartSec = "5s";
# Hardening
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
# AF_UNIX required for nix daemon communication when flake collector enabled
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]
++ lib.optionals cfg.flake.enable [ "AF_UNIX" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
MemoryDenyWriteExecute = true;
LockPersonality = true;
} // lib.optionalAttrs cfg.flake.enable {
# nix and git need writable cache directories
StateDirectory = "nixos-exporter";
Environment = [
"HOME=/var/lib/nixos-exporter"
"XDG_CACHE_HOME=/var/lib/nixos-exporter/.cache"
];
};
};
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
};
}