tools k8s password-vault-init
homelabctl2 tools k8s password-vault-init reads a TOML file describing
groups of KV entries with per-field length / static / prevent_override
metadata, generates passwords (or uses the configured static value), and
writes them to a Vault KV v2 mount. It supersedes the legacy Rust
homelabctl tools k8s password-vault-init and is the only command run by
the in-cluster vault-credential-setup Job
(k8s/manifests/base/tools/vault-credential-setup/job.yaml).
homelabctl2 tools k8s password-vault-init -c <toml-file> [--vault-addr URL] [--vault-token TOKEN]| Flag | Default | Description |
|---|---|---|
-c, --config | required | Path to the TOML config (see schema below). |
--vault-addr | $VAULT_ADDR | Vault address. Required unless $VAULT_ADDR is set. |
--vault-token | $VAULT_TOKEN | Vault token. Falls back to Kubernetes SA login when both arg+env are empty. |
Vault authentication
Section titled “Vault authentication”The client picks an auth method in this order:
--vault-tokenargument$VAULT_TOKENenv var- Kubernetes Service Account login at
auth/<vault_k8s_mount>/login, using the projected JWT at/var/run/secrets/kubernetes.io/serviceaccount/tokenand thevault_rolefrom the TOML.
The K8s path is what the in-cluster Job uses — no $VAULT_TOKEN is set, so
the pod’s SA JWT is exchanged for a short-lived Vault token via the role
declared in the config.
TOML schema
Section titled “TOML schema”Config├── vault_role (string, required for k8s auth)├── vault_k8s_mount (string, default: "kubernetes")├── kv_mount (string, default: "secrets")├── global_path_prefix (string, optional)└── groups: map<group-name, [GroupInstance]> └── GroupInstance ├── path_prefix (string) └── <entry-name>: Entry ├── type (string, "kv" required) ├── exclude_group_prefix (bool, default false) ├── exclude_global_prefix (bool, default false) └── <field-name>: Field ├── length (int, default 20) ├── static (string, optional — overrides generated) ├── prevent_override (bool, default false) ├── special_chars (bool, default true) └── allowed_special_chars (string, optional — narrows the special set)Path composition
Section titled “Path composition”Each entry is written to a Vault KV v2 path computed as:
{global_path_prefix}/{group.path_prefix}/{entry-name}exclude_group_prefix = true drops the group’s path_prefix;
exclude_global_prefix = true drops global_path_prefix. Setting both
writes directly under <entry-name>.
Fields
Section titled “Fields”| Key | Behavior |
|---|---|
length | Length of the generated password (printable ASCII, uniform via crypto/rand). Default 20. |
static | Use this fixed value instead of generating. length is ignored when static is set. |
prevent_override | If true and the Vault path already has this key, the existing value is preserved (read-modify-write). |
special_chars | Include non-alphanumeric characters in generated passwords. Default true. Set to false for apps that reject specials (alphanumeric-only output). |
allowed_special_chars | Optional string restricting which specials are used when special_chars = true. Empty (default) means all printable-ASCII specials. Ignored when special_chars = false. |
prevent_override is required for credentials that downstream consumers
have already issued tokens against (Authentik bootstrap, Postgres app users
with active leases). Generating fresh values on every reconcile would break
them.
special_chars / allowed_special_chars exist for downstream apps that
either reject special characters outright or only accept a known subset
(e.g. PowerDNS rejects passwords containing : in its web UI). Set
special_chars = false to emit alphanumeric-only output, or set
allowed_special_chars = "!@#$%" to restrict the special set.
Example: realistic vault-credential-setup.toml
Section titled “Example: realistic vault-credential-setup.toml”This is the example config that ships in code/homelabctl/test/config.toml
in the legacy repo. It seeds two Postgres accounts for the
homelab-services CNPG cluster — the operator-owned acc-root is rotated
on every run, and acc-vault is created once and never rewritten (Vault’s
own database secrets engine then derives leases off it).
vault_role = "pg-homelab-services-setup"global_path_prefix = "cluster/internal/ns"kv_mount = "secret"
[[groups.cluster-pg-hl-service]] path_prefix = "pg-homelab-services/homelab-services-cluster"
[groups.cluster-pg-hl-service.acc-root] type = "kv" [groups.cluster-pg-hl-service.acc-root.password] length = 24 prevent_override = false [groups.cluster-pg-hl-service.acc-root.username] static = "root" prevent_override = false
[groups.cluster-pg-hl-service.acc-vault] type = "kv" exclude_global_prefix = false [groups.cluster-pg-hl-service.acc-vault.password] length = 20 prevent_override = true [groups.cluster-pg-hl-service.acc-vault.password2] length = 20 [groups.cluster-pg-hl-service.acc-vault.username] static = "vault"That config writes two secrets:
| Path | Keys | Notes |
|---|---|---|
secret/cluster/internal/ns/pg-homelab-services/homelab-services-cluster/acc-root | username (static root), password (24-char) | Rotated on every reconcile. |
secret/cluster/internal/ns/pg-homelab-services/homelab-services-cluster/acc-vault | username (static vault), password (20-char, prevented), password2 (20-char) | password is set once and preserved thereafter — anything sharing the existing lease keeps working. |
In-cluster usage
Section titled “In-cluster usage”The vault-credential-setup Job (k8s/manifests/base/tools/vault-credential-setup/job.yaml)
mounts a ConfigMap-provided TOML at /etc/homelabctl/config/:
args: - --log-level - debug - --log-format - json - tools - k8s - password-vault-init - -c - /etc/homelabctl/config/vault-credential-setup.tomlenv: - name: VAULT_ADDR value: "http://openbao.openbao.svc.cluster.local:8200"The Job runs under a SA bound to a Vault role (vault_role in the TOML)
that grants write access to the configured KV mount + paths.