diff --git a/README.md b/README.md index 72c2f1f..d10a25a 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,12 @@ key management APIs such as AWS KMS, GCP KMS, Azure Key Vault or Hashicorp's vau - Compatible with all NixOS deployment frameworks: [NixOps](https://github.com/NixOS/nixops), nixos-rebuild, [krops](https://github.com/krebs/krops/), [morph](https://github.com/DBCDK/morph), [nixus](https://github.com/Infinisil/nixus) - Version-control friendly: Since all files are encrypted they can directly committed to version control. The format is readable in diffs and there are also ways of showing [git diffs in cleartext](https://github.com/mozilla/sops#showing-diffs-in-cleartext-in-git) -- CI friendly: Since nixops files can be added to the nix store as well without leaking secrets, machine definition can be build as a whole. +- CI friendly: Since sops files can be added to the nix store as well without leaking secrets, machine definition can be build as a whole. - Atomic upgrades: New secrets are written to a new directory which replaces the old directory in an atomic step. - Rollback support: If sops files are added to Nix store, old secrets can be rolled back. This is optional. - Fast: Unlike solutions implemented by NixOps, krops and morph there is no extra step required to upload secrets - Different storage formats: Secrets can be stored in Yaml, JSON or binary. +- Minimize configuration errors: sops files are checked against the configuration at evluation time. ## Usage example diff --git a/modules/sops/default.nix b/modules/sops/default.nix index f972866..b339f19 100644 --- a/modules/sops/default.nix +++ b/modules/sops/default.nix @@ -80,6 +80,13 @@ let symlinkPath = "/run/secrets"; inherit (cfg) gnupgHome sshKeyPaths; }); + + checkedManifest = pkgs.runCommandNoCC "checked-manifest.json" { + nativeBuildInputs = [ sops-install-secrets ]; + } '' + sops-install-secrets -check-mode=${if cfg.validateSopsFiles then "sopsfile" else "manifest"} ${manifest} + cp ${manifest} $out + ''; in { options.sops = { secrets = mkOption { @@ -97,6 +104,15 @@ in { ''; }; + validateSopsFiles = mkOption { + type = types.bool; + default = true; + description = '' + Check all sops files at evaluation time. + This requires sops files to be added to the nix store. + ''; + }; + gnupgHome = mkOption { type = types.nullOr types.str; default = null; @@ -118,18 +134,22 @@ in { }; }; config = mkIf (cfg.secrets != {}) { - assertions = [{ assertion = cfg.gnupgHome != null -> cfg.sshKeyPaths == []; - message = "config.sops.gnupgHome and config.sops.sshKeyPaths are mutual exclusive"; + message = "Configuration options sops.gnupgHome and sops.sshKeyPaths cannot be set both at the same time"; } { assertion = cfg.gnupgHome == null -> cfg.sshKeyPaths != []; - message = "Either config.sops.sshKeyPaths and config.sops.gnupgHome must be set"; - }]; + message = "Either sops.sshKeyPaths and sops.gnupgHome must be set"; + }] ++ map (name: let + inherit (cfg.secrets.${name}) sopsFile; + in { + assertion = cfg.validateSopsFiles -> builtins.isPath sopsFile; + message = "${sopsFile} is not in the nix store. Either add it to the nix store or set `sops.validateSopsFiles` to false"; + }) (builtins.attrNames cfg.secrets); system.activationScripts.setup-secrets = stringAfter [ "users" "groups" ] '' echo setting up secrets... - ${optionalString (cfg.gnupgHome != null) "SOPS_GPG_EXEC=${pkgs.gnupg}/bin/gpg"} ${sops-install-secrets}/bin/sops-install-secrets ${manifest} + ${optionalString (cfg.gnupgHome != null) "SOPS_GPG_EXEC=${pkgs.gnupg}/bin/gpg"} ${sops-install-secrets}/bin/sops-install-secrets ${checkedManifest} ''; }; }