dnstt_exporter/nixos-module.nix
2026-05-05 14:07:27 +02:00

190 lines
4.7 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.prometheus.exporters.dnstt;
inherit (lib)
escapeShellArgs
mkDefault
mkEnableOption
mkIf
mkOption
optionals
types
;
listenAddress = "${cfg.listenAddress}:${toString cfg.port}";
args = [
"-web.listen-address"
listenAddress
"-web.telemetry-path"
cfg.telemetryPath
"-dnstt.port"
(toString cfg.dnsttPort)
]
++ lib.concatMap (domain: [
"-dnstt.domain"
domain
]) cfg.domains
++ optionals (cfg.geoip.countryDatabase != null) [
"-geoip.country-database"
cfg.geoip.countryDatabase
]
++ optionals (cfg.geoip.asnDatabase != null) [
"-geoip.asn-database"
cfg.geoip.asnDatabase
]
++ cfg.extraFlags;
in
{
options.services.prometheus.exporters.dnstt = {
enable = mkEnableOption "the Prometheus DNSTT exporter";
package = mkOption {
type = types.package;
description = ''
Package providing the `dnstt_exporter` binary.
'';
};
domains = mkOption {
type = types.nonEmptyListOf types.str;
example = [ "tunnel.example.com" ];
description = ''
DNSTT tunnel domains to observe. The exporter matches DNS query names
below these domains and decodes DNSTT session IDs from the query prefix.
'';
};
dnsttPort = mkOption {
type = types.port;
default = 53;
description = ''
UDP port where DNSTT DNS traffic is observed.
'';
};
port = mkOption {
type = types.port;
default = 9713;
description = ''
Port to listen on for Prometheus metrics.
'';
};
listenAddress = mkOption {
type = types.str;
default = "0.0.0.0";
description = ''
Address to listen on for Prometheus metrics.
'';
};
telemetryPath = mkOption {
type = types.str;
default = "/metrics";
description = ''
HTTP path under which to expose Prometheus metrics.
'';
};
geoip = {
countryDatabase = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/GeoIP/GeoLite2-Country.mmdb";
description = ''
Optional MaxMind GeoIP2 or GeoLite2 Country database path. When set,
exported DNSTT metrics include a `country` label for the resolver
address seen by the server.
'';
};
asnDatabase = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/GeoIP/GeoLite2-ASN.mmdb";
description = ''
Optional MaxMind GeoIP2 or GeoLite2 ASN database path. When set,
exported DNSTT metrics include an `asn` label for the resolver address
seen by the server.
'';
};
};
extraFlags = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Extra command line flags to pass to `dnstt_exporter`.
'';
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = ''
Open the exporter metrics port in the firewall.
'';
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.domains != [ ];
message = "services.prometheus.exporters.dnstt.domains must contain at least one DNSTT domain.";
}
];
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
systemd.services.prometheus-dnstt-exporter = {
description = "Prometheus DNSTT exporter";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/dnstt_exporter ${escapeShellArgs args}";
Restart = "always";
DynamicUser = true;
AmbientCapabilities = [ "CAP_NET_RAW" ];
CapabilityBoundingSet = [ "CAP_NET_RAW" ];
NoNewPrivileges = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateDevices = mkDefault false;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_PACKET"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
UMask = "0077";
};
};
};
meta.maintainers = [ ];
meta.doc = false;
meta.buildDocsInSandbox = false;
}