nixos/perses: init

This commit is contained in:
Dustin Frisch
2025-05-28 17:23:43 +02:00
parent 0c7fdb7843
commit 944ef0ea3b
6 changed files with 203 additions and 0 deletions

View File

@@ -66,6 +66,8 @@
- [Shoko](https://shokoanime.com), an anime management system. Available as [services.shoko](#opt-services.shoko.enable).
- [perses](https://perses.dev/), the open dashboard tool for Prometheus and other data sources. Available as [services.perses](#opt-services.perses.enable).
- [Drasl](https://github.com/unmojang/drasl), an alternative authentication server for Minecraft. Available as [services.drasl](#opt-services.drasl.enable).
## Backward Incompatibilities {#sec-release-26.05-incompatibilities}

View File

@@ -1038,6 +1038,7 @@
./services/monitoring/opentelemetry-collector.nix
./services/monitoring/osquery.nix
./services/monitoring/parsedmarc.nix
./services/monitoring/perses.nix
./services/monitoring/pgscv.nix
./services/monitoring/prometheus/alertmanager-gotify-bridge.nix
./services/monitoring/prometheus/alertmanager-irc-relay.nix

View File

@@ -0,0 +1,131 @@
{
pkgs,
lib,
config,
utils,
...
}:
let
inherit (lib)
getExe
mkOption
mkEnableOption
mkPackageOption
mkIf
types
;
cfg = config.services.perses;
settingsFormat = pkgs.formats.yaml { };
configPath = "/run/perses/config.yaml";
secretsReplacement = utils.genJqSecretsReplacement {
loadCredential = true;
} cfg.settings configPath;
in
{
options.services.perses = {
enable = mkEnableOption "perses";
package = mkPackageOption pkgs "perses" { };
port = mkOption {
type = types.port;
default = 8080;
description = ''
Perses Web interface port.
'';
};
listenAddress = mkOption {
type = types.str;
default = "";
description = ''
Address to listen on. Empty string will listen on all interfaces.
'';
};
settings = mkOption {
type = types.submodule {
freeformType = settingsFormat.type;
};
description = ''
Perses settings. See <https://perses.dev/perses/docs/configuration/configuration/> for available options.
You can specify secret values in this configuration by setting `somevalue._secret = "/path/to/file"` instead of setting `somevalue` directly.
'';
default = { };
};
extraOptions = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"-web.telemetry-path=/metrics"
];
description = "Additional options passed to perses daemon.";
};
};
config = mkIf cfg.enable {
systemd.services.perses = {
description = "Perses Daemon";
wantedBy = [ "multi-user.target" ];
after = [ "networking.target" ];
preStart = secretsReplacement.script;
serviceConfig = rec {
ExecStart = utils.escapeSystemdExecArgs (
[
(getExe cfg.package)
"-config=${configPath}"
"-web.listen-address=${cfg.listenAddress}:${toString cfg.port}"
]
++ cfg.extraOptions
);
User = "perses";
DynamicUser = true;
Restart = "on-failure";
RuntimeDirectory = "perses";
RuntimeDirectoryMode = "0755";
StateDirectory = "perses";
WorkingDirectory = "%S/${StateDirectory}";
LoadCredential = secretsReplacement.credentials;
# Hardening
AmbientCapabilities = mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = if (cfg.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "full";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0027";
};
};
environment.systemPackages = [ cfg.package ];
};
}

View File

@@ -1249,6 +1249,7 @@ in
peerflix = runTest ./peerflix.nix;
peering-manager = runTest ./web-apps/peering-manager.nix;
peertube = handleTestOn [ "x86_64-linux" ] ./web-apps/peertube.nix { };
perses = runTest ./perses.nix;
pgadmin4 = runTest ./pgadmin4.nix;
pgbackrest = import ./pgbackrest { inherit runTest; };
pgbouncer = runTest ./pgbouncer.nix;

65
nixos/tests/perses.nix Normal file
View File

@@ -0,0 +1,65 @@
{ pkgs, lib, ... }:
{
name = "perses";
meta.maintainers = with lib.maintainers; [ fooker ];
nodes.prometheus = {
services.prometheus.enable = true;
networking.firewall.allowedTCPPorts = [ 9090 ];
};
nodes.machine = {
services.perses = {
enable = true;
settings.provisioning.folders = [
(pkgs.writeTextDir "perses-test-provision-project.yaml" ''
kind: "Project"
metadata:
name: "my-project"
'')
(pkgs.writeTextDir "perses-test-provision-datasource.yaml" ''
kind: "Datasource"
metadata:
name: "my-prometheus"
project: "my-project"
spec:
default: true
plugin:
kind: "PrometheusDatasource"
spec:
proxy:
kind: "HTTPProxy"
spec:
url: "http://prometheus:9090/"
'')
];
};
};
testScript = ''
start_all()
prometheus.wait_for_unit("prometheus.service")
prometheus.wait_for_open_port(9090)
with subtest("Perses starts"):
machine.wait_for_unit("perses.service")
machine.wait_for_open_port(8080)
machine.succeed("percli login http://127.0.0.1:8080")
machine.succeed("percli version")
machine.succeed("percli config")
machine.succeed("percli plugin list | grep 'Prometheus'")
with subtest("Query resources"):
machine.succeed("percli get projects | grep my-project")
machine.succeed("percli project my-project")
machine.succeed("percli get datasources | grep my-prometheus")
machine.succeed("curl --fail -s 127.0.0.1:8080/api/v1/projects/my-project/datasources/my-prometheus")
with subtest("Datasource check"):
machine.succeed("curl --fail -s http://127.0.0.1:8080/proxy/projects/my-project/datasources/my-prometheus/api/v1/status/config")
'';
}

View File

@@ -10,6 +10,7 @@
turbo,
linkFarm,
installShellFiles,
nixosTests,
}:
let
@@ -118,6 +119,8 @@ buildGoModule (finalAttrs: {
passthru = {
updateScript = ./update.sh;
tests.nixos = nixosTests.perses;
inherit pluginsArchive;
};