fix opensearch pod resolution + sol-agent vault policy
os_api: resolve pod name by label instead of hardcoded opensearch-0. added find_pod_by_label helper to kube.rs. secrets.py: sol-agent policy (read/write sol-tokens/*) and k8s auth role bound to matrix namespace default SA.
This commit is contained in:
19
src/kube.rs
19
src/kube.rs
@@ -305,6 +305,25 @@ pub async fn create_secret(ns: &str, name: &str, data: HashMap<String, String>)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the first Running pod matching a label selector in a namespace.
|
||||||
|
pub async fn find_pod_by_label(ns: &str, label: &str) -> Option<String> {
|
||||||
|
let client = get_client().await.ok()?;
|
||||||
|
let pods: kube::Api<k8s_openapi::api::core::v1::Pod> =
|
||||||
|
kube::Api::namespaced(client, ns);
|
||||||
|
let lp = kube::api::ListParams::default().labels(label);
|
||||||
|
let pod_list = pods.list(&lp).await.ok()?;
|
||||||
|
pod_list
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.find(|p| {
|
||||||
|
p.status
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|s| s.phase.as_deref())
|
||||||
|
== Some("Running")
|
||||||
|
})
|
||||||
|
.and_then(|p| p.metadata.name.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute a command in a pod and return (exit_code, stdout).
|
/// Execute a command in a pod and return (exit_code, stdout).
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn kube_exec(
|
pub async fn kube_exec(
|
||||||
|
|||||||
@@ -475,10 +475,16 @@ async fn os_api(path: &str, method: &str, body: Option<&str>) -> Option<String>
|
|||||||
curl_args.extend_from_slice(&["-H", "Content-Type: application/json", "-d", &body_string]);
|
curl_args.extend_from_slice(&["-H", "Content-Type: application/json", "-d", &body_string]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the full exec command: exec deploy/opensearch -n data -c opensearch -- curl ...
|
// Resolve the actual pod name from the app=opensearch label
|
||||||
let exec_cmd = curl_args;
|
let pod_name = match crate::kube::find_pod_by_label("data", "app=opensearch").await {
|
||||||
|
Some(name) => name,
|
||||||
|
None => {
|
||||||
|
crate::output::warn("No OpenSearch pod found in data namespace");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match crate::kube::kube_exec("data", "opensearch-0", &exec_cmd, Some("opensearch")).await {
|
match crate::kube::kube_exec("data", &pod_name, &curl_args, Some("opensearch")).await {
|
||||||
Ok((0, out)) if !out.is_empty() => Some(out),
|
Ok((0, out)) if !out.is_empty() => Some(out),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,6 +308,25 @@ pub async fn create_secret(ns: &str, name: &str, data: HashMap<String, String>)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the first Running pod matching a label selector in a namespace.
|
||||||
|
pub async fn find_pod_by_label(ns: &str, label: &str) -> Option<String> {
|
||||||
|
let client = get_client().await.ok()?;
|
||||||
|
let pods: kube::Api<k8s_openapi::api::core::v1::Pod> =
|
||||||
|
kube::Api::namespaced(client.clone(), ns);
|
||||||
|
let lp = kube::api::ListParams::default().labels(label);
|
||||||
|
let pod_list = pods.list(&lp).await.ok()?;
|
||||||
|
pod_list
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.find(|p| {
|
||||||
|
p.status
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|s| s.phase.as_deref())
|
||||||
|
== Some("Running")
|
||||||
|
})
|
||||||
|
.and_then(|p| p.metadata.name.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute a command in a pod and return (exit_code, stdout).
|
/// Execute a command in a pod and return (exit_code, stdout).
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn kube_exec(
|
pub async fn kube_exec(
|
||||||
|
|||||||
@@ -475,10 +475,15 @@ async fn os_api(path: &str, method: &str, body: Option<&str>) -> Option<String>
|
|||||||
curl_args.extend_from_slice(&["-H", "Content-Type: application/json", "-d", &body_string]);
|
curl_args.extend_from_slice(&["-H", "Content-Type: application/json", "-d", &body_string]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the full exec command: exec deploy/opensearch -n data -c opensearch -- curl ...
|
let pod_name = match crate::kube::find_pod_by_label("data", "app=opensearch").await {
|
||||||
let exec_cmd = curl_args;
|
Some(name) => name,
|
||||||
|
None => {
|
||||||
|
crate::output::warn("No OpenSearch pod found in data namespace");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match crate::kube::kube_exec("data", "opensearch-0", &exec_cmd, Some("opensearch")).await {
|
match crate::kube::kube_exec("data", &pod_name, &curl_args, Some("opensearch")).await {
|
||||||
Ok((0, out)) if !out.is_empty() => Some(out),
|
Ok((0, out)) if !out.is_empty() => Some(out),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -383,6 +383,14 @@ def _seed_openbao() -> dict:
|
|||||||
"turn-secret": tuwunel["turn-secret"],
|
"turn-secret": tuwunel["turn-secret"],
|
||||||
"registration-token": tuwunel["registration-token"]})
|
"registration-token": tuwunel["registration-token"]})
|
||||||
|
|
||||||
|
# Patch gitea admin credentials into secret/sol for Sol's Gitea integration.
|
||||||
|
# Uses kv patch (not put) to preserve manually-set keys (matrix-access-token etc.).
|
||||||
|
ok("Patching Gitea admin credentials into secret/sol...")
|
||||||
|
bao(f"BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN='{root_token}' "
|
||||||
|
f"bao kv patch secret/sol "
|
||||||
|
f"gitea-admin-username='{gitea['admin-username']}' "
|
||||||
|
f"gitea-admin-password='{gitea['admin-password']}'")
|
||||||
|
|
||||||
# Configure Kubernetes auth method so VSO can authenticate with OpenBao
|
# Configure Kubernetes auth method so VSO can authenticate with OpenBao
|
||||||
ok("Configuring Kubernetes auth for VSO...")
|
ok("Configuring Kubernetes auth for VSO...")
|
||||||
bao(f"BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN='{root_token}' "
|
bao(f"BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN='{root_token}' "
|
||||||
@@ -407,6 +415,23 @@ def _seed_openbao() -> dict:
|
|||||||
f"policies=vso-reader "
|
f"policies=vso-reader "
|
||||||
f"ttl=1h")
|
f"ttl=1h")
|
||||||
|
|
||||||
|
# Sol agent policy — read/write access to sol-tokens/* for user impersonation PATs
|
||||||
|
ok("Configuring Kubernetes auth for Sol agent...")
|
||||||
|
sol_policy_hcl = (
|
||||||
|
'path "secret/data/sol-tokens/*" { capabilities = ["create", "read", "update", "delete"] }\n'
|
||||||
|
'path "secret/metadata/sol-tokens/*" { capabilities = ["read", "delete", "list"] }\n'
|
||||||
|
)
|
||||||
|
sol_policy_b64 = base64.b64encode(sol_policy_hcl.encode()).decode()
|
||||||
|
bao(f"BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN='{root_token}' "
|
||||||
|
f"sh -c 'echo {sol_policy_b64} | base64 -d | bao policy write sol-agent -'")
|
||||||
|
|
||||||
|
bao(f"BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN='{root_token}' "
|
||||||
|
f"bao write auth/kubernetes/role/sol-agent "
|
||||||
|
f"bound_service_account_names=default "
|
||||||
|
f"bound_service_account_namespaces=matrix "
|
||||||
|
f"policies=sol-agent "
|
||||||
|
f"ttl=1h")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"hydra-system-secret": hydra["system-secret"],
|
"hydra-system-secret": hydra["system-secret"],
|
||||||
"hydra-cookie-secret": hydra["cookie-secret"],
|
"hydra-cookie-secret": hydra["cookie-secret"],
|
||||||
|
|||||||
Reference in New Issue
Block a user