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!).
This commit is contained in:
Maximilian Bosch 2022-11-08 19:37:45 +01:00
parent b300ec349c
commit 4ec456b725
No known key found for this signature in database
GPG Key ID: 9A6EEA275CA5BE0A

View File

@ -72,6 +72,17 @@ let
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;
@ -101,28 +112,6 @@ let
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;
@ -600,6 +589,7 @@ in {
description = lib.mdDoc "List of datasources to insert/update.";
default = [];
type = types.listOf grafanaTypes.datasourceConfig;
apply = map (flip builtins.removeAttrs [ "password" "basicAuthPassword" ]);
};
deleteDatasources = mkOption {
@ -858,7 +848,7 @@ in {
};
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;
@ -1165,31 +1155,44 @@ in {
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 ];