get rid systemd patches

main
Jörg Thalheim 2 years ago
parent af90e52ec9
commit 97b656163a

@ -96,6 +96,14 @@ files: `systemd-vaultd.service`, `systemd-vaultd.socket`:
make install make install
``` ```
## Known limitations
systemd's LoadCredential option will not update credentials if a service is
reloaded. However systemd-vaultd called `systemd-vaultd-update-secrets` comes
with a helper program that can write secrets from the json file generated by
systemd-vaultd to a directory readable by the service. Checkout
`systemd-vaultd/nix/checks/systemd-vaultd-test.nix` for more details.
## License ## License
Copyright (c) 2022 [Jörg Thalheim](https://github.com/mic92) and contributors. Copyright (c) 2022 [Jörg Thalheim](https://github.com/mic92) and contributors.

@ -0,0 +1,77 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path"
"syscall"
"time"
)
const (
systemdVaultdir = "/run/systemd-vaultd/secrets"
)
func updateSecrets(credentialsDirectory, target string) error {
// get systemd service name from credentials directory
serviceName := path.Base(credentialsDirectory)
stat, err := os.Stat(credentialsDirectory)
if err != nil {
return fmt.Errorf("failed to stat %s: %w", credentialsDirectory, err)
}
// inherit the owner and group of the credentials directory
uid := stat.Sys().(*syscall.Stat_t).Uid
gid := stat.Sys().(*syscall.Stat_t).Gid
jsonPath := path.Join(credentialsDirectory, fmt.Sprintf("%s.json", serviceName))
var content []byte
for i := 0; i < 10; i++ {
content, err = os.ReadFile(jsonPath)
if err != nil {
if os.IsNotExist(err) {
// wait for the file to be created
fmt.Printf("waiting for %s to be created", jsonPath)
time.Sleep(1 * time.Second)
continue
}
return fmt.Errorf("failed to read vault json file %s: %w", serviceName, err)
}
break
}
var data map[string]interface{}
if err := json.Unmarshal(content, &data); err != nil {
return fmt.Errorf("failed to unmarshal json from %s: %w", jsonPath, err)
}
for key, value := range data {
targetPath := path.Join(target, key)
err := os.MkdirAll(path.Dir(targetPath), 0o700)
os.Chown(path.Dir(targetPath), int(uid), int(gid))
if err != nil {
return fmt.Errorf("failed to create directory %s: %w", path.Dir(targetPath), err)
}
os.WriteFile(targetPath, []byte(value.(string)), 0o400)
os.Chown(targetPath, int(uid), int(gid))
}
return nil
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: systemd-vaultd-update-secrets <target>")
os.Exit(1)
}
credentialsDirectory := os.Getenv("CREDENTIALS_DIRECTORY")
if credentialsDirectory == "" {
fmt.Println("CREDENTIALS_DIRECTORY environment variable must be set")
os.Exit(1)
}
target := os.Args[1]
if err := updateSecrets(credentialsDirectory, target); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

@ -22,7 +22,6 @@
, ... , ...
}: { }: {
packages.default = pkgs.callPackage ./default.nix { }; packages.default = pkgs.callPackage ./default.nix { };
packages.systemd = pkgs.callPackage ./nix/pkgs/systemd.nix { };
devShells.default = pkgs.mkShellNoCC { devShells.default = pkgs.mkShellNoCC {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
python3.pkgs.pytest python3.pkgs.pytest
@ -35,7 +34,6 @@
go go
just just
config.treefmt.build.wrapper config.treefmt.build.wrapper
config.packages.systemd
]; ];
}; };

@ -2,7 +2,6 @@
name = "systemd-vaultd"; name = "systemd-vaultd";
nodes.server = nodes.server =
{ config { config
, pkgs
, ... , ...
}: { }: {
imports = [ imports = [
@ -10,6 +9,9 @@
../modules/systemd-vaultd.nix ../modules/systemd-vaultd.nix
./dev-vault-server.nix ./dev-vault-server.nix
]; ];
# speed up tests
virtualisation.cores = 4;
virtualisation.memorySize = 1024;
systemd.services.service1 = { systemd.services.service1 = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -33,17 +35,33 @@
}; };
}; };
users.users.service2 = {
isSystemUser = true;
group = "service2";
uid = 1000;
};
users.groups.service2.gid = 1000;
systemd.services.service2 = { systemd.services.service2 = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
preStart = ''
cp -r $CREDENTIALS_DIRECTORY /run/service2/secrets
'';
script = '' script = ''
set -x set -x
while true; do while true; do
cat $CREDENTIALS_DIRECTORY/secret > /tmp/service2 cat /run/service2/secrets/secret >&2 || :
cat /run/service2/secrets/secret > /tmp/service2 || :
sleep 0.1 sleep 0.1
done done
''; '';
serviceConfig.ExecReload = "${pkgs.coreutils}/bin/true"; serviceConfig = {
serviceConfig.LoadCredential = [ "secret:/run/systemd-vaultd/sock" ]; ExecReload = "+${config.services.systemd-vaultd.package}/bin/systemd-vaultd-update-secrets /run/service2/secrets";
User = "service2";
Group = "service2";
LoadCredential = [ "secret:/run/systemd-vaultd/sock" ];
RuntimeDirectory = "service2";
};
vault = { vault = {
template = '' template = ''
{{ with secret "secret/blocking-secret" }}{{ scratch.MapSet "secrets" "secret" .Data.data.foo }}{{ end }} {{ with secret "secret/blocking-secret" }}{{ scratch.MapSet "secrets" "secret" .Data.data.foo }}{{ end }}
@ -77,32 +95,37 @@
machine.wait_for_open_port(8200) machine.wait_for_open_port(8200)
machine.wait_for_unit("setup-vault-agent-approle.service") machine.wait_for_unit("setup-vault-agent-approle.service")
out = machine.wait_until_succeeds("cat /tmp/service1") out = machine.wait_until_succeeds("grep -q bar /tmp/service1")
print(out)
assert out == "bar", f"{out} != bar for service1"
out = machine.succeed("cat /tmp/service1-env") out = machine.succeed("grep -q bar /tmp/service1-env")
print(out)
assert out == "bar", f"{out} != bar for service1-env"
out = machine.succeed("systemctl status service2") out = machine.succeed("systemctl status service2 || :")
print(out) print(out)
assert "(sd-mkdcreds)" in out, "service2 should be still blocked" assert "(sd-mkdcreds)" in out, "service2 should be still blocked"
machine.succeed("vault kv put secret/blocking-secret foo=bar") machine.succeed("vault kv put secret/blocking-secret foo=bar")
out = machine.wait_until_succeeds("cat /tmp/service2") machine.wait_until_succeeds("grep -q bar /tmp/service2 >&2")
print(out)
assert out == "bar", f"{out} != bar for service2"
machine.succeed("vault kv put secret/blocking-secret foo=reload") machine.succeed("umount /run/credentials/service2.service")
machine.succeed("rm /run/systemd-vaultd/secrets/service2.service.json") machine.succeed("rm /run/systemd-vaultd/secrets/service2.service.json")
machine.succeed("vault kv put secret/blocking-secret foo=reload")
machine.succeed("systemctl restart vault-agent-default") machine.succeed("systemctl restart vault-agent-default")
machine.wait_until_succeeds("cat /run/systemd-vaultd/secrets/service2.service.json >&2") machine.wait_until_succeeds("cat /run/systemd-vaultd/secrets/service2.service.json >&2")
machine.succeed("systemctl reload service2") machine.succeed("systemctl restart service2")
machine.succeed("rm /tmp/service2") machine.succeed("rm /tmp/service2")
out = machine.wait_until_succeeds("cat /tmp/service2") machine.wait_until_succeeds("grep -q reload /tmp/service2 >&2")
print(out)
assert out == "reload", f"{out} != reload for service2" # get uid and gid
out = machine.succeed("stat -c %u /run/service2/secrets/secret").strip()
assert out == "1000", "service2 should have access to secret file with uid 1000, got " + out
out = machine.succeed("stat -c %g /run/service2/secrets/secret").strip()
assert out == "1000", "service2 should have access to secret file with gid 1000, got " + out
# get permissions in octal
out = machine.succeed("stat -c %a /run/service2/secrets/secret").strip()
assert out == "400", "service2 should have access to secret file with permissions 0400, got " + out
''; '';
} }

@ -3,11 +3,10 @@
, pkgs , pkgs
, lib , lib
, coreutils , coreutils
, , systemd
}: }:
let let
systemd-vaultd = pkgs.callPackage ../../default.nix { }; systemd-vaultd = pkgs.callPackage ../../default.nix { };
systemd = pkgs.callPackage ../pkgs/systemd.nix { };
in in
writeShellScript "unittests" '' writeShellScript "unittests" ''
set -eu -o pipefail set -eu -o pipefail

@ -1,4 +1,6 @@
{ pkgs { pkgs
, lib
, config
, ... , ...
}: }:
let let
@ -8,27 +10,39 @@ in
imports = [ imports = [
./vault-secrets.nix ./vault-secrets.nix
]; ];
options = {
services.systemd-vaultd = {
package = lib.mkOption {
type = lib.types.package;
default = systemd-vaultd;
defaultText = "pkgs.systemd-vaultd";
description = ''
The package to use for systemd-vaultd
'';
};
};
};
systemd.package = pkgs.callPackage ../pkgs/systemd.nix { }; config = {
systemd.sockets.systemd-vaultd = {
systemd.sockets.systemd-vaultd = { description = "systemd-vaultd socket";
description = "systemd-vaultd socket"; wantedBy = [ "sockets.target" ];
wantedBy = [ "sockets.target" ];
socketConfig = { socketConfig = {
ListenStream = "/run/systemd-vaultd/sock"; ListenStream = "/run/systemd-vaultd/sock";
SocketUser = "root"; SocketUser = "root";
SocketMode = "0600"; SocketMode = "0600";
};
}; };
}; systemd.services.systemd-vaultd = {
systemd.services.systemd-vaultd = { description = "systemd-vaultd daemon";
description = "systemd-vaultd daemon"; requires = [ "systemd-vaultd.socket" ];
requires = [ "systemd-vaultd.socket" ]; after = [ "systemd-vaultd.socket" ];
after = [ "systemd-vaultd.socket" ]; # Restarting can break services waiting for secrets
# Restarting can break services waiting for secrets stopIfChanged = false;
stopIfChanged = false; serviceConfig = {
serviceConfig = { ExecStart = "${config.services.systemd-vaultd.package}/bin/systemd-vaultd";
ExecStart = "${systemd-vaultd}/bin/systemd-vaultd"; };
}; };
}; };
} }

@ -1,14 +0,0 @@
{ systemd
, fetchpatch
,
}:
systemd.overrideAttrs (old: {
patches =
old.patches
++ [
(fetchpatch {
url = "https://github.com/Mic92/systemd/commit/93a2921a81cab3be9b7eacab6b0095c96a0ae9e2.patch";
sha256 = "sha256-7WlhMLE7sfD3Cxn6n6R1sUNzUOvas7XMyabi3bsq7jM=";
})
];
})
Loading…
Cancel
Save