mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 00:06:06 +00:00
feat(vault): optional KV secret path prefix setting (WIN-1960) (#9249)
* feat(vault): add optional KV secret path prefix setting (WIN-1960) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: update ee-repo-ref to 0189ba6504fd70eb4929e4881d624d48efd14aee This commit updates the EE repository reference after PR #581 was merged in windmill-ee-private. Previous ee-repo-ref: e32e8d6483550c67897e09b6f900dff1034bdae8 New ee-repo-ref: 0189ba6504fd70eb4929e4881d624d48efd14aee Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
windmill-internal-app[bot]
parent
457a78cc6a
commit
d08f72b3e1
@@ -1 +1 @@
|
||||
017d36418a65ce5c840c502e3174df0c393612ba
|
||||
0189ba6504fd70eb4929e4881d624d48efd14aee
|
||||
|
||||
@@ -21076,6 +21076,9 @@ components:
|
||||
mount_path:
|
||||
type: string
|
||||
description: KV v2 secrets engine mount path (e.g., windmill)
|
||||
kv_secret_path_prefix:
|
||||
type: string
|
||||
description: Optional path prefix inserted between the KV data/metadata segment and the workspace id (e.g., "apps/windmill"). When set, secrets are stored at `<mount>/data/<prefix>/<workspace>/<secret>`, allowing a Vault policy scoped to exactly `<mount>/data/<prefix>/*`.
|
||||
jwt_role:
|
||||
type: string
|
||||
description: Vault JWT auth role name for Windmill (optional, if not provided token auth is used)
|
||||
|
||||
@@ -118,6 +118,13 @@ pub struct VaultSettings {
|
||||
pub address: String,
|
||||
/// KV v2 mount path (e.g., "windmill")
|
||||
pub mount_path: String,
|
||||
/// Optional path prefix inserted between the KV `data`/`metadata` segment
|
||||
/// and the workspace id, e.g. "apps/windmill". When set, secrets live at
|
||||
/// `<mount>/data/<prefix>/<workspace>/<secret>`, so a Vault policy can be
|
||||
/// scoped to exactly `<mount>/data/<prefix>/*`. Surrounding slashes are
|
||||
/// trimmed.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub kv_secret_path_prefix: Option<String>,
|
||||
/// JWT auth role name configured in Vault (used for JWT/OIDC auth)
|
||||
/// Optional - if not provided, token auth is used
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -25,6 +25,7 @@ mod tests {
|
||||
VaultSettings {
|
||||
address: "http://127.0.0.1:8200".to_string(),
|
||||
mount_path: "windmill".to_string(),
|
||||
kv_secret_path_prefix: None,
|
||||
jwt_role: Some("windmill-secrets".to_string()),
|
||||
jwt_mount_path: None,
|
||||
namespace: None,
|
||||
|
||||
@@ -90,6 +90,7 @@ mod tests {
|
||||
address: std::env::var("VAULT_ADDR")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:8200".to_string()),
|
||||
mount_path: "windmill".to_string(),
|
||||
kv_secret_path_prefix: None,
|
||||
jwt_role: None, // Static token mode
|
||||
jwt_mount_path: None,
|
||||
namespace: None,
|
||||
@@ -106,6 +107,7 @@ mod tests {
|
||||
address: std::env::var("VAULT_ADDR")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:8200".to_string()),
|
||||
mount_path: "windmill".to_string(),
|
||||
kv_secret_path_prefix: None,
|
||||
jwt_role: Some("windmill-secrets".to_string()), // JWT mode
|
||||
jwt_mount_path: None,
|
||||
namespace: None,
|
||||
|
||||
@@ -34,6 +34,7 @@ fn test_vault_settings() -> VaultSettings {
|
||||
address: std::env::var("VAULT_ADDR")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:8200".to_string()),
|
||||
mount_path: "windmill".to_string(),
|
||||
kv_secret_path_prefix: None,
|
||||
jwt_role: Some("windmill-secrets".to_string()),
|
||||
jwt_mount_path: None,
|
||||
namespace: None,
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
type: 'HashiCorpVault',
|
||||
address: $values['secret_backend']?.address ?? '',
|
||||
mount_path: $values['secret_backend']?.mount_path ?? 'windmill',
|
||||
kv_secret_path_prefix: $values['secret_backend']?.kv_secret_path_prefix ?? null,
|
||||
jwt_role: $values['secret_backend']?.jwt_role ?? 'windmill-secrets',
|
||||
jwt_mount_path: $values['secret_backend']?.jwt_mount_path ?? null,
|
||||
namespace: $values['secret_backend']?.namespace ?? null,
|
||||
@@ -122,6 +123,7 @@
|
||||
return {
|
||||
address: $values['secret_backend'].address,
|
||||
mount_path: $values['secret_backend'].mount_path,
|
||||
kv_secret_path_prefix: $values['secret_backend'].kv_secret_path_prefix || undefined,
|
||||
jwt_role: $values['secret_backend'].jwt_role,
|
||||
jwt_mount_path: $values['secret_backend'].jwt_mount_path || undefined,
|
||||
namespace: $values['secret_backend'].namespace || undefined,
|
||||
@@ -355,6 +357,11 @@
|
||||
|
||||
let baseUrl = $derived($values['base_url'] ?? 'https://your-windmill-instance.com')
|
||||
let jwtMount = $derived(($values['secret_backend']?.jwt_mount_path?.trim() || 'jwt') as string)
|
||||
let kvPrefix = $derived(
|
||||
($values['secret_backend']?.kv_secret_path_prefix?.trim().replace(/^\/+|\/+$/g, '') ||
|
||||
'') as string
|
||||
)
|
||||
let kvPolicyPath = $derived(kvPrefix ? `${kvPrefix}/*` : '*')
|
||||
let vaultAudience = $derived(
|
||||
($values['secret_backend']?.address?.trim() || 'https://vault.example.com:8200') as string
|
||||
)
|
||||
@@ -453,6 +460,27 @@
|
||||
bind:value={$values['secret_backend'].mount_path}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="vault_kv_secret_path_prefix" class="block text-xs font-semibold text-emphasis"
|
||||
>KV Secret Path Prefix (optional)</label
|
||||
>
|
||||
<span class="text-2xs text-secondary"
|
||||
>Optional prefix inserted before the workspace id. When set, secrets are stored at
|
||||
<code><mount>/data/<prefix>/<workspace>/<secret></code>, so you
|
||||
can keep an existing layout and scope a Vault policy to exactly
|
||||
<code>{$values['secret_backend']?.mount_path ?? 'windmill'}/data/{kvPolicyPath}</code
|
||||
>.</span
|
||||
>
|
||||
<TextInput
|
||||
inputProps={{
|
||||
type: 'text',
|
||||
id: 'vault_kv_secret_path_prefix',
|
||||
placeholder: 'apps/windmill',
|
||||
disabled
|
||||
}}
|
||||
bind:value={$values['secret_backend'].kv_secret_path_prefix}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="block text-xs font-semibold text-emphasis">Authentication Method</span>
|
||||
<ToggleButtonGroup selected={authMethod} onSelected={(v) => setAuthMethod(v)}>
|
||||
@@ -541,10 +569,10 @@ vault write auth/{jwtMount}/config \
|
||||
|
||||
# Create a policy for Windmill secrets
|
||||
vault policy write windmill-secrets - <<EOF
|
||||
path "{$values['secret_backend']?.mount_path ?? 'windmill'}/data/*" {
|
||||
path "{$values['secret_backend']?.mount_path ?? 'windmill'}/data/{kvPolicyPath}" {
|
||||
capabilities = ["create", "read", "update", "delete"]
|
||||
}
|
||||
path "{$values['secret_backend']?.mount_path ?? 'windmill'}/metadata/*" {
|
||||
path "{$values['secret_backend']?.mount_path ?? 'windmill'}/metadata/{kvPolicyPath}" {
|
||||
capabilities = ["list", "delete"]
|
||||
}
|
||||
EOF
|
||||
|
||||
Reference in New Issue
Block a user