summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--consumers/users.nix11
-rw-r--r--contracts/secrets.nix99
-rw-r--r--default.nix59
-rw-r--r--modules/userSecret.nix20
-rw-r--r--providers/asecret.nix127
5 files changed, 38 insertions, 278 deletions
diff --git a/consumers/users.nix b/consumers/users.nix
deleted file mode 100644
index 9e4f401..0000000
--- a/consumers/users.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-{ lib, config, ... }:
-{
- options = {
- userPasswords.secrets.consumer = lib.mkOption {
- type = config.contracts.secrets.provider;
- };
- };
- config = {
- users.users.root.passwordFile = config.userPasswords.secrets.consumer.output.path;
- };
-}
diff --git a/contracts/secrets.nix b/contracts/secrets.nix
deleted file mode 100644
index 89b6466..0000000
--- a/contracts/secrets.nix
+++ /dev/null
@@ -1,99 +0,0 @@
-{ lib, ... }:
-{
- contracts.secrets = {
- meta = {
- maintainer = [ "cindi" ];
- description = "provide 'secrets'";
- };
-
- input = {
- options.type = lib.mkOption {
- type = lib.types.str;
- default = "random-password";
- };
- };
-
- output = {
- options.path = lib.mkOption {
- type = lib.types.path;
- description = ''
- Path to the file containing the secret generated out of band.
- This path will exist after deploying to a target host,
- it is not available through the nix store.
- '';
- };
- };
-
- behaviorTest = { providerRoot, extraModules ? [] }: {
- nodes.machine = { config, ... }: {
- imports = extraModules;
-
- options.test = {
- owner = lib.mkOption {
- type = lib.types.str;
- default = "root";
- };
-
- group = lib.mkOption {
- type = lib.types.str;
- default = "root";
- };
-
- mode = lib.mkOption {
- type = lib.types.str;
- default = "0400";
- };
-
- content = lib.mkOption {
- type = lib.types.str;
- default = "a super secret secret!";
- };
- };
-
- config = lib.mkMerge [
- (lib.setAttrByPath providerRoot {
- # We set consumer.input and not input directly because the latter is readOnly.
- consumer.input = {
- inherit (config.test) owner group mode;
- };
- })
- (lib.mkIf (config.test.owner != "root") {
- users.users.${config.test.owner}.isNormalUser = true;
- })
- (lib.mkIf (config.test.group != "root") {
- users.groups.${config.test.group} = {};
- })
- ];
- };
-
- testScript = { nodes, ... }:
- let
- cfg = nodes.machine;
- inherit (lib.getAttrFromPath providerRoot nodes.machine) output;
- in
- ''
- owner = machine.succeed("stat -c '%U' ${output.path}").strip()
- print(f"Got owner {owner}")
- if owner != "${cfg.test.owner}":
- raise Exception(f"Owner should be '${cfg.test.owner}' but got '{owner}'")
-
- group = machine.succeed("stat -c '%G' ${output.path}").strip()
- print(f"Got group {group}")
- if group != "${cfg.test.group}":
- raise Exception(f"Group should be '${cfg.test.group}' but got '{group}'")
-
- mode = str(int(machine.succeed("stat -c '%a' ${output.path}").strip()))
- print(f"Got mode {mode}")
- wantedMode = str(int("${cfg.test.mode}"))
- if mode != wantedMode:
- raise Exception(f"Mode should be '{wantedMode}' but got '{mode}'")
-
- content = machine.succeed("cat ${output.path}").strip()
- print(f"Got content {content}")
- if content != "${cfg.test.content}":
- raise Exception(f"Content should be '${cfg.test.content}' but got '{content}'")
- '';
- };
- };
-
-}
diff --git a/default.nix b/default.nix
index 5fe0306..03a67f4 100644
--- a/default.nix
+++ b/default.nix
@@ -3,47 +3,24 @@
}:
with (import ./lib {});
eval {
- machines.bob = { self, config, ... }: {
- imports = [
- ./consumers/users.nix
- ./contracts/secrets.nix
- ./providers/asecret.nix
- ];
- networking.hostName = "bob";
- asecret.secrets.provider = config.userPasswords.secrets;
- userPasswords.secrets.consumer = config.asecret.secrets;
- };
+ machines.bob.imports = [
+ ({ self, config, ... }: {
+ imports = [
+ "${sources.nixpkgs}/nixos/modules/testing/hardcodedSecret.nix"
+ ./modules/userSecret.nix
+ ];
+ networking.hostName = "bob";
+
+ testing.hardcodedSecret.rootPassword = {
+ secret.consumer = config.users.users.root.passwordSecret;
+ content = "nixos";
+ };
+
+ users.users.root.passwordSecret.provider =
+ config.testing.hardcodedSecret.rootPassword.secret;
+ })
+ ];
machines.alice = {
networking.hostName = "alice";
};
-} //
-(let
- lib = pkgs.lib;
- config = {};
-in
-{
- test =
- let
- inherit ((import ./contracts/secrets.nix {
- inherit lib;
- }).contracts.secrets) behaviorTest;
- in
- pkgs.testers.runNixOSTest ({
- name = "contracts-filebackup-restic";
- meta.maintainers = [ lib.maintainers.ibizaman ];
- # I tried using the following line but it leads to infinite recursion.
- # Instead, I made a hacky import. pkgs.callPackage was also giving an
- # infinite recursion.
- #
- # } // config.contracts.secret.behaviorTest {
- #
- } // behaviorTest {
- providerRoot = [ "testing" "asecret" "mysecret" "secret" ];
- extraModules = [
- ./providers/asecret.nix
- ({ config, ... }: {
- testing.asecret.mysecret.content = config.test.content;
- })
- ];
- });
-})
+}
diff --git a/modules/userSecret.nix b/modules/userSecret.nix
new file mode 100644
index 0000000..af1e978
--- /dev/null
+++ b/modules/userSecret.nix
@@ -0,0 +1,20 @@
+# "secret" consumer
+{ config, lib, ... }:
+{
+ options.users.users = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.submodule {
+ options.passwordSecret = lib.mkOption {
+ type = lib.types.nullOr config.contracts.secret.consumer;
+ };
+ });
+ };
+ config = {
+ # TODO other users than root
+ users.users.root.passwordFile = lib.mkIf (config.users.users.root.passwordSecret != null) config.users.users.root.passwordSecret.output.path;
+ users.users.root.passwordSecret.input = lib.mkIf (config.users.users.root.passwordSecret != null) {
+ owner = "root";
+ group = "root";
+ mode = "0400";
+ };
+ };
+}
diff --git a/providers/asecret.nix b/providers/asecret.nix
deleted file mode 100644
index df7fe20..0000000
--- a/providers/asecret.nix
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, lib, pkgs, ... }:
-let
- cfg = config.asecret;
-
- inherit (lib) mapAttrs' mkOption nameValuePair;
- inherit (lib.types) attrsOf nullOr str submodule;
- inherit (pkgs) writeText;
-in
-{
- options.asecret = mkOption {
- default = {};
- description = ''
- A secret. These should only be used in tests.
- '';
-
- example = lib.literalExpression ''
- {
- mySecret = {
- secret.input = {
- user = "me";
- mode = "0400";
- restartUnits = [ "myservice.service" ];
- };
- settings.content = "My Secret";
- };
- }
- '';
- type = attrsOf (submodule (mod@{ name, options, ... }: {
- options = {
- mode = mkOption {
- description = ''
- Mode of the secret file.
- '';
- type = str;
- default = "0400";
- };
-
- owner = mkOption {
- description = ''
- Linux user owning the secret file.
- '';
- type = str;
- };
-
- group = mkOption {
- description = ''
- Linux group owning the secret file.
- '';
- type = str;
- default = options.user.default;
- defaultText = "user";
- };
-
- content = mkOption {
- type = nullOr str;
- description = ''
- Content of the secret as a string.
-
- This will be stored in the nix store and should only be used for testing or maybe in dev.
- '';
- default = null;
- };
-
- source = mkOption {
- type = nullOr str;
- description = ''
- Source of the content of the secret as a path in the nix store.
- '';
- default = null;
- };
-
- path = mkOption {
- type = str;
- description = ''
- Path where the secret should be located.
- '';
- default = "/run/hardcodedSecrets/hardcodedSecret_${name}";
- };
-
- secrets = mkOption {
- type = config.contracts.secrets.provider;
- };
- };
-
- config = {
- inherit (mod.config.secret.input) mode owner group;
- secret.output.path = mod.config.path;
- };
- }));
- };
-
- config = {
- system.activationScripts = mapAttrs' (n: cfg':
- let
- source = if cfg'.source != null
- then cfg'.source
- else writeText "hardcodedSecret_${n}_content" cfg'.content;
- in
- nameValuePair "hardcodedSecret_${n}" ''
- (
- set -e
- mkdir -p "$(dirname "${cfg'.path}")"
- touch "${cfg'.path}"
- chmod ${cfg'.mode} "${cfg'.path}"
- chown ${cfg'.owner}:${cfg'.group} "${cfg'.path}"
- cp ${source} "${cfg'.path}"
- ) || echo "Failed to create hardcoded secret at ${cfg'.path}"
- ''
- ) cfg;
- };
-
- # Without `meta.buildDocsInSandbox = false;`, I get:
- #
- # > error: attribute 'contracts' missing
- # > at /nix/store/2gd9yzcfpqqp00vskxlqq4ds48mpgdzv-nixos/modules/testing/hardcodedSecret.nix:81:18:
- # > 80| secret = mkOption {
- # > 81| type = config.contracts.secret.provider;
- # > | ^
- # > 82| };
- # > Cacheable portion of option doc build failed.
- # > Usually this means that an option attribute that ends up in documentation (eg `default` or `description`) depends on the restricted module arguments `config` or `pkgs`.
- # >
- # > Rebuild your configuration with `--show-trace` to find the offending location. Remove the references to restricted arguments (eg by escaping their antiquotations or adding a `defaultText`) or disable the sandboxed build for the failing module by setting `meta.buildDocsInSandbox = false`.
- #
- # With the line, I don't get the warning but still get the missing 'contracts' attribute error.
- meta.buildDocsInSandbox = false;
-}