203 lines
5.6 KiB
Nix
203 lines
5.6 KiB
Nix
{ lib, config, pkgs, ... }:
|
|
|
|
with lib;
|
|
with lib.plusultra;
|
|
let
|
|
cfg = config.plusultra.services.vault;
|
|
|
|
package = if cfg.ui then pkgs.vault-bin else pkgs.vault;
|
|
|
|
has-policies = (builtins.length (builtins.attrNames cfg.policies)) != 0;
|
|
|
|
format-policy = name: file: pkgs.runCommandNoCC
|
|
"formatted-vault-policy"
|
|
{
|
|
inherit file;
|
|
buildInputs = [ package ];
|
|
}
|
|
''
|
|
name="$(basename "$file")"
|
|
|
|
cp "$file" "./$name"
|
|
|
|
# Ensure that vault can overwrite the file.
|
|
chmod +w "./$name"
|
|
|
|
# Create this variable here to avoid swallowing vault's exit code.
|
|
vault_output=
|
|
|
|
set +e
|
|
vault_output=$(vault policy fmt "./$name" 2>&1)
|
|
vault_status=$?
|
|
set -e
|
|
|
|
if [ "$vault_status" != 0 ]; then
|
|
echo 'Error formatting policy "${name}"'
|
|
echo "This is normally caused by a syntax error in the policy file."
|
|
echo "$file"
|
|
echo ""
|
|
echo "Vault Output:"
|
|
echo "$vault_output"
|
|
exit 1
|
|
fi
|
|
|
|
mv "./$name" $out
|
|
'';
|
|
|
|
policies = mapAttrs
|
|
(name: value:
|
|
if builtins.isPath value then
|
|
format-policy name value
|
|
else
|
|
format-policy name (pkgs.writeText "${name}.hcl" value)
|
|
)
|
|
cfg.policies;
|
|
in
|
|
{
|
|
options.plusultra.services.vault = {
|
|
enable = mkEnableOption "Vault";
|
|
|
|
ui = mkBoolOpt true "Whether the UI should be enabled.";
|
|
|
|
storage = {
|
|
backend = mkOpt types.str "file" "The storage backend for Vault.";
|
|
};
|
|
|
|
settings = mkOpt types.str "" "Configuration for Vault's config file.";
|
|
|
|
mutable-policies = mkBoolOpt false "Whether policies not specified in Nix should be removed.";
|
|
|
|
policies = mkOpt (types.attrsOf (types.either types.str types.path)) { } "Policies to install when Vault runs.";
|
|
|
|
policy-agent = {
|
|
user = mkOpt types.str "vault" "The user to run the Vault Agent as.";
|
|
group = mkOpt types.str "vault" "The group to run the Vault Agent as.";
|
|
|
|
auth = {
|
|
roleIdFilePath = mkOpt types.str "/var/lib/vault/role-id" "The file to read the role-id from.";
|
|
secretIdFilePath = mkOpt types.str "/var/lib/vault/secret-id" "The file to read the secret-id from.";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
services.vault = {
|
|
enable = true;
|
|
inherit package;
|
|
|
|
extraConfig = ''
|
|
ui = ${if cfg.ui then "true" else "false"}
|
|
|
|
${cfg.settings}
|
|
'';
|
|
|
|
};
|
|
|
|
systemd.services.vault = { };
|
|
|
|
systemd.services.vault-policies = mkIf (has-policies || !cfg.mutable-policies) {
|
|
wantedBy = [ "vault.service" ];
|
|
after = [ "vault.service" ];
|
|
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = cfg.policy-agent.user;
|
|
Group = cfg.policy-agent.group;
|
|
Restart = "on-failure";
|
|
RestartSec = 30;
|
|
RemainAfterExit = "yes";
|
|
};
|
|
|
|
restartTriggers = (mapAttrsToList (name: value: "${name}=${value}") policies);
|
|
|
|
path = [
|
|
package
|
|
pkgs.curl
|
|
pkgs.jq
|
|
];
|
|
|
|
environment = {
|
|
VAULT_ADDR = "http://${config.services.vault.address}";
|
|
};
|
|
|
|
script =
|
|
let
|
|
write-policies-commands = mapAttrsToList
|
|
(name: policy:
|
|
''
|
|
echo Writing policy '${name}': '${policy}'
|
|
vault policy write '${name}' '${policy}'
|
|
''
|
|
)
|
|
policies;
|
|
write-policies = concatStringsSep "\n" write-policies-commands;
|
|
|
|
known-policies = mapAttrsToList (name: value: name) policies;
|
|
|
|
remove-unknown-policies = ''
|
|
current_policies=$(vault policy list -format=json | jq -r '.[]')
|
|
known_policies=(${concatStringsSep " " (builtins.map (policy: "\"${policy}\"") known-policies)})
|
|
|
|
while read current_policy; do
|
|
is_known=false
|
|
|
|
for known_policy in "''${known_policies[@]}"; do
|
|
if [ "$known_policy" = "$current_policy" ]; then
|
|
is_known=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$is_known" = "false" ] && [ "$current_policy" != "default" ] && [ "$current_policy" != "root" ]; then
|
|
echo "Removing policy: $current_policy"
|
|
vault policy delete "$current_policy"
|
|
else
|
|
echo "Keeping policy: $current_policy"
|
|
fi
|
|
done <<< "$current_policies"
|
|
'';
|
|
in
|
|
''
|
|
if ! [ -f '${cfg.policy-agent.auth.roleIdFilePath}' ]; then
|
|
echo 'role-id file not found: ${cfg.policy-agent.auth.roleIdFilePath}'
|
|
exit 1
|
|
fi
|
|
|
|
if ! [ -f '${cfg.policy-agent.auth.secretIdFilePath}' ]; then
|
|
echo 'secret-id file not found: ${cfg.policy-agent.auth.secretIdFilePath}'
|
|
exit 1
|
|
fi
|
|
|
|
role_id="$(cat '${cfg.policy-agent.auth.roleIdFilePath}')"
|
|
secret_id="$(cat '${cfg.policy-agent.auth.secretIdFilePath}')"
|
|
|
|
seal_status=$(curl -s "$VAULT_ADDR/v1/sys/seal-status" | jq ".sealed")
|
|
|
|
echo "Seal Status: $seal_status"
|
|
|
|
if [ seal_status = "true" ]; then
|
|
echo "Vault is currently sealed, cannot install policies."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Getting token..."
|
|
|
|
token=$(vault write -field=token auth/approle/login \
|
|
role_id="$role_id" \
|
|
secret_id="$secret_id" \
|
|
)
|
|
|
|
echo "Logging in..."
|
|
|
|
export VAULT_TOKEN="$(vault login -method=token -token-only token="$token")"
|
|
|
|
echo "Writing policies..."
|
|
|
|
${write-policies}
|
|
|
|
${optionalString (!cfg.mutable-policies) remove-unknown-policies}
|
|
'';
|
|
};
|
|
};
|
|
}
|