fix(#3370): give Grafana a secret_key, generated in-container on first boot
nixpkgs dropped the default for `services.grafana.settings.security.secret_key`
and asserts on null, so the module I merged an hour ago fails the build on any
host that enables it. That is a broken deploy, not a warning.
Generated in-container like authelia's own keys and for the same reason:
nothing outside the container ever reads it. Generated ONCE and kept — the
key signs Grafana's stored datasource secrets, and rotating it does not
re-encrypt what it already encrypted, so a fresh key per boot would leave
Grafana unable to read its own database. Delivered as `$__file{}`, so it
never enters the store.
The gate missed this because a container is a separate NixOS evaluation with
its own assertions: forcing the host's `config.assertions` never touched the
container's. `state/eval-3265.sh` now forces
`containers.swarm-grafana.config.assertions` and reports any that fail.
This commit is contained in:
parent
405ed85550
commit
859833b186
1 changed files with 56 additions and 0 deletions
|
|
@ -41,6 +41,11 @@ let
|
|||
# intermittent one.
|
||||
secretPath = "/var/lib/grafana-oidc/${cfg.oidc.clientId}.secret";
|
||||
|
||||
# Grafana's own datasource-encryption key. Generated in-container (see the
|
||||
# unit below) because nothing outside the container ever reads it — unlike
|
||||
# the OIDC secret above, whose other reader is authelia's container.
|
||||
secretKeyPath = "/var/lib/grafana-secret/secret_key";
|
||||
|
||||
# Format-locked by Grafana: the generic OAuth callback is always
|
||||
# `<root_url>/login/generic_oauth`. Declared once here and read by both
|
||||
# the authelia client and Grafana itself.
|
||||
|
|
@ -344,6 +349,52 @@ in
|
|||
|
||||
systemd.services.grafana.environment.SSL_CERT_FILE = lib.mkIf useSelfSigned grafanaCaBundle;
|
||||
|
||||
# Grafana's `secret_key` has **no default in nixpkgs** and an
|
||||
# assertion refuses the build without one — which is how the first
|
||||
# version of this module broke a deploy. It signs the datasource
|
||||
# secrets in Grafana's own database.
|
||||
#
|
||||
# Generated in-container on first boot, like authelia's own keys and
|
||||
# for the same reason: nothing outside this container ever reads it,
|
||||
# which makes in-container generation right rather than merely
|
||||
# easier. (The OIDC client secret is delivered host-side precisely
|
||||
# because it has a second reader.)
|
||||
#
|
||||
# ⚠️ Generated ONCE and kept — the `-s` guard is load-bearing.
|
||||
# Rotating this key does not re-encrypt what it already encrypted, so
|
||||
# a fresh key on every boot would leave Grafana unable to decrypt its
|
||||
# own stored datasource secrets. Under /var/lib, never /run.
|
||||
systemd.services.swarm-grafana-secret-key = {
|
||||
description = "generate Grafana's datasource encryption key on first boot";
|
||||
wantedBy = [ "grafana.service" ];
|
||||
before = [ "grafana.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
# Same User/Group/StateDirectory as grafana itself, so systemd
|
||||
# creates the directory owned by the account that has to read
|
||||
# the file — no chown, no mode juggling.
|
||||
User = "grafana";
|
||||
Group = "grafana";
|
||||
StateDirectory = "grafana-secret";
|
||||
StateDirectoryMode = "0700";
|
||||
SyslogIdentifier = "swarm-grafana-secret-key";
|
||||
};
|
||||
path = [
|
||||
pkgs.openssl
|
||||
pkgs.coreutils
|
||||
];
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
key=${lib.escapeShellArg secretKeyPath}
|
||||
if [ ! -s "$key" ]; then
|
||||
umask 077
|
||||
openssl rand -hex 32 > "$key"
|
||||
echo "generated Grafana's datasource encryption key"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
services.grafana = {
|
||||
enable = true;
|
||||
package = cfg.package;
|
||||
|
|
@ -371,6 +422,11 @@ in
|
|||
|
||||
users.auto_assign_org_role = cfg.oidc.role;
|
||||
|
||||
# ⚠️ `$__file{}`, and required rather than optional: nixpkgs
|
||||
# dropped the default and asserts on null, so without this the
|
||||
# whole host fails to build. The unit below generates it.
|
||||
security.secret_key = "$__file{${secretKeyPath}}";
|
||||
|
||||
# No local password path at all when SSO is configured. This
|
||||
# is not tidiness: Grafana ships an `admin`/`admin` account,
|
||||
# and this vhost is on the public gateway.
|
||||
|
|
|
|||
Loading…
Reference in a new issue