Route railiance01 admin kubeconfig through an attended lane
Assistant: grok Assistant-Session: 01a1625d-4770-76a0-8dee-7f5c514a47eb
This commit is contained in:
parent
97c2e3533d
commit
537d551ef2
7 changed files with 94 additions and 4 deletions
|
|
@ -214,6 +214,42 @@ entries:
|
||||||
exec_capable: true
|
exec_capable: true
|
||||||
lane: login
|
lane: login
|
||||||
|
|
||||||
|
- id: kubernetes-railiance01-admin
|
||||||
|
title: Attended railiance01 cluster-admin kubeconfig
|
||||||
|
# Identity pointer for the admin kubeconfig file. The agent default path is
|
||||||
|
# the scoped ops-mason-builder identity (MASON-IN-0006 choice (b), decision
|
||||||
|
# 30d29060-8d14-4a8e-9cde-34aa826eb8d1). This lane does not read the file.
|
||||||
|
risk: high
|
||||||
|
workload_ref:
|
||||||
|
applicability: not-applicable
|
||||||
|
reason: "Attended operator identity for the railiance01 admin kubeconfig file. The agent default path is the scoped ops-mason-builder user."
|
||||||
|
need_keywords: [kubernetes, kubeconfig, railiance01, cluster-admin, k3s, admin-kubeconfig, ops-mason-builder]
|
||||||
|
owner_repo: railiance-platform
|
||||||
|
subsystem: attended kubeconfig file outside the agent default path
|
||||||
|
warden_executes: false
|
||||||
|
wiki_ref: wiki/playbooks/kubernetes-railiance01-admin.md#worker-checklist
|
||||||
|
canon_ref: railiance-platform/argocd/platform-addons/ops-mason-builder
|
||||||
|
reviewed: "2026-10-22"
|
||||||
|
status: active
|
||||||
|
delegation:
|
||||||
|
mode: native
|
||||||
|
intended_owner: railiance-platform
|
||||||
|
reviewed: "2026-10-22"
|
||||||
|
verified: source-read
|
||||||
|
auth_method: "attended operator; set KUBECONFIG to /home/worsch/.kube/config-railiance01 for one command. That file is not the agent default path."
|
||||||
|
path_template: "/home/worsch/.kube/config-railiance01"
|
||||||
|
fetch_command: "test -f /home/worsch/.kube/config-railiance01"
|
||||||
|
exec_capable: true
|
||||||
|
lane: secret
|
||||||
|
# vends_secret is structural (active secret lane with a path). Renewal is
|
||||||
|
# re-establishing the file in place, never reading or copying it.
|
||||||
|
rotation:
|
||||||
|
method: re-establish
|
||||||
|
owner: railiance-platform
|
||||||
|
automatable: false
|
||||||
|
steps:
|
||||||
|
- "Re-establish the attended admin kubeconfig at /home/worsch/.kube/config-railiance01, outside the agent default path. Do not copy the admin client key into the builder kubeconfig and do not print the file."
|
||||||
|
|
||||||
- id: secrets-engine-approval-client-login
|
- id: secrets-engine-approval-client-login
|
||||||
title: Attended secrets-engine approval client reader login
|
title: Attended secrets-engine approval client reader login
|
||||||
risk: high
|
risk: high
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,15 @@ def denied_data_paths(policy_text: str) -> set[str]:
|
||||||
|
|
||||||
|
|
||||||
def to_data_path(path_template: str) -> str | None:
|
def to_data_path(path_template: str) -> str | None:
|
||||||
"""Catalog path -> KV v2 data path. `<mount>/rest` -> `<mount>/data/rest`."""
|
"""Catalog path -> KV v2 data path. `<mount>/rest` -> `<mount>/data/rest`.
|
||||||
if PLACEHOLDER.search(path_template) or " " in path_template:
|
|
||||||
|
An absolute filesystem path is not a KV mount.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
PLACEHOLDER.search(path_template)
|
||||||
|
or " " in path_template
|
||||||
|
or path_template.startswith("/")
|
||||||
|
):
|
||||||
return None
|
return None
|
||||||
mount, _, rest = path_template.partition("/")
|
mount, _, rest = path_template.partition("/")
|
||||||
return f"{mount}/data/{rest}" if rest else None
|
return f"{mount}/data/{rest}" if rest else None
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,14 @@ def build() -> tuple[str, int]:
|
||||||
|
|
||||||
def _to_data_path(template: str) -> str | None:
|
def _to_data_path(template: str) -> str | None:
|
||||||
import re
|
import re
|
||||||
if re.search(r"[<>{}*]", template) or " " in template or template.startswith("k8s:"):
|
# A leading slash is a filesystem path, not an OpenBao mount. partition()
|
||||||
|
# would otherwise invent an empty mount and a fake `/data/...` address.
|
||||||
|
if (
|
||||||
|
re.search(r"[<>{}*]", template)
|
||||||
|
or " " in template
|
||||||
|
or template.startswith("k8s:")
|
||||||
|
or template.startswith("/")
|
||||||
|
):
|
||||||
return None
|
return None
|
||||||
mount, _, rest = template.partition("/")
|
mount, _, rest = template.partition("/")
|
||||||
return f"{mount}/data/{rest}" if rest else None
|
return f"{mount}/data/{rest}" if rest else None
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,10 @@ class TestToDataPath:
|
||||||
"""`ops-warden-warden-sign-token` is a broker grant, not a KV path."""
|
"""`ops-warden-warden-sign-token` is a broker grant, not a KV path."""
|
||||||
assert mod.to_data_path("credential-grants/catalog.yaml grant ops-warden/warden-sign") is None
|
assert mod.to_data_path("credential-grants/catalog.yaml grant ops-warden/warden-sign") is None
|
||||||
|
|
||||||
|
def test_absolute_filesystem_path_has_no_kv_address(self):
|
||||||
|
"""A kubeconfig path must not become an empty-mount `/data/...` address."""
|
||||||
|
assert mod.to_data_path("/home/worsch/.kube/config-railiance01") is None
|
||||||
|
|
||||||
|
|
||||||
class TestAgainstTheRealCatalog:
|
class TestAgainstTheRealCatalog:
|
||||||
def test_every_high_risk_lane_resolves_or_is_explicitly_pattern(self):
|
def test_every_high_risk_lane_resolves_or_is_explicitly_pattern(self):
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,25 @@ def test_platform_admin_login_lane_is_exact_and_non_value_bearing():
|
||||||
assert entry.workload_ref.resolution == "not-applicable"
|
assert entry.workload_ref.resolution == "not-applicable"
|
||||||
|
|
||||||
|
|
||||||
|
def test_kubernetes_railiance01_admin_lane_is_attended_file_pointer():
|
||||||
|
catalog = load_catalog(_repo_catalog())
|
||||||
|
entry = catalog.get("kubernetes-railiance01-admin")
|
||||||
|
assert entry is not None
|
||||||
|
assert entry.status == "active"
|
||||||
|
assert entry.risk == "high"
|
||||||
|
assert entry.lane == "secret"
|
||||||
|
assert entry.lane != "login"
|
||||||
|
assert entry.exec_capable is True
|
||||||
|
assert entry.resolvable is True
|
||||||
|
assert entry.fetch_command == "test -f /home/worsch/.kube/config-railiance01"
|
||||||
|
assert entry.path_template == "/home/worsch/.kube/config-railiance01"
|
||||||
|
assert "cat" not in entry.fetch_command
|
||||||
|
assert "config view" not in entry.fetch_command
|
||||||
|
assert "<" not in entry.fetch_command and ">" not in entry.fetch_command
|
||||||
|
matches = catalog.find("kubernetes railiance01 admin kubeconfig", limit=1)
|
||||||
|
assert matches[0].id == "kubernetes-railiance01-admin"
|
||||||
|
|
||||||
|
|
||||||
def test_netkingdom_sso_bind_lanes_are_routed_but_not_resolvable():
|
def test_netkingdom_sso_bind_lanes_are_routed_but_not_resolvable():
|
||||||
catalog = load_catalog(_repo_catalog())
|
catalog = load_catalog(_repo_catalog())
|
||||||
for lane_id in (
|
for lane_id in (
|
||||||
|
|
@ -405,7 +424,7 @@ def test_exec_capable_without_fetch_command_rejected(tmp_path):
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"leaked",
|
"leaked",
|
||||||
[
|
[
|
||||||
"bao write x token=ghp_abcdef0123456789abcdef0123", # github token prefix
|
"bao write x ghp_abcdef0123456789abcdef0123", # github pat prefix
|
||||||
"x=AKIAIOSFODNN7EXAMPLE", # aws key id
|
"x=AKIAIOSFODNN7EXAMPLE", # aws key id
|
||||||
"header=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", # jwt prefix
|
"header=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", # jwt prefix
|
||||||
"val=ZmFrZXNlY3JldDEyMzQ1Njc4OWFiY2RlZmdoaWprbA", # high-entropy run
|
"val=ZmFrZXNlY3JldDEyMzQ1Njc4OWFiY2RlZmdoaWprbA", # high-entropy run
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ lists the interim set — WARDEN-WP-0030).
|
||||||
| `ops-warden-warden-sign-token` | "railiance-platform broker owns the `warden-sign` lease — use `credential exec`" | `railiance-platform/scripts/credential.py exec --grant ops-warden/warden-sign` (see playbook) |
|
| `ops-warden-warden-sign-token` | "railiance-platform broker owns the `warden-sign` lease — use `credential exec`" | `railiance-platform/scripts/credential.py exec --grant ops-warden/warden-sign` (see playbook) |
|
||||||
| `openbao-api-key` | "OpenBao owns this — here is the path/command shape" | Call OpenBao directly, or use `warden access --fetch/--exec` as yourself when the lane is `exec_capable` |
|
| `openbao-api-key` | "OpenBao owns this — here is the path/command shape" | Call OpenBao directly, or use `warden access --fetch/--exec` as yourself when the lane is `exec_capable` |
|
||||||
| `openbao-platform-admin-login` | "This is an attended OpenBao administration identity act, not workload secret retrieval" | Use KeyCape-backed OIDC/MFA at `auth/netkingdom`, role `platform-admin`; never substitute a workload role or root token |
|
| `openbao-platform-admin-login` | "This is an attended OpenBao administration identity act, not workload secret retrieval" | Use KeyCape-backed OIDC/MFA at `auth/netkingdom`, role `platform-admin`; never substitute a workload role or root token |
|
||||||
|
| `kubernetes-railiance01-admin` | "Attended railiance01 admin kubeconfig; the agent default path is ops-mason-builder" | Prefix kubectl with KUBECONFIG=/home/worsch/.kube/config-railiance01. Do not fetch the file. |
|
||||||
| `openbao-shamir-recovery-ceremony` | "This is an attended trust-root ceremony, not secret provisioning" | Obtain one explicit approval and follow the railiance-platform recovery checklist with the existing out-of-band custodians; never send shares through Warden |
|
| `openbao-shamir-recovery-ceremony` | "This is an attended trust-root ceremony, not secret provisioning" | Obtain one explicit approval and follow the railiance-platform recovery checklist with the existing out-of-band custodians; never send shares through Warden |
|
||||||
| `flex-auth-policy-check` | "flex-auth decides — here is the policy doc" | Query flex-auth / embed the PEP |
|
| `flex-auth-policy-check` | "flex-auth decides — here is the policy doc" | Query flex-auth / embed the PEP |
|
||||||
| `key-cape-oidc-login` | "key-cape / Keycloak owns identity" | Authenticate via IAM Profile, or use the `warden access` login lane as yourself |
|
| `key-cape-oidc-login` | "key-cape / Keycloak owns identity" | Authenticate via IAM Profile, or use the `warden access` login lane as yourself |
|
||||||
|
|
|
||||||
16
wiki/playbooks/kubernetes-railiance01-admin.md
Normal file
16
wiki/playbooks/kubernetes-railiance01-admin.md
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Attended railiance01 cluster-admin kubeconfig
|
||||||
|
|
||||||
|
## Worker checklist
|
||||||
|
|
||||||
|
This lane is the attended path required by MASON-IN-0006 choice (b). It names
|
||||||
|
the admin kubeconfig file. It does not read that file.
|
||||||
|
|
||||||
|
- The admin file is `/home/worsch/.kube/config-railiance01` and stays that path.
|
||||||
|
- After the scoped identity is installed, `~/.kube/config` is the ops-mason-builder context named `default`, and is not cluster-admin.
|
||||||
|
- Reach admin by prefixing the command: `KUBECONFIG=/home/worsch/.kube/config-railiance01 kubectl ...`
|
||||||
|
- Do not cat the kubeconfig, do not run `kubectl config view`, and do not use `warden access --fetch` to obtain the file. The fetch_command only tests that the file exists.
|
||||||
|
- Do not copy the admin client key into the builder kubeconfig.
|
||||||
|
|
||||||
|
The catalog fetch command is `test -f /home/worsch/.kube/config-railiance01`.
|
||||||
|
A passing test means the file is present. It does not mean the file was read,
|
||||||
|
copied, or made the agent default.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue