summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2021-03-21 12:43:45 -0400
committerKjetil Orbekk <kj@orbekk.com>2021-03-21 18:49:11 -0400
commit54b9b52bc0d92926d7889cdf9fda5f8bcc2dbbdc (patch)
treecb613c9989d8f926a6bcee95c6eef5bc940d798f
parentbd0bee6b5cbf1af5698c997b9d856c6a90b6e3d3 (diff)
Add secret generation at startup with test
-rw-r--r--flake.nix67
-rw-r--r--modules/localsecrets/default.nix96
2 files changed, 112 insertions, 51 deletions
diff --git a/flake.nix b/flake.nix
index d92dda5..51a4f78 100644
--- a/flake.nix
+++ b/flake.nix
@@ -8,54 +8,8 @@
supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
in {
- nixosModules.localsecrets =
- { config, pkgs, lib, ... }:
- let
- types = lib.types;
- cfg = config.localsecrets;
- keyOpts = {name, ...}: {
- options = {
- generate = lib.mkOption {
- type = types.str;
- };
- generatePublic = lib.mkOption {
- type = types.nullOr types.str;
- };
-
- path = lib.mkOption {
- type = types.str;
- default = "/var/lib/localsecrets/${name}";
- };
-
- publicPath = lib.mkOption {
- type = types.str;
- default = ./. + "/public_keys/${name}";
- };
- };
- };
- in
- {
- options = {
- localsecrets = {
- enable = lib.mkEnableOption "Deploy localsecrets";
- secrets = lib.mkOption {
- type = types.attrsOf (types.submodule keyOpts);
- default = [];
- };
- };
- };
- config = {
- environment.systemPackages = [
- (
- pkgs.writeScriptBin "localsecrets" ''
- echo "Secrets I know about:"
- ${lib.concatMapStrings (secret: "echo ${secret.path}\n") (lib.attrValues cfg.secrets)}
- ''
- )
- ];
- };
- };
+ nixosModules.localsecrets = import ./modules/localsecrets;
checks = forAllSystems (system: {
test =
@@ -65,18 +19,29 @@
};
in
testing.makeTest {
- nodes.client = { ... }: {
+ nodes.client = { pkgs, lib, ... }: {
imports = [ self.nixosModules.localsecrets ];
localsecrets.enable = true;
localsecrets.secrets.test-secret = {
- generate = "echo hello";
- generatePublic = "echo world";
+ generator = pkgs.writeScript "test-secret.sh" ''
+ #! ${pkgs.bash}/bin/bash
+ PATH=${lib.makeBinPath (with pkgs; [ coreutils ])}
+ mkdir -p private public
+ echo hello > private/key.private
+ echo world > public/key.public
+ '';
};
};
testScript = ''
start_all()
- client.succeed("localsecrets")
+ client.wait_for_unit("multi-user.target")
+ private = "/var/lib/localsecrets/private/test-secret/key.private"
+ public = "/var/lib/localsecrets/public/test-secret/key.public"
+ client.succeed("grep hello {}".format(private))
+ client.fail("sudo -u nobody cat {}".format(private))
+ client.succeed("grep world {}".format(public))
+ client.succeed("sudo -u nobody cat {}".format(public))
'';
};
});
diff --git a/modules/localsecrets/default.nix b/modules/localsecrets/default.nix
new file mode 100644
index 0000000..6665216
--- /dev/null
+++ b/modules/localsecrets/default.nix
@@ -0,0 +1,96 @@
+{ config, pkgs, lib, ... }:
+let
+ types = lib.types;
+ cfg = config.localsecrets;
+
+ keyOpts = {name, ...}: {
+ options = {
+ generator = lib.mkOption {
+ type = types.either types.str types.path;
+ default = "";
+ };
+
+ privateDir = lib.mkOption {
+ type = types.str;
+ default = "/var/lib/${cfg.stateDir}/private/${name}";
+ };
+
+ publicDir = lib.mkOption {
+ type = types.str;
+ default = "/var/lib/${cfg.stateDir}/public/${name}";
+ };
+
+ mode = lib.mkOption {
+ type = types.str;
+ default = "0700";
+ };
+
+ user = lib.mkOption {
+ type = types.str;
+ default = "root";
+ };
+
+ group = lib.mkOption {
+ type = types.str;
+ default = "root";
+ };
+ };
+ };
+in
+{
+ options = {
+ localsecrets = {
+ enable = lib.mkEnableOption "Deploy localsecrets";
+
+ stateDir = lib.mkOption {
+ type = types.str;
+ default = "localsecrets";
+ };
+
+ secrets = lib.mkOption {
+ type = types.attrsOf (types.submodule keyOpts);
+ default = [];
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.localsecrets-generate = {
+ description = "Generate local secrets";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+
+ serviceConfig = {
+ StateDirectory = cfg.stateDir;
+ };
+
+ script = ''
+ umask 0022
+ mkdir -p $STATE_DIRECTORY/private
+ mkdir -p $STATE_DIRECTORY/public
+ '' + (lib.concatStringsSep "\n" (lib.mapAttrsToList
+ (name: secret:
+ let
+ runtimeDirectory="secret-${name}";
+ in ''
+ systemd-run \
+ --wait \
+ -p DynamicUser=true \
+ -p RuntimeDirectory=${runtimeDirectory} \
+ -p RuntimeDirectoryMode=${secret.mode} \
+ -p RuntimeDirectoryPreserve=true \
+ -p WorkingDirectory=/run/${runtimeDirectory} \
+ -p PrivateTmp=true \
+ ${pkgs.bash}/bin/bash -c '${secret.generator}'
+
+ rm -rf "${secret.privateDir}" || true
+ chown "${secret.user}:${secret.group}" "/run/${runtimeDirectory}/private"
+ chmod "${secret.mode}" "/run/${runtimeDirectory}/private"
+ mv "/run/${runtimeDirectory}/private" "${secret.privateDir}"
+
+ rm -rf "${secret.publicDir}" || true
+ mv "/run/${runtimeDirectory}/public" "${secret.publicDir}"
+ '') cfg.secrets));
+ };
+ };
+}