diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md index 16ce699c53f2..bb21bd20daf7 100644 --- a/nixos/doc/manual/release-notes/rl-2511.section.md +++ b/nixos/doc/manual/release-notes/rl-2511.section.md @@ -144,6 +144,8 @@ - [conman](https://github.com/dun/conman), a serial console management program. Available as [services.conman](#opt-services.conman.enable). +- [Prometheus Tailscale Exporter](https://github.com/adinhodovic/tailscale-exporter), a Prometheus exporter for Tailscale Tailnet metrics. + - [KMinion](https://github.com/redpanda-data/kminion), feature-rich Prometheus exporter for Apache Kafka. Available as [services.prometheus.exporters.kafka](options.html#opt-services.prometheus.exporters.kafka). - [Spoolman](https://github.com/Donkie/Spoolman), a inventory management system for Filament spools. Available as [services.spoolman](#opt-services.spoolman.enable). diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix index a4fa24bdcd5a..c387bf06c8f6 100644 --- a/nixos/modules/services/monitoring/prometheus/exporters.nix +++ b/nixos/modules/services/monitoring/prometheus/exporters.nix @@ -127,6 +127,7 @@ let "storagebox" "surfboard" "systemd" + "tailscale" "tibber" "unbound" "unpoller" diff --git a/nixos/modules/services/monitoring/prometheus/exporters/tailscale.nix b/nixos/modules/services/monitoring/prometheus/exporters/tailscale.nix new file mode 100644 index 000000000000..9743d108a56c --- /dev/null +++ b/nixos/modules/services/monitoring/prometheus/exporters/tailscale.nix @@ -0,0 +1,47 @@ +{ + config, + lib, + pkgs, + utils, + ... +}: + +let + inherit (lib) + getExe + mkOption + mkPackageOption + types + ; + + inherit (utils) escapeSystemdExecArgs; + + cfg = config.services.prometheus.exporters.tailscale; +in +{ + port = 9250; + extraOpts = with types; { + package = mkPackageOption pkgs "prometheus-tailscale-exporter" { }; + environmentFile = mkOption { + type = path; + description = '' + Environment file containg at least the TAILSCALE_TAILNET, + TAILSCALE_OAUTH_CLIENT_ID, and TAILSCALE_OAUTH_CLIENT_SECRET + environment variables. + ''; + }; + }; + serviceOpts = { + serviceConfig = { + EnvironmentFile = cfg.environmentFile; + ExecStart = escapeSystemdExecArgs ( + [ + (getExe cfg.package) + "--listen-address" + "${cfg.listenAddress}:${toString cfg.port}" + ] + ++ cfg.extraFlags + ); + }; + }; +} diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix index c4e60a72416a..55601b3b05a8 100644 --- a/nixos/tests/prometheus-exporters.nix +++ b/nixos/tests/prometheus-exporters.nix @@ -1760,6 +1760,51 @@ let ''; }; + tailscale = { + exporterConfig = { + package = pkgs.prometheus-tailscale-exporter.overrideAttrs { + patches = [ + # This patch prevents the exporter from exiting immediately upon + # startup when no credentials are provided, which is useful for + # testing the NixOS module. + (pkgs.writeText "allow-running-without-credentials" '' + diff --git a/cmd/tailscale-exporter/root.go b/cmd/tailscale-exporter/root.go + index 2ff11cb..2fb576f 100644 + --- a/cmd/tailscale-exporter/root.go + +++ b/cmd/tailscale-exporter/root.go + @@ -137,14 +137,6 @@ func runExporter(cmd *cobra.Command, args []string) error { + ''\t// Create HTTP client that automatically handles token refresh + ''\thttpClient := oauthConfig.Client(context.Background()) + + -''\t// Test OAuth token generation + -''\ttoken, err := oauthConfig.Token(context.Background()) + -''\tif err != nil { + -''\t''\treturn fmt.Errorf("failed to obtain OAuth token: %w", err) + -''\t} + -''\tlogger.Info("OAuth token obtained", "token_type", token.TokenType) + -''\tlogger.Info("Successfully obtained OAuth token", "expires", token.Expiry) + - + ''\t// Default labels for all metrics + ''\tdefaultLabels := prometheus.Labels{"tailnet": tailnet} + ''\treg := prometheus.WrapRegistererWith( + '') + ]; + }; + enable = true; + environmentFile = pkgs.writeText "tailscale-exporter-env" '' + TAILSCALE_OAUTH_CLIENT_ID=12345678 + TAILSCALE_OAUTH_CLIENT_SECRET=12345678 + TAILSCALE_TAILNET=example.com + ''; + }; + + exporterTest = '' + wait_for_unit("prometheus-tailscale-exporter.service") + wait_for_open_port(9250) + succeed("curl -sSf localhost:9250/metrics | grep 'tailscale_up{tailnet=\"example.com\"} 1'") + ''; + }; + unpoller = { nodeName = "unpoller"; exporterConfig.enable = true;