nix: initial nixos module; complete packaging
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I59ec5a4f67ddae2139e72ef4c0c113366a6a6964
This commit is contained in:
parent
da1fab4257
commit
75018f69df
3 changed files with 223 additions and 10 deletions
182
nix/module.nix
Normal file
182
nix/module.nix
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
inputs: {
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib.modules) mkIf;
|
||||||
|
inherit (lib.options) mkOption mkEnableOption mkPackageOption;
|
||||||
|
inherit (lib.types) str path bool;
|
||||||
|
|
||||||
|
settingsType = (pkgs.formats.yaml {}).type;
|
||||||
|
|
||||||
|
cfg = config.services.watchdog;
|
||||||
|
in {
|
||||||
|
meta.maintainers = with lib.maintainers; [NotAShelf];
|
||||||
|
|
||||||
|
options.services.watchdog = {
|
||||||
|
enable = mkEnableOption "Watchdog privacy-preserving analytics";
|
||||||
|
|
||||||
|
package = mkPackageOption inputs.self.packages.${pkgs.stdenv.hostPlatform.system} "watchdog" {
|
||||||
|
pkgsText = "self.packages.\${pkgs.stdenv.hostPlatform.system}";
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = settingsType;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Configuration for Watchdog analytics.
|
||||||
|
|
||||||
|
See <https://github.com/notashelf/watchdog> for available options.
|
||||||
|
'';
|
||||||
|
example = {
|
||||||
|
site = {
|
||||||
|
domain = "example.com";
|
||||||
|
salt_rotation = "daily";
|
||||||
|
sampling = 1.0;
|
||||||
|
collect = {
|
||||||
|
pageviews = true;
|
||||||
|
country = false;
|
||||||
|
device = true;
|
||||||
|
referrer = "domain";
|
||||||
|
};
|
||||||
|
|
||||||
|
path = {
|
||||||
|
strip_query = true;
|
||||||
|
strip_fragment = true;
|
||||||
|
collapse_numeric_segments = true;
|
||||||
|
max_segments = 5;
|
||||||
|
normalize_trailing_slash = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
limits = {
|
||||||
|
max_paths = 10000;
|
||||||
|
max_sources = 500;
|
||||||
|
max_custom_events = 100;
|
||||||
|
max_events_per_minute = 10000;
|
||||||
|
device_breakpoints = {
|
||||||
|
mobile = 768;
|
||||||
|
tablet = 1024;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
security = {
|
||||||
|
trusted_proxies = ["127.0.0.1" "::1"];
|
||||||
|
cors = {
|
||||||
|
enabled = false;
|
||||||
|
allowed_origins = ["*"];
|
||||||
|
};
|
||||||
|
metrics_auth = {
|
||||||
|
enabled = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
server = {
|
||||||
|
listen_addr = "127.0.0.1:8080";
|
||||||
|
metrics_path = "/metrics";
|
||||||
|
ingestion_path = "/api/event";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = str;
|
||||||
|
default = "watchdog";
|
||||||
|
description = "User account under which Watchdog runs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = str;
|
||||||
|
default = "watchdog";
|
||||||
|
description = "Group under which Watchdog runs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = path;
|
||||||
|
default = "/var/lib/watchdog";
|
||||||
|
description = "Directory for Watchdog state (HLL persistence).";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = bool;
|
||||||
|
default = false;
|
||||||
|
description = "Open firewall port for Watchdog.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.watchdog = {
|
||||||
|
description = "Watchdog Privacy Analytics";
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
after = ["network-online.target"];
|
||||||
|
wants = ["network-online.target"];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
|
||||||
|
ExecStart = "${lib.getExe cfg.package} -config ${settingsType.generate "config.yaml" cfg.settings}";
|
||||||
|
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "5s";
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
ReadWritePaths = [cfg.stateDir];
|
||||||
|
|
||||||
|
# Capabilities
|
||||||
|
AmbientCapabilities = "";
|
||||||
|
CapabilityBoundingSet = "";
|
||||||
|
|
||||||
|
# Sandboxing
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"];
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
RemoveIPC = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
|
||||||
|
# System call filtering
|
||||||
|
SystemCallFilter = ["@system-service" "~@privileged" "~@resources"];
|
||||||
|
SystemCallErrorNumber = "EPERM";
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users = {
|
||||||
|
users = mkIf (cfg.user == "watchdog") {
|
||||||
|
watchdog = {
|
||||||
|
description = "Watchdog analytics daemon user";
|
||||||
|
isSystemUser = true;
|
||||||
|
group = cfg.group;
|
||||||
|
home = cfg.stateDir;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
groups = mkIf (cfg.group == "watchdog") {
|
||||||
|
watchdog = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.firewall = mkIf cfg.openFirewall {
|
||||||
|
allowedTCPPorts = let
|
||||||
|
# Extract port from listen_addr (format: "host:port" or ":port")
|
||||||
|
listenAddr = cfg.settings.server.listen_addr or "127.0.0.1:8080";
|
||||||
|
port = lib.toInt (lib.last (lib.splitString ":" listenAddr));
|
||||||
|
in [port];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,40 @@
|
||||||
{buildGoModule}:
|
{
|
||||||
buildGoModule {
|
lib,
|
||||||
pname = "sample-go";
|
buildGoModule,
|
||||||
version = "0.0.1";
|
}:
|
||||||
|
buildGoModule (finalAttrs: {
|
||||||
|
pname = "watchdog";
|
||||||
|
version = "0.1.0";
|
||||||
|
|
||||||
src = ../.;
|
src = let
|
||||||
|
fs = lib.fileset;
|
||||||
|
s = ../.;
|
||||||
|
in
|
||||||
|
fs.toSource {
|
||||||
|
root = s;
|
||||||
|
fileset = fs.unions [
|
||||||
|
(s + /cmd)
|
||||||
|
(s + /internal)
|
||||||
|
(s + /go.mod)
|
||||||
|
(s + /go.sum)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
vendorHash = "";
|
vendorHash = "sha256-jMqPVvMZDm406Gi2g4zNSRJMySLAN7/CR/2NgF+gqtA=";
|
||||||
|
|
||||||
ldflags = ["-s" "-w"];
|
ldflags = ["-s" "-w" "-X main.version=${finalAttrs.version}"];
|
||||||
}
|
|
||||||
|
# Copy web assets
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $out/share/watchdog
|
||||||
|
cp -r $src/web $out/share/watchdog/
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Privacy-preserving web analytics with Prometheus-native metrics";
|
||||||
|
homepage = "https://github.com/notashelf/watchdog";
|
||||||
|
license = lib.licenses.gpl3Only;
|
||||||
|
maintainers = with lib.maintainers; [NotAShelf];
|
||||||
|
mainProgram = "watchdog";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@
|
||||||
mkShell,
|
mkShell,
|
||||||
go,
|
go,
|
||||||
gopls,
|
gopls,
|
||||||
|
golines,
|
||||||
delve,
|
delve,
|
||||||
}:
|
}:
|
||||||
mkShell {
|
mkShell {
|
||||||
name = "go";
|
name = "go";
|
||||||
packages = [
|
packages = [
|
||||||
delve
|
|
||||||
go
|
go
|
||||||
gopls
|
gopls # formatter
|
||||||
|
golines # line wrapper
|
||||||
|
delve # debugger
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue