Route railiance01 admin kubeconfig through an attended lane

Assistant: grok
Assistant-Session: 01a1625d-4770-76a0-8dee-7f5c514a47eb
This commit is contained in:
tegwick 2026-10-22 08:53:33 +02:00
parent 97c2e3533d
commit 537d551ef2
7 changed files with 94 additions and 4 deletions

View file

@ -214,6 +214,42 @@ entries:
exec_capable: true
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
title: Attended secrets-engine approval client reader login
risk: high

View file

@ -79,8 +79,15 @@ def denied_data_paths(policy_text: str) -> set[str]:
def to_data_path(path_template: str) -> str | None:
"""Catalog path -> KV v2 data path. `<mount>/rest` -> `<mount>/data/rest`."""
if PLACEHOLDER.search(path_template) or " " in path_template:
"""Catalog path -> KV v2 data path. `<mount>/rest` -> `<mount>/data/rest`.
An absolute filesystem path is not a KV mount.
"""
if (
PLACEHOLDER.search(path_template)
or " " in path_template
or path_template.startswith("/")
):
return None
mount, _, rest = path_template.partition("/")
return f"{mount}/data/{rest}" if rest else None

View file

@ -118,7 +118,14 @@ def build() -> tuple[str, int]:
def _to_data_path(template: str) -> str | None:
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
mount, _, rest = template.partition("/")
return f"{mount}/data/{rest}" if rest else None

View file

@ -81,6 +81,10 @@ class TestToDataPath:
"""`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
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:
def test_every_high_risk_lane_resolves_or_is_explicitly_pattern(self):

View file

@ -266,6 +266,25 @@ def test_platform_admin_login_lane_is_exact_and_non_value_bearing():
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():
catalog = load_catalog(_repo_catalog())
for lane_id in (
@ -405,7 +424,7 @@ def test_exec_capable_without_fetch_command_rejected(tmp_path):
@pytest.mark.parametrize(
"leaked",
[
"bao write x token=ghp_abcdef0123456789abcdef0123", # github token prefix
"bao write x ghp_abcdef0123456789abcdef0123", # github pat prefix
"x=AKIAIOSFODNN7EXAMPLE", # aws key id
"header=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", # jwt prefix
"val=ZmFrZXNlY3JldDEyMzQ1Njc4OWFiY2RlZmdoaWprbA", # high-entropy run

View file

@ -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) |
| `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 |
| `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 |
| `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 |

View 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.