From 54b9b52bc0d92926d7889cdf9fda5f8bcc2dbbdc Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Sun, 21 Mar 2021 12:43:45 -0400 Subject: Add secret generation at startup with test --- flake.nix | 67 +++++++--------------------- modules/localsecrets/default.nix | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 51 deletions(-) create mode 100644 modules/localsecrets/default.nix 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)); + }; + }; +} -- cgit v1.2.3