nixos/grafana: fix secret-related warnings

Closes #198646

* The options `password`/`basicAuthPassword` were removed for
datasources in Grafana 9. The only option to declare them now is to use
`secureJsonData`.
* Fix description for contactPoints provisioning: when using file/env
providers, nothing will be leaked into the store.
* Fix regex in file-provider usage check: it's also possible to either
use `$__env{FOO}` or `$FOO` to fetch secrets from the environment.
* Fix warning for datasources: `password`/`basicAuthPassword` was
removed, also check for each setting in `secureJsonData` if
env/file-provider was used (then no warning is needed!).

Changed files
+45 -42
nixos
modules
services
monitoring
+45 -42
nixos/modules/services/monitoring/grafana.nix
···
grafanaTypes.datasourceConfig = types.submodule {
freeformType = provisioningSettingsFormat.type;
+
imports = [
+
(mkRemovedOptionModule [ "password" ] ''
+
`services.grafana.provision.datasources.settings.datasources.<name>.password` has been removed
+
in Grafana 9. Use `secureJsonData` instead.
+
'')
+
(mkRemovedOptionModule [ "basicAuthPassword" ] ''
+
`services.grafana.provision.datasources.settings.datasources.<name>.basicAuthPassword` has been removed
+
in Grafana 9. Use `secureJsonData` instead.
+
'')
+
];
+
options = {
name = mkOption {
type = types.str;
···
default = false;
description = lib.mdDoc "Allow users to edit datasources from the UI.";
};
-
password = mkOption {
-
type = types.nullOr types.str;
-
default = null;
-
description = lib.mdDoc ''
-
Database password, if used. Please note that the contents of this option
-
will end up in a world-readable Nix store. Use the file provider
-
pointing at a reasonably secured file in the local filesystem
-
to work around that. Look at the documentation for details:
-
<https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
-
'';
-
};
-
basicAuthPassword = mkOption {
-
type = types.nullOr types.str;
-
default = null;
-
description = lib.mdDoc ''
-
Basic auth password. Please note that the contents of this option
-
will end up in a world-readable Nix store. Use the file provider
-
pointing at a reasonably secured file in the local filesystem
-
to work around that. Look at the documentation for details:
-
<https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
-
'';
-
};
secureJsonData = mkOption {
type = types.nullOr types.attrs;
default = null;
···
description = lib.mdDoc "List of datasources to insert/update.";
default = [];
type = types.listOf grafanaTypes.datasourceConfig;
+
apply = map (flip builtins.removeAttrs [ "password" "basicAuthPassword" ]);
};
deleteDatasources = mkOption {
···
};
contactPoints = mkOption {
-
description = lib.mdDoc "List of contact points to import or update. Please note that sensitive data will end up in world-readable Nix store.";
+
description = lib.mdDoc "List of contact points to import or update.";
default = [];
type = types.listOf (types.submodule {
freeformType = provisioningSettingsFormat.type;
···
config = mkIf cfg.enable {
warnings = let
-
usesFileProvider = opt: defaultValue: builtins.match "^${defaultValue}$|^\\$__file\\{.*}$" opt != null;
+
doesntUseFileProvider = opt: defaultValue:
+
let
+
regex = "${optionalString (defaultValue != null) "^${defaultValue}$|"}^\\$__(file|env)\\{.*}$|^\\$[^_\\$][^ ]+$";
+
in builtins.match regex opt == null;
in
+
# Ensure that no custom credentials are leaked into the Nix store. Unless the default value
+
# is specified, this can be achieved by using the file/env provider:
+
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
(optional (
-
! usesFileProvider cfg.settings.database.password "" ||
-
! usesFileProvider cfg.settings.security.admin_password "admin"
-
) "Grafana passwords will be stored as plaintext in the Nix store! Use file provider instead.")
+
doesntUseFileProvider cfg.settings.database.password "" ||
+
doesntUseFileProvider cfg.settings.security.admin_password "admin"
+
) ''
+
Grafana passwords will be stored as plaintext in the Nix store!
+
Use file/env provider or an env-var instead.
+
'')
+
# Warn about deprecated notifiers.
+
++ (optional (cfg.provision.notifiers != []) ''
+
Notifiers are deprecated upstream and will be removed in Grafana 10.
+
Use `services.grafana.provision.alerting.contactPoints` instead.
+
'')
+
# Ensure that `secureJsonData` of datasources provisioned via `datasources.settings`
+
# only uses file/env providers.
++ (optional (
let
-
checkOpts = opt: any (x: x.password != null || x.basicAuthPassword != null || x.secureJsonData != null) opt;
-
datasourcesUsed = optionals (cfg.provision.datasources.settings != null) cfg.provision.datasources.settings.datasources;
-
in checkOpts datasourcesUsed
-
) ''
-
Datasource passwords will be stored as plaintext in the Nix store!
-
It is not possible to use file provider in provisioning; please provision
-
datasources via `services.grafana.provision.datasources.path` instead.
-
'')
+
datasourcesToCheck = optionals
+
(cfg.provision.datasources.settings != null)
+
cfg.provision.datasources.settings.datasources;
+
declarationUnsafe = { secureJsonData, ... }:
+
secureJsonData != null
+
&& any (flip doesntUseFileProvider null) (attrValues secureJsonData);
+
in any declarationUnsafe datasourcesToCheck
+
) ''
+
Declarations in the `secureJsonData`-block of a datasource will be leaked to the
+
Nix store unless a file/env-provider or an env-var is used!
+
'')
++ (optional (
any (x: x.secure_settings != null) cfg.provision.notifiers
-
) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.")
-
++ (optional (
-
cfg.provision.notifiers != []
-
) ''
-
Notifiers are deprecated upstream and will be removed in Grafana 10.
-
Use `services.grafana.provision.alerting.contactPoints` instead.
-
'');
+
) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.");
environment.systemPackages = [ cfg.package ];