mirror of
https://github.com/Mic92/sops-nix.git
synced 2024-12-14 11:57:52 +00:00
make sops-install-secrets work with sysusers
This commit is contained in:
parent
2eb7c4ba3a
commit
695275c349
3 changed files with 100 additions and 47 deletions
|
@ -1,4 +1,4 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{ config, options, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sops;
|
||||
|
@ -12,6 +12,8 @@ let
|
|||
|
||||
regularSecrets = lib.filterAttrs (_: v: !v.neededForUsers) cfg.secrets;
|
||||
|
||||
sysusersEnabled = options.systemd ? sysusers && config.systemd.sysusers.enable;
|
||||
|
||||
withEnvironment = import ./with-environment.nix {
|
||||
inherit cfg lib;
|
||||
};
|
||||
|
@ -312,8 +314,22 @@ in {
|
|||
|
||||
sops.environment.SOPS_GPG_EXEC = lib.mkIf (cfg.gnupg.home != null) (lib.mkDefault "${pkgs.gnupg}/bin/gpg");
|
||||
|
||||
# When using sysusers we no longer be started as an activation script because those are started in initrd while sysusers is started later.
|
||||
systemd.services.sops-install-secrets = lib.mkIf (regularSecrets != { } && sysusersEnabled) {
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
after = [ "systemd-sysusers.service" ];
|
||||
environment = cfg.environment;
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = [ "${cfg.package}/bin/sops-install-secrets ${manifest}" ];
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
|
||||
system.activationScripts = {
|
||||
setupSecrets = lib.mkIf (regularSecrets != {}) (lib.stringAfter ([ "specialfs" "users" "groups" ] ++ lib.optional cfg.age.generateKey "generate-age-key") ''
|
||||
setupSecrets = lib.mkIf (regularSecrets != {} && !sysusersEnabled) (lib.stringAfter ([ "specialfs" "users" "groups" ] ++ lib.optional cfg.age.generateKey "generate-age-key") ''
|
||||
[ -e /run/current-system ] || echo setting up secrets...
|
||||
${withEnvironment "${sops-install-secrets}/bin/sops-install-secrets ${manifest}"}
|
||||
'' // lib.optionalAttrs (config.system ? dryActivationScript) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ lib, config, pkgs, ... }:
|
||||
{ lib, options, config, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sops;
|
||||
secretsForUsers = lib.filterAttrs (_: v: v.neededForUsers) cfg.secrets;
|
||||
|
@ -13,24 +13,42 @@ let
|
|||
secretsMountPoint = "/run/secrets-for-users.d";
|
||||
symlinkPath = "/run/secrets-for-users";
|
||||
};
|
||||
sysusersEnabled = options.systemd ? sysusers && config.systemd.sysusers.enable;
|
||||
in
|
||||
{
|
||||
system.activationScripts = lib.mkIf (secretsForUsers != {}) {
|
||||
setupSecretsForUsers = lib.mkIf (secretsForUsers != {}) (lib.stringAfter ([ "specialfs" ] ++ lib.optional cfg.age.generateKey "generate-age-key") ''
|
||||
[ -e /run/current-system ] || echo setting up secrets for users...
|
||||
${withEnvironment "${cfg.package}/bin/sops-install-secrets -ignore-passwd ${manifestForUsers}"}
|
||||
'' // lib.optionalAttrs (config.system ? dryActivationScript) {
|
||||
supportsDryActivation = true;
|
||||
});
|
||||
systemd.services.sops-install-secrets-for-users = lib.mkIf (secretsForUsers != { } && sysusersEnabled) {
|
||||
wantedBy = [ "systemd-sysusers.service" ];
|
||||
before = [ "systemd-sysusers.service" ];
|
||||
environment = cfg.environment;
|
||||
unitConfig.DefaultDependencies = "no";
|
||||
|
||||
users = lib.mkIf (secretsForUsers != {}) {
|
||||
deps = [ "setupSecretsForUsers" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = [ "${cfg.package}/bin/sops-install-secrets -ignore-passwd ${manifestForUsers}" ];
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
};
|
||||
|
||||
system.activationScripts = lib.mkIf (secretsForUsers != { } && !sysusersEnabled) {
|
||||
setupSecretsForUsers = lib.stringAfter ([ "specialfs" ] ++ lib.optional cfg.age.generateKey "generate-age-key") ''
|
||||
[ -e /run/current-system ] || echo setting up secrets for users...
|
||||
${withEnvironment "${cfg.package}/bin/sops-install-secrets -ignore-passwd ${manifestForUsers}"}
|
||||
'' // lib.optionalAttrs (config.system ? dryActivationScript) {
|
||||
supportsDryActivation = true;
|
||||
};
|
||||
|
||||
users.deps = [ "setupSecretsForUsers" ];
|
||||
};
|
||||
|
||||
assertions = [{
|
||||
assertion = (lib.filterAttrs (_: v: v.owner != "root" || v.group != "root") secretsForUsers) == {};
|
||||
assertion = (lib.filterAttrs (_: v: v.owner != "root" || v.group != "root") secretsForUsers) == { };
|
||||
message = "neededForUsers cannot be used for secrets that are not root-owned";
|
||||
} {
|
||||
assertion = secretsForUsers != { } && sysusersEnabled -> config.users.mutableUsers;
|
||||
message = ''
|
||||
systemd.sysusers.enable in combination with sops.secrets.<name>.neededForUsers can only work with config.users.mutableUsers enabled.
|
||||
See https://github.com/Mic92/sops-nix/issues/475
|
||||
'';
|
||||
}];
|
||||
|
||||
system.build.sops-nix-users-manifest = manifestForUsers;
|
||||
|
|
|
@ -1,5 +1,47 @@
|
|||
{ makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix>
|
||||
, pkgs ? (import <nixpkgs> { }) }: {
|
||||
, pkgs ? (import <nixpkgs> { }) }:
|
||||
let
|
||||
userPasswordTest = name: extraConfig: makeTest {
|
||||
inherit name;
|
||||
nodes.machine = { config, lib, ... }: {
|
||||
imports = [
|
||||
../../modules/sops
|
||||
extraConfig
|
||||
];
|
||||
sops = {
|
||||
age.keyFile = ./test-assets/age-keys.txt;
|
||||
defaultSopsFile = ./test-assets/secrets.yaml;
|
||||
secrets.test_key.neededForUsers = true;
|
||||
secrets."nested/test/file".owner = "example-user";
|
||||
};
|
||||
|
||||
users.users.example-user = {
|
||||
isNormalUser = true;
|
||||
hashedPasswordFile = config.sops.secrets.test_key.path;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
machine.succeed("getent shadow example-user | grep -q :test_value:") # password was set
|
||||
machine.succeed("cat /run/secrets/nested/test/file | grep -q 'another value'") # regular secrets work...
|
||||
user = machine.succeed("stat -c%U /run/secrets/nested/test/file").strip() # ...and are owned...
|
||||
assert user == "example-user", f"Expected 'example-user', got '{user}'"
|
||||
machine.succeed("cat /run/secrets-for-users/test_key | grep -q 'test_value'") # the user password still exists
|
||||
|
||||
# BUG in nixos's overlayfs... systemd crashes on switch-to-configuration test
|
||||
'' + pkgs.lib.optionalString (!(extraConfig ? system.etc.overlay.enable)) ''
|
||||
machine.succeed("/run/current-system/bin/switch-to-configuration test")
|
||||
machine.succeed("cat /run/secrets/nested/test/file | grep -q 'another value'") # the regular secrets still work after a switch
|
||||
machine.succeed("cat /run/secrets-for-users/test_key | grep -q 'test_value'") # the user password is still present after a switch
|
||||
'';
|
||||
} {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) system;
|
||||
};
|
||||
in {
|
||||
ssh-keys = makeTest {
|
||||
name = "sops-ssh-keys";
|
||||
nodes.server = { ... }: {
|
||||
|
@ -23,39 +65,6 @@
|
|||
inherit (pkgs) system;
|
||||
};
|
||||
|
||||
user-passwords = makeTest {
|
||||
name = "sops-user-passwords";
|
||||
nodes.machine = { config, lib, ... }: {
|
||||
imports = [ ../../modules/sops ];
|
||||
sops = {
|
||||
age.keyFile = ./test-assets/age-keys.txt;
|
||||
defaultSopsFile = ./test-assets/secrets.yaml;
|
||||
secrets.test_key.neededForUsers = true;
|
||||
secrets."nested/test/file".owner = "example-user";
|
||||
};
|
||||
|
||||
users.users.example-user = {
|
||||
isNormalUser = true;
|
||||
hashedPasswordFile = config.sops.secrets.test_key.path;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
machine.succeed("getent shadow example-user | grep -q :test_value:") # password was set
|
||||
machine.succeed("cat /run/secrets/nested/test/file | grep -q 'another value'") # regular secrets work...
|
||||
machine.succeed("[ $(stat -c%U /run/secrets/nested/test/file) = example-user ]") # ...and are owned...
|
||||
machine.succeed("cat /run/secrets-for-users/test_key | grep -q 'test_value'") # the user password still exists
|
||||
|
||||
machine.succeed("/run/current-system/bin/switch-to-configuration test")
|
||||
machine.succeed("cat /run/secrets/nested/test/file | grep -q 'another value'") # the regular secrets still work after a switch
|
||||
machine.succeed("cat /run/secrets-for-users/test_key | grep -q 'test_value'") # the user password is still present after a switch
|
||||
'';
|
||||
} {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) system;
|
||||
};
|
||||
|
||||
pruning = makeTest {
|
||||
name = "sops-pruning";
|
||||
nodes.machine = { lib, ... }: {
|
||||
|
@ -370,4 +379,14 @@
|
|||
inherit pkgs;
|
||||
inherit (pkgs) system;
|
||||
};
|
||||
|
||||
user-passwords = userPasswordTest "sops-user-passwords" {};
|
||||
} // pkgs.lib.optionalAttrs (pkgs.lib.versionAtLeast (pkgs.lib.versions.majorMinor pkgs.lib.version) "24.05") {
|
||||
user-passwords-sysusers = userPasswordTest "sops-user-passwords-sysusers" {
|
||||
systemd.sysusers.enable = true;
|
||||
users.mutableUsers = true;
|
||||
system.etc.overlay.enable = true;
|
||||
boot.initrd.systemd.enable = true;
|
||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue