diff options
| author | Tim Keller <tjk@tjkeller.xyz> | 2026-08-27 23:07:02 -0500 |
|---|---|---|
| committer | Tim Keller <tjk@tjkeller.xyz> | 2026-08-27 23:07:02 -0500 |
| commit | 89f2c328fc1b113a9e9b862841e0c7d0391b3d09 (patch) | |
| tree | 447bb5894afc1b7908ffac7cadb03796cc01a5e1 /README.md | |
| parent | ca193ad52b2ee2985af079be0159cc679c914f02 (diff) | |
| download | telemetry-flake-89f2c328fc1b113a9e9b862841e0c7d0391b3d09.tar.xz telemetry-flake-89f2c328fc1b113a9e9b862841e0c7d0391b3d09.zip | |
generate Grafana's secret key by default
Grafana's upstream default for security.secret_key is a constant compiled into
every release, which is why nixpkgs 26.05 stopped passing it through and started
asserting. Requiring secretKeyFile made that assertion the first thing anyone
hit, and forced a secret manager plus a container bindMount on a deployment
whose Grafana database holds no credentials at all -- both datasources are
provisioned without any.
generateSecretKey defaults to true (and to false as soon as secretKeyFile is
set), writing 32 random bytes to Grafana's data directory on first start. The
unit runs as the grafana user into a directory users.users.grafana.createHome
already owns, so there is no chown and nothing fights Grafana for the directory.
UMask=0077 makes the file 0600 on creation and an unconditional chmod repairs
one left readable by a previous version or a manual copy.
The tradeoff is stated in the option description rather than hidden: the key
lives beside the database it encrypts, so it defends against a stolen
grafana.db, not against filesystem access. secretKeyFile remains for when the
database holds a datasource password or a contact-point token.
Verified both paths build: with no secret wiring the generator unit is present
and LoadCredential points into the data directory; with secretKeyFile set the
generator is absent and LoadCredential points at the supplied path. Both emit
secret_key=$__file{...} into config.ini. The script is idempotent, leaves an
existing key untouched, and restores 0600 on a file chmodded to 0644.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JKczN1f66kmLxNBLX37voQ
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 34 |
1 files changed, 17 insertions, 17 deletions
@@ -76,14 +76,15 @@ nixosConfigurations.poweredge = nixpkgs.lib.nixosSystem { collectors.enable = true; server = { enable = true; - grafana.secretKeyFile = "/run/secrets/grafana-secret-key"; proxy.enable = true; # Grafana on :80 }; }; } ``` -Grafana at `http://localhost`. `collectorHosts` defaults to `[ "localhost" ]`. +Grafana at `http://localhost`. `collectorHosts` defaults to `[ "localhost" ]`, and +Grafana's secret key is generated on first start into `/var/lib/grafana/secret-key` +(mode 0600, owned by `grafana`) — see below if you would rather supply it yourself. ## Example: web services in a container @@ -91,7 +92,7 @@ The host runs the collectors; the container runs everything else. This is the in shape. ```nix -{ config, inputs, ... }: +{ inputs, ... }: { imports = [ inputs.telemetry.nixosModules.collectors ]; @@ -100,23 +101,12 @@ shape. logs.lokiUrl = "http://metrics:3100"; # the container, by name }; - sops.secrets.grafana-secret-key = { - sopsFile = ./secrets/grafana.yaml; - key = "secret_key"; - }; - containers.metrics = { autoStart = true; privateNetwork = true; hostBridge = "br-lan0"; localMacAddress = "02:00:00:00:00:06"; - # The module reads whatever path you point secretKeyFile at. - bindMounts."/run/grafana-secret-key" = { - hostPath = config.sops.secrets.grafana-secret-key.path; - isReadOnly = true; - }; - config = { ... }: { imports = [ inputs.telemetry.nixosModules.server ]; @@ -126,7 +116,6 @@ shape. services.telemetry.server = { enable = true; collectorHosts = [ "poweredge" ]; # the host above - grafana.secretKeyFile = "/run/grafana-secret-key"; proxy.enable = true; }; @@ -262,8 +251,19 @@ inventing placeholder values. render "No data". Don't configure the Grafana datasource by hand. - **The datasource UIDs `prometheus` and `loki` are fixed, not options.** The generated dashboards reference them literally. -- **Grafana's secret key is mandatory** as of nixpkgs 26.05. `openssl rand -base64 32`, - store it somewhere, point `grafana.secretKeyFile` at it. +- **Grafana's secret key is mandatory** as of nixpkgs 26.05, because Grafana's upstream + default is a constant compiled into every release. By default this module generates one + on first start into `/var/lib/grafana/secret-key` (0600, owned by `grafana`), so there + is nothing to wire up. Two things to know: it must stay *stable*, or credentials + encrypted in Grafana's database stop decrypting — so keep the Grafana data directory on + persistent storage; and it sits beside the database it protects, so it defends against + a stolen `grafana.db` but not against filesystem access. Set + `grafana.secretKeyFile` to a path you manage instead once the database holds anything + worth protecting, such as a datasource password: + ```nix + services.telemetry.server.grafana.secretKeyFile = + config.sops.secrets.grafana-secret-key.path; # turns generation off + ``` - **Changing `collectorHosts` changes the label set.** Panels look empty for a minute after first deploy while the recording rules re-evaluate; data from before the change stays under the old labels. |
