From f9e8592cbe8f0dc4628a2d391b7eb1a76ead96a4 Mon Sep 17 00:00:00 2001 From: Lumpiasty Date: Sun, 16 Feb 2025 21:27:46 +0100 Subject: [PATCH] Create vault policies recoinciling script --- flake.nix | 6 +++ utils/synchronize-vault.py | 75 +++++++++++++++++++++++++++++++++ vault/policy/default.hcl | 86 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100755 utils/synchronize-vault.py create mode 100644 vault/policy/default.hcl diff --git a/flake.nix b/flake.nix index 9d97353..d21d619 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,9 @@ in pkgs.mkShell { packages = with pkgs; [ + (python313.withPackages (python-pkgs: with python-pkgs; [ + hvac + ])) vim gnumake talosctl cilium-cli kubectx k9s kubernetes-helm @@ -47,6 +50,9 @@ # export AWS_SECRET_ACCESS_KEY=? # export RESTIC_PASSWORD=? export VAULT_ADDR=https://openbao.lumpiasty.xyz:8200 + + # Add scripts from utils subdir + export PATH="$PATH:$(pwd)/utils" ''; }; }; diff --git a/utils/synchronize-vault.py b/utils/synchronize-vault.py new file mode 100755 index 0000000..8478813 --- /dev/null +++ b/utils/synchronize-vault.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +import argparse +import os +from hvac.api.system_backend import mount +import yaml +import hvac +from hvac.api.auth_methods import Kubernetes, kubernetes + +# Read vault/policies dir then write what is there and delete missing +def synchronize_policies(client: hvac.Client): + policies = {} + # Read all policies files + policy_dir = os.path.join(os.path.dirname(__file__), '../vault/policy') + for filename in os.listdir(policy_dir): + with open(os.path.join(policy_dir, filename), 'r') as f: + policy_name = os.path.splitext(filename)[0] + policies[policy_name] = f.read() + + policies_on_vault = client.sys.list_policies()['data']['policies'] + + # Delete policies that should not be there + for policy in policies_on_vault: + if policy not in policies and policy != 'root': + print(f'Deleting policy: {policy}') + client.sys.delete_policy(policy) + + # Update policies from local directory + for policy_name, policy_content in policies.items(): + print(f'Updating policy: {policy_name}') + client.sys.create_or_update_acl_policy(policy_name, policy_content) + +# Read vault/kubernetes-roles dir then write what is there and delete missing +def synchronize_kubernetes_roles(client: hvac.Client): + kubernetes = Kubernetes(client.adapter) + + policy_dir = os.path.join(os.path.dirname(__file__), '../vault/kubernetes-roles/') + + roles = {} + for filename in os.listdir(policy_dir): + with open(os.path.join(policy_dir, filename), 'r') as f: + role_name = os.path.splitext(filename)[0] + roles[role_name] = yaml.safe_load(f.read()) + + roles_on_vault = [] + try: + roles_on_vault = kubernetes.list_roles()['keys'] + except hvac.exceptions.InvalidPath: + print("No roles found on server!") + + + for role in roles_on_vault: + if role not in roles_on_vault: + print(f'Deleting role: {role}') + kubernetes.delete_role(role) + + for role_name, role_content in roles.items(): + print(f'Updating role: {role_name}') + # Using write data instead of kubernetes.create_role, we can pass raw yaml + client.write_data(f'/auth/kubernetes/role/{role_name}', data=role_content) + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + prog="synchronizeVault", + description="Update vault config" + ) + args = parser.parse_args() + + client = hvac.Client(url=os.environ['VAULT_ADDR']) + + print('Synchronizing policies') + synchronize_policies(client) + + print('Synchronizing kubernetes roles') + synchronize_kubernetes_roles(client) diff --git a/vault/policy/default.hcl b/vault/policy/default.hcl new file mode 100644 index 0000000..d4fc188 --- /dev/null +++ b/vault/policy/default.hcl @@ -0,0 +1,86 @@ +# Allow tokens to look up their own properties +path "auth/token/lookup-self" { + capabilities = ["read"] +} + +# Allow tokens to renew themselves +path "auth/token/renew-self" { + capabilities = ["update"] +} + +# Allow tokens to revoke themselves +path "auth/token/revoke-self" { + capabilities = ["update"] +} + +# Allow a token to look up its own capabilities on a path +path "sys/capabilities-self" { + capabilities = ["update"] +} + +# Allow a token to look up its own entity by id or name +path "identity/entity/id/{{identity.entity.id}}" { + capabilities = ["read"] +} +path "identity/entity/name/{{identity.entity.name}}" { + capabilities = ["read"] +} + + +# Allow a token to look up its resultant ACL from all policies. This is useful +# for UIs. It is an internal path because the format may change at any time +# based on how the internal ACL features and capabilities change. +path "sys/internal/ui/resultant-acl" { + capabilities = ["read"] +} + +# Allow a token to renew a lease via lease_id in the request body; old path for +# old clients, new path for newer +path "sys/renew" { + capabilities = ["update"] +} +path "sys/leases/renew" { + capabilities = ["update"] +} + +# Allow looking up lease properties. This requires knowing the lease ID ahead +# of time and does not divulge any sensitive information. +path "sys/leases/lookup" { + capabilities = ["update"] +} + +# Allow a token to manage its own cubbyhole +path "cubbyhole/*" { + capabilities = ["create", "read", "update", "delete", "list"] +} + +# Allow a token to wrap arbitrary values in a response-wrapping token +path "sys/wrapping/wrap" { + capabilities = ["update"] +} + +# Allow a token to look up the creation time and TTL of a given +# response-wrapping token +path "sys/wrapping/lookup" { + capabilities = ["update"] +} + +# Allow a token to unwrap a response-wrapping token. This is a convenience to +# avoid client token swapping since this is also part of the response wrapping +# policy. +path "sys/wrapping/unwrap" { + capabilities = ["update"] +} + +# Allow general purpose tools +path "sys/tools/hash" { + capabilities = ["update"] +} +path "sys/tools/hash/*" { + capabilities = ["update"] +} + +# Allow a token to make requests to the Authorization Endpoint for OIDC providers. +path "identity/oidc/provider/+/authorize" { + capabilities = ["read", "update"] +}