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
```
## 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
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.systemd = pkgs.callPackage ./nix/pkgs/systemd.nix { };
devShells.default = pkgs.mkShellNoCC {
buildInputs = with pkgs; [
python3.pkgs.pytest
@ -35,7 +34,6 @@
go
just
config.treefmt.build.wrapper
config.packages.systemd
];
};

@ -2,7 +2,6 @@
name = "systemd-vaultd";
nodes.server =
{ config
, pkgs
, ...
}: {
imports = [
@ -10,6 +9,9 @@
../modules/systemd-vaultd.nix
./dev-vault-server.nix
];
# speed up tests
virtualisation.cores = 4;
virtualisation.memorySize = 1024;
systemd.services.service1 = {
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 = {
wantedBy = [ "multi-user.target" ];
preStart = ''
cp -r $CREDENTIALS_DIRECTORY /run/service2/secrets
'';
script = ''
set -x
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
done
'';
serviceConfig.ExecReload = "${pkgs.coreutils}/bin/true";
serviceConfig.LoadCredential = [ "secret:/run/systemd-vaultd/sock" ];
serviceConfig = {
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 = {
template = ''
{{ 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_unit("setup-vault-agent-approle.service")
out = machine.wait_until_succeeds("cat /tmp/service1")
print(out)
assert out == "bar", f"{out} != bar for service1"
out = machine.wait_until_succeeds("grep -q bar /tmp/service1")
out = machine.succeed("cat /tmp/service1-env")
print(out)
assert out == "bar", f"{out} != bar for service1-env"
out = machine.succeed("grep -q bar /tmp/service1-env")
out = machine.succeed("systemctl status service2")
out = machine.succeed("systemctl status service2 || :")
print(out)
assert "(sd-mkdcreds)" in out, "service2 should be still blocked"
machine.succeed("vault kv put secret/blocking-secret foo=bar")
out = machine.wait_until_succeeds("cat /tmp/service2")
print(out)
assert out == "bar", f"{out} != bar for service2"
machine.wait_until_succeeds("grep -q bar /tmp/service2 >&2")
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("vault kv put secret/blocking-secret foo=reload")
machine.succeed("systemctl restart vault-agent-default")
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")
out = machine.wait_until_succeeds("cat /tmp/service2")
print(out)
assert out == "reload", f"{out} != reload for service2"
machine.wait_until_succeeds("grep -q reload /tmp/service2 >&2")
# 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
, lib
, coreutils
,
, systemd
}:
let
systemd-vaultd = pkgs.callPackage ../../default.nix { };
systemd = pkgs.callPackage ../pkgs/systemd.nix { };
in
writeShellScript "unittests" ''
set -eu -o pipefail

@ -1,4 +1,6 @@
{ pkgs
, lib
, config
, ...
}:
let
@ -8,9 +10,20 @@ in
imports = [
./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 = {
description = "systemd-vaultd socket";
wantedBy = [ "sockets.target" ];
@ -28,7 +41,8 @@ in
# Restarting can break services waiting for secrets
stopIfChanged = false;
serviceConfig = {
ExecStart = "${systemd-vaultd}/bin/systemd-vaultd";
ExecStart = "${config.services.systemd-vaultd.package}/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