parent
09a8a72b0c
commit
2b281286d0
4 changed files with 57 additions and 0 deletions
|
@ -68,6 +68,15 @@ with subtest("Check that key files have correct permissions"):
|
||||||
for path, permission in permissions.items():
|
for path, permission in permissions.items():
|
||||||
node.succeed(f"if [[ \"{permission}\" != \"$(stat -c '%a %U %G' '{path}')\" ]]; then ls -lah '{path}'; exit 1; fi")
|
node.succeed(f"if [[ \"{permission}\" != \"$(stat -c '%a %U %G' '{path}')\" ]]; then ls -lah '{path}'; exit 1; fi")
|
||||||
|
|
||||||
|
with subtest("Check that key services respond to key file changes"):
|
||||||
|
alpha.require_unit_state("key-text-key.service", "active")
|
||||||
|
|
||||||
|
alpha.succeed("rm /run/keys/key-text")
|
||||||
|
alpha.wait_until_succeeds("systemctl --no-pager show key-text-key.service | grep ActiveState=inactive", timeout=10)
|
||||||
|
|
||||||
|
alpha.succeed("touch /run/keys/key-text")
|
||||||
|
alpha.wait_for_unit("key-text-key.service")
|
||||||
|
|
||||||
with subtest("Check that we can correctly deploy to remaining nodes despite failures"):
|
with subtest("Check that we can correctly deploy to remaining nodes despite failures"):
|
||||||
beta.systemctl("stop sshd")
|
beta.systemctl("stop sshd")
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ let
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
git # for git flake tests
|
git # for git flake tests
|
||||||
|
inotifyTools # for key services build
|
||||||
|
|
||||||
# HACK: copy stderr to both stdout and stderr
|
# HACK: copy stderr to both stdout and stderr
|
||||||
# (the test framework only captures stdout, and only stderr appears on screen during the build)
|
# (the test framework only captures stdout, and only stderr appears on screen during the build)
|
||||||
|
|
|
@ -28,3 +28,8 @@ For example, to deploy DNS-01 credentials for use with `security.acme`:
|
||||||
|
|
||||||
Take note that if you use the default path (`/run/keys`), the secret files are only stored in-memory and will not survive reboots.
|
Take note that if you use the default path (`/run/keys`), the secret files are only stored in-memory and will not survive reboots.
|
||||||
To upload your secrets without performing a full deployment, use `colmena upload-keys`.
|
To upload your secrets without performing a full deployment, use `colmena upload-keys`.
|
||||||
|
|
||||||
|
## Key Services
|
||||||
|
|
||||||
|
For each secret file deployed using `deployment.keys`, a systemd service with the name of `${name}-key.service` is created (`acme-credentials.secret-key.service` for the example above).
|
||||||
|
This unit is only active when the corresponding file is present, allowing you to set up dependencies for services requiring secret files to function.
|
||||||
|
|
|
@ -467,6 +467,47 @@ let
|
||||||
in {
|
in {
|
||||||
system.activationScripts.colmena-chown-keys = lib.mkIf (length commands != 0) script;
|
system.activationScripts.colmena-chown-keys = lib.mkIf (length commands != 0) script;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Create "${name}-key" services for NixOps compatibility
|
||||||
|
#
|
||||||
|
# This is built as part of the system profile.
|
||||||
|
# We must be careful not to access `text` / `keyCommand` / `keyFile` here
|
||||||
|
#
|
||||||
|
# Sadly, path units don't automatically deactivate the bound units when
|
||||||
|
# the key files are deleted, so we use inotifywait in the services' scripts.
|
||||||
|
#
|
||||||
|
# <https://github.com/systemd/systemd/issues/3642>
|
||||||
|
keyServiceModule = { pkgs, lib, config, ... }: {
|
||||||
|
systemd.paths = lib.mapAttrs' (name: val: {
|
||||||
|
name = "${name}-key";
|
||||||
|
value = {
|
||||||
|
wantedBy = [ "paths.target" ];
|
||||||
|
pathConfig = {
|
||||||
|
PathExists = val.path;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) config.deployment.keys;
|
||||||
|
|
||||||
|
systemd.services = lib.mapAttrs' (name: val: {
|
||||||
|
name = "${name}-key";
|
||||||
|
value = {
|
||||||
|
bindsTo = [ "${name}-key.path" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Restart = "on-failure";
|
||||||
|
};
|
||||||
|
path = [ pkgs.inotifyTools ];
|
||||||
|
script = ''
|
||||||
|
if [[ ! -e "${val.path}" ]]; then
|
||||||
|
>&2 echo "${val.path} does not exist"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
inotifywait -qq -e delete_self "${val.path}"
|
||||||
|
>&2 echo "${val.path} disappeared"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}) config.deployment.keys;
|
||||||
|
};
|
||||||
in evalConfig {
|
in evalConfig {
|
||||||
inherit (npkgs) system;
|
inherit (npkgs) system;
|
||||||
|
|
||||||
|
@ -474,6 +515,7 @@ let
|
||||||
assertionModule
|
assertionModule
|
||||||
nixpkgsModule
|
nixpkgsModule
|
||||||
keyChownModule
|
keyChownModule
|
||||||
|
keyServiceModule
|
||||||
deploymentOptions
|
deploymentOptions
|
||||||
hive.defaults
|
hive.defaults
|
||||||
] ++ configs;
|
] ++ configs;
|
||||||
|
|
Loading…
Reference in a new issue