CCR-2026-0029/0030: activity-core ops_run worker token paths (proposed)
ACTIVITY-WP-0039-T02. Add read on the two exact worker-token paths to workload-kv-read-activity-core-eso, one CCR per worker identity. Adds scripts/openbao-policy-sync.sh, a silent attended policy writer that refuses undeclared live drift and verifies on readback. Not applied; CCRs await platform-operator and activity-core-owner approval. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 150322@bnt-lap001 Assistant-Session: 16a7b788-374e-4915-a1df-fc87ffd9a5e4
This commit is contained in:
parent
915a3cacd9
commit
15184aaa01
8 changed files with 413 additions and 2 deletions
|
|
@ -22,6 +22,7 @@
|
||||||
| workplan | RPF-WP-0043 | active | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md |
|
| workplan | RPF-WP-0043 | active | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md |
|
||||||
| workplan | RPF-WP-0044 | active | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
|
| workplan | RPF-WP-0044 | active | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md |
|
||||||
| workplan | RPF-WP-0045 | active | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
| workplan | RPF-WP-0045 | active | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
||||||
|
| workplan | RPF-WP-0046 | active | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
| task | RPF-WP-0015-T01 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
| task | RPF-WP-0015-T01 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||||
| task | RPF-WP-0015-T02 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
| task | RPF-WP-0015-T02 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||||
| task | RPF-WP-0015-T03 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
| task | RPF-WP-0015-T03 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||||
|
|
@ -85,3 +86,9 @@
|
||||||
| task | RPF-WP-0045-T04 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
| task | RPF-WP-0045-T04 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
||||||
| task | RPF-WP-0045-T05 | cancel | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
| task | RPF-WP-0045-T05 | cancel | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
||||||
| task | RPF-WP-0045-T06 | wait | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
| task | RPF-WP-0045-T06 | wait | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md |
|
||||||
|
| task | RPF-WP-0046-T01 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
|
| task | RPF-WP-0046-T02 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
|
| task | RPF-WP-0046-T03 | wait | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
|
| task | RPF-WP-0046-T04 | wait | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
|
| task | RPF-WP-0046-T05 | wait | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
|
| task | RPF-WP-0046-T06 | todo | — | workplans/RPF-WP-0046-eso-database-token-renewal.md |
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
id: CCR-2026-0029
|
||||||
|
kind: credential-change-request
|
||||||
|
schema_version: 1
|
||||||
|
request_type: workload-kv-read
|
||||||
|
title: activity-core ops_run worker token for rein-aharness@railiance01 via the activity-core-eso store
|
||||||
|
status: proposed
|
||||||
|
created: '2026-09-23'
|
||||||
|
updated: '2026-09-23'
|
||||||
|
requester:
|
||||||
|
agent: activity-core
|
||||||
|
message_id: e87bdefc
|
||||||
|
reason: ACTIVITY-WP-0039-T02. secrets-engine (SECRETS-WP-0009-T03,
|
||||||
|
SECRETS-WP-0011-T04) asked that the ops_run worker tokens move from a
|
||||||
|
hand-generated value into OpenBao custody and be delivered by the
|
||||||
|
existing openbao-activity-core ClusterSecretStore.
|
||||||
|
review:
|
||||||
|
required: true
|
||||||
|
required_approvers:
|
||||||
|
- platform-operator
|
||||||
|
- activity-core-owner
|
||||||
|
comments:
|
||||||
|
- at: '2026-09-23'
|
||||||
|
reviewer: claude-code
|
||||||
|
decision: metadata_review_pending_owner_approval
|
||||||
|
comment: Both paths already hold values (founder-attended mint 2026-09-23,
|
||||||
|
ACTIVITY-WP-0039-T03). The consuming ExternalSecret
|
||||||
|
activity-core/actcore-ops-run-worker-tokens (activity-core
|
||||||
|
k8s/railiance/15-externalsecret-worker-tokens.yaml, not yet applied)
|
||||||
|
reads field token at exactly these two paths through store
|
||||||
|
openbao-activity-core. That store is on Kubernetes auth role
|
||||||
|
activity-core-eso (RPF-WP-0045). The change adds two exact data-read
|
||||||
|
paths to that role's policy. There is no wildcard, no metadata or list,
|
||||||
|
and no new role.
|
||||||
|
target:
|
||||||
|
domain: financials
|
||||||
|
tenant: activity-core
|
||||||
|
workload: activity-core-ops-run-worker-rein-aharness-railiance01
|
||||||
|
environment: production
|
||||||
|
purpose: Deliver the bearer token of ops_run queue worker rein-aharness@railiance01 to the
|
||||||
|
activity-core runtime Secret through External Secrets. The companion lane is
|
||||||
|
CCR-2026-0030 (the other worker identity, same policy change).
|
||||||
|
openbao:
|
||||||
|
mount: platform
|
||||||
|
kv_path: platform/workloads/activity-core/ops-run-workers/rein-aharness-railiance01
|
||||||
|
fields:
|
||||||
|
- token
|
||||||
|
policy_name: workload-kv-read-activity-core-eso
|
||||||
|
policy_file: openbao/policies/workload-kv-read-activity-core-eso.hcl
|
||||||
|
auth:
|
||||||
|
method: kubernetes
|
||||||
|
mount: kubernetes
|
||||||
|
role: activity-core-eso
|
||||||
|
bound_claims:
|
||||||
|
service_account_names:
|
||||||
|
- activity-core-eso
|
||||||
|
service_account_namespaces:
|
||||||
|
- activity-core
|
||||||
|
bound_claims_confirmed: true
|
||||||
|
policies:
|
||||||
|
- workload-kv-read-activity-core-eso
|
||||||
|
access_frontdoor:
|
||||||
|
type: external-secrets
|
||||||
|
catalog_id: activity-core-ops-run-worker-rein-aharness-railiance01
|
||||||
|
readiness: pending-review
|
||||||
|
resolvable: false
|
||||||
|
delivery:
|
||||||
|
surface: external-secrets
|
||||||
|
target: ExternalSecret actcore-ops-run-worker-tokens merges ACTIVITY_CORE_WORKER_TOKEN
|
||||||
|
into Secret actcore-runtime-secret in namespace activity-core.
|
||||||
|
risk:
|
||||||
|
classification: high
|
||||||
|
notes:
|
||||||
|
- Each token authenticates one queue worker identity to activity-core. A
|
||||||
|
leaked token lets its holder claim ops_run work as that worker.
|
||||||
|
- Read is limited to the two exact paths. Other ops-run-workers paths stay
|
||||||
|
unreadable.
|
||||||
|
- A read-only lane for the metered worker's path is being catalogued
|
||||||
|
separately by secrets-engine. It is not granted by this CCR.
|
||||||
|
verification:
|
||||||
|
positive:
|
||||||
|
- ExternalSecret actcore-ops-run-worker-tokens reaches SecretSynced without
|
||||||
|
printing values.
|
||||||
|
negative:
|
||||||
|
- The activity-core-eso role cannot read a sibling path under
|
||||||
|
platform/data/workloads/activity-core/ops-run-workers/.
|
||||||
|
- Metadata and list on ops-run-workers are denied.
|
||||||
|
activation_conditions:
|
||||||
|
- Policy applied with platform-admin authority through
|
||||||
|
scripts/openbao-policy-sync.sh, guarded by the prior declared digest
|
||||||
|
41f4278c3f62ff879575e62ef52071feaeb794fabd05868cb3ee40608cfd4785.
|
||||||
|
- Values provisioned directly in OpenBao (done, ACTIVITY-WP-0039-T03).
|
||||||
|
evidence: []
|
||||||
|
lifecycle:
|
||||||
|
deactivate: Remove the two path blocks from workload-kv-read-activity-core-eso
|
||||||
|
and re-apply it.
|
||||||
|
rotate: activity-core re-mints the worker token at its path. ESO syncs it
|
||||||
|
within 1h, and the queue accepts the new token per ACTIVITY_CORE_WORKERS.
|
||||||
|
compromised: Re-mint the affected worker token, force-sync the
|
||||||
|
ExternalSecret, and record blast-radius notes.
|
||||||
|
state_hub:
|
||||||
|
workplan_id: ACTIVITY-WP-0039
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
id: CCR-2026-0030
|
||||||
|
kind: credential-change-request
|
||||||
|
schema_version: 1
|
||||||
|
request_type: workload-kv-read
|
||||||
|
title: activity-core ops_run worker token for rein-aharness-metered@railiance01 via the activity-core-eso store
|
||||||
|
status: proposed
|
||||||
|
created: '2026-09-23'
|
||||||
|
updated: '2026-09-23'
|
||||||
|
requester:
|
||||||
|
agent: activity-core
|
||||||
|
message_id: e87bdefc
|
||||||
|
reason: ACTIVITY-WP-0039-T02. secrets-engine (SECRETS-WP-0009-T03,
|
||||||
|
SECRETS-WP-0011-T04) asked that the ops_run worker tokens move from a
|
||||||
|
hand-generated value into OpenBao custody and be delivered by the
|
||||||
|
existing openbao-activity-core ClusterSecretStore.
|
||||||
|
review:
|
||||||
|
required: true
|
||||||
|
required_approvers:
|
||||||
|
- platform-operator
|
||||||
|
- activity-core-owner
|
||||||
|
comments:
|
||||||
|
- at: '2026-09-23'
|
||||||
|
reviewer: claude-code
|
||||||
|
decision: metadata_review_pending_owner_approval
|
||||||
|
comment: Both paths already hold values (founder-attended mint 2026-09-23,
|
||||||
|
ACTIVITY-WP-0039-T03). The consuming ExternalSecret
|
||||||
|
activity-core/actcore-ops-run-worker-tokens (activity-core
|
||||||
|
k8s/railiance/15-externalsecret-worker-tokens.yaml, not yet applied)
|
||||||
|
reads field token at exactly these two paths through store
|
||||||
|
openbao-activity-core. That store is on Kubernetes auth role
|
||||||
|
activity-core-eso (RPF-WP-0045). The change adds two exact data-read
|
||||||
|
paths to that role's policy. There is no wildcard, no metadata or list,
|
||||||
|
and no new role.
|
||||||
|
target:
|
||||||
|
domain: financials
|
||||||
|
tenant: activity-core
|
||||||
|
workload: activity-core-ops-run-worker-rein-aharness-metered-railiance01
|
||||||
|
environment: production
|
||||||
|
purpose: Deliver the bearer token of ops_run queue worker rein-aharness-metered@railiance01 to the
|
||||||
|
activity-core runtime Secret through External Secrets. The companion lane is
|
||||||
|
CCR-2026-0029 (the other worker identity, same policy change).
|
||||||
|
openbao:
|
||||||
|
mount: platform
|
||||||
|
kv_path: platform/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01
|
||||||
|
fields:
|
||||||
|
- token
|
||||||
|
policy_name: workload-kv-read-activity-core-eso
|
||||||
|
policy_file: openbao/policies/workload-kv-read-activity-core-eso.hcl
|
||||||
|
auth:
|
||||||
|
method: kubernetes
|
||||||
|
mount: kubernetes
|
||||||
|
role: activity-core-eso
|
||||||
|
bound_claims:
|
||||||
|
service_account_names:
|
||||||
|
- activity-core-eso
|
||||||
|
service_account_namespaces:
|
||||||
|
- activity-core
|
||||||
|
bound_claims_confirmed: true
|
||||||
|
policies:
|
||||||
|
- workload-kv-read-activity-core-eso
|
||||||
|
access_frontdoor:
|
||||||
|
type: external-secrets
|
||||||
|
catalog_id: activity-core-ops-run-worker-rein-aharness-metered-railiance01
|
||||||
|
readiness: pending-review
|
||||||
|
resolvable: false
|
||||||
|
delivery:
|
||||||
|
surface: external-secrets
|
||||||
|
target: ExternalSecret actcore-ops-run-worker-tokens merges ACTIVITY_CORE_WORKER_TOKEN_METERED
|
||||||
|
into Secret actcore-runtime-secret in namespace activity-core.
|
||||||
|
risk:
|
||||||
|
classification: high
|
||||||
|
notes:
|
||||||
|
- Each token authenticates one queue worker identity to activity-core. A
|
||||||
|
leaked token lets its holder claim ops_run work as that worker.
|
||||||
|
- Read is limited to the two exact paths. Other ops-run-workers paths stay
|
||||||
|
unreadable.
|
||||||
|
- A read-only lane for the metered worker's path is being catalogued
|
||||||
|
separately by secrets-engine. It is not granted by this CCR.
|
||||||
|
verification:
|
||||||
|
positive:
|
||||||
|
- ExternalSecret actcore-ops-run-worker-tokens reaches SecretSynced without
|
||||||
|
printing values.
|
||||||
|
negative:
|
||||||
|
- The activity-core-eso role cannot read a sibling path under
|
||||||
|
platform/data/workloads/activity-core/ops-run-workers/.
|
||||||
|
- Metadata and list on ops-run-workers are denied.
|
||||||
|
activation_conditions:
|
||||||
|
- Policy applied with platform-admin authority through
|
||||||
|
scripts/openbao-policy-sync.sh, guarded by the prior declared digest
|
||||||
|
41f4278c3f62ff879575e62ef52071feaeb794fabd05868cb3ee40608cfd4785.
|
||||||
|
- Values provisioned directly in OpenBao (done, ACTIVITY-WP-0039-T03).
|
||||||
|
evidence: []
|
||||||
|
lifecycle:
|
||||||
|
deactivate: Remove the two path blocks from workload-kv-read-activity-core-eso
|
||||||
|
and re-apply it.
|
||||||
|
rotate: activity-core re-mints the worker token at its path. ESO syncs it
|
||||||
|
within 1h, and the queue accepts the new token per ACTIVITY_CORE_WORKERS.
|
||||||
|
compromised: Re-mint the affected worker token, force-sync the
|
||||||
|
ExternalSecret, and record blast-radius notes.
|
||||||
|
state_hub:
|
||||||
|
workplan_id: ACTIVITY-WP-0039
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# RPF-WP-0045: ESO delivery for ClusterSecretStore openbao-activity-core via
|
# RPF-WP-0045: ESO delivery for ClusterSecretStore openbao-activity-core via
|
||||||
# Kubernetes auth role activity-core-eso (SA activity-core/activity-core-eso).
|
# Kubernetes auth role activity-core-eso (SA activity-core/activity-core-eso).
|
||||||
# Exactly the four KV paths the four activity-core ExternalSecrets read; data
|
# Exactly the KV paths the activity-core ExternalSecrets read; data read only,
|
||||||
# read only, no metadata/list/write. ESO also needs lookup-self and revoke-self.
|
# no metadata/list/write. ESO also needs lookup-self and revoke-self.
|
||||||
|
|
||||||
path "platform/data/workloads/activity-core/llm-connect/llm-connect-provider-secrets" {
|
path "platform/data/workloads/activity-core/llm-connect/llm-connect-provider-secrets" {
|
||||||
capabilities = ["read"]
|
capabilities = ["read"]
|
||||||
|
|
@ -19,6 +19,16 @@ path "platform/data/workloads/railiance/backup/offsite-lane" {
|
||||||
capabilities = ["read"]
|
capabilities = ["read"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# CCR-2026-0029 and CCR-2026-0030 / ACTIVITY-WP-0039-T02: ops_run queue worker tokens, one path
|
||||||
|
# per worker identity, read by ExternalSecret actcore-ops-run-worker-tokens.
|
||||||
|
path "platform/data/workloads/activity-core/ops-run-workers/rein-aharness-railiance01" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}
|
||||||
|
|
||||||
|
path "platform/data/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}
|
||||||
|
|
||||||
path "auth/token/lookup-self" {
|
path "auth/token/lookup-self" {
|
||||||
capabilities = ["read"]
|
capabilities = ["read"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
scripts/openbao-policy-sync.sh
Executable file
4
scripts/openbao-policy-sync.sh
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Silent child for the governed attended login; results go to the receipt only.
|
||||||
|
set -euo pipefail
|
||||||
|
exec python3 "$(dirname "$0")/openbao_policy_sync.py" "$@" >/dev/null 2>&1
|
||||||
116
scripts/openbao_policy_sync.py
Executable file
116
scripts/openbao_policy_sync.py
Executable file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Silent contained write of one declared ACL policy, guarded against drift.
|
||||||
|
|
||||||
|
Writes openbao/policies/<name>.hcl to sys/policy/<name> only when the live
|
||||||
|
rules still equal the previously declared version (--expect-live-sha256). If
|
||||||
|
the live rules already equal the file, nothing is written. Any other live
|
||||||
|
state is refused, so an undeclared live edit is never overwritten. Readback
|
||||||
|
must match. One non-secret receipt; no output.
|
||||||
|
|
||||||
|
python3 scripts/openbao-attended-exec.py -- scripts/openbao-policy-sync.sh \\
|
||||||
|
--policy <name> --expect-live-sha256 <sha> --receipt <absolute file>
|
||||||
|
"""
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
POLICY_DIR = Path(__file__).resolve().parents[1] / 'openbao/policies'
|
||||||
|
NAME = re.compile(r'^[a-z0-9][a-z0-9-]{1,80}$')
|
||||||
|
|
||||||
|
|
||||||
|
class Refused(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def digest(text):
|
||||||
|
return hashlib.sha256(text.strip().encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def require_attended():
|
||||||
|
if Path.home().parent.name != '.warden-attended-login' or os.getenv('BAO_TOKEN') or os.getenv('VAULT_TOKEN'):
|
||||||
|
raise Refused('attended_envelope_required')
|
||||||
|
|
||||||
|
|
||||||
|
def read_live(name):
|
||||||
|
result = subprocess.run(['bao', 'read', '-format=json', 'sys/policy/' + name],
|
||||||
|
capture_output=True, check=True, timeout=30)
|
||||||
|
return json.loads(result.stdout)['data']['rules']
|
||||||
|
|
||||||
|
|
||||||
|
def write_live(name, path):
|
||||||
|
subprocess.run(['bao', 'policy', 'write', name, str(path)],
|
||||||
|
capture_output=True, check=True, timeout=30)
|
||||||
|
|
||||||
|
|
||||||
|
def sync(name, expect, read=read_live, write=write_live):
|
||||||
|
path = POLICY_DIR / (name + '.hcl')
|
||||||
|
declared = digest(path.read_text(encoding='utf-8'))
|
||||||
|
live = digest(read(name))
|
||||||
|
if live == declared:
|
||||||
|
return {'status': 'already_current', 'changed': False, 'live_sha256': live}
|
||||||
|
if live != expect:
|
||||||
|
raise Refused('live_policy_drifted')
|
||||||
|
write(name, path)
|
||||||
|
after = digest(read(name))
|
||||||
|
if after != declared:
|
||||||
|
raise Refused('readback_mismatch')
|
||||||
|
return {'status': 'applied', 'changed': True, 'previous_sha256': live, 'live_sha256': after}
|
||||||
|
|
||||||
|
|
||||||
|
def write_receipt(path, name, **body):
|
||||||
|
fd = os.open(Path(path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||||
|
body = {
|
||||||
|
'schema': 'railiance-platform.openbao-policy-sync.v1',
|
||||||
|
'observed_at': datetime.now(timezone.utc).isoformat(),
|
||||||
|
'policy_name': name, 'credential_values_emitted': False, **body,
|
||||||
|
}
|
||||||
|
with os.fdopen(fd, 'w', encoding='utf-8') as handle:
|
||||||
|
json.dump(body, handle, indent=2, sort_keys=True)
|
||||||
|
handle.write('\n')
|
||||||
|
|
||||||
|
|
||||||
|
def classify(error):
|
||||||
|
if isinstance(error, Refused):
|
||||||
|
return str(error)
|
||||||
|
if isinstance(error, subprocess.CalledProcessError):
|
||||||
|
return 'bao_write_failed' if 'policy' in error.cmd else 'bao_read_failed'
|
||||||
|
return 'contained_operation_failed'
|
||||||
|
|
||||||
|
|
||||||
|
def parse(argv):
|
||||||
|
opts, args = {}, list(argv)
|
||||||
|
while args:
|
||||||
|
if args[0] in ('--policy', '--expect-live-sha256', '--receipt') and len(args) > 1:
|
||||||
|
opts[args[0]] = args[1]
|
||||||
|
args = args[2:]
|
||||||
|
else:
|
||||||
|
raise SystemExit(2)
|
||||||
|
if set(opts) != {'--policy', '--expect-live-sha256', '--receipt'} or not NAME.match(opts['--policy']):
|
||||||
|
raise SystemExit(2)
|
||||||
|
if not (POLICY_DIR / (opts['--policy'] + '.hcl')).is_file():
|
||||||
|
raise SystemExit(2)
|
||||||
|
return opts['--policy'], opts['--expect-live-sha256'], opts['--receipt']
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
name, expect, receipt = parse(argv)
|
||||||
|
try:
|
||||||
|
require_attended()
|
||||||
|
result = sync(name, expect)
|
||||||
|
write_receipt(receipt, name, declared_sha256=digest((POLICY_DIR / (name + '.hcl')).read_text()), **result)
|
||||||
|
return 0
|
||||||
|
except Exception as error:
|
||||||
|
try:
|
||||||
|
write_receipt(receipt, name, status=classify(error), changed=False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
65
tests/test_openbao_policy_sync.py
Normal file
65
tests/test_openbao_policy_sync.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
'sync', Path(__file__).resolve().parents[1] / 'scripts/openbao_policy_sync.py')
|
||||||
|
m = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(m)
|
||||||
|
|
||||||
|
NAME = 'workload-kv-read-activity-core-eso'
|
||||||
|
DECLARED = (m.POLICY_DIR / (NAME + '.hcl')).read_text()
|
||||||
|
|
||||||
|
|
||||||
|
def store(initial):
|
||||||
|
live = {'rules': initial}
|
||||||
|
writes = []
|
||||||
|
|
||||||
|
def write(name, path):
|
||||||
|
writes.append(name)
|
||||||
|
live['rules'] = Path(path).read_text()
|
||||||
|
return live, writes, (lambda name: live['rules']), write
|
||||||
|
|
||||||
|
|
||||||
|
def test_applies_when_live_is_the_expected_prior_version():
|
||||||
|
live, writes, read, write = store('path "old" {}')
|
||||||
|
result = m.sync(NAME, m.digest('path "old" {}'), read, write)
|
||||||
|
assert result['status'] == 'applied' and writes == [NAME]
|
||||||
|
assert m.digest(live['rules']) == m.digest(DECLARED)
|
||||||
|
|
||||||
|
|
||||||
|
def test_already_current_writes_nothing():
|
||||||
|
_, writes, read, write = store(DECLARED)
|
||||||
|
assert m.sync(NAME, 'unused', read, write)['status'] == 'already_current'
|
||||||
|
assert writes == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_refuses_undeclared_drift():
|
||||||
|
_, writes, read, write = store('path "someone-else" {}')
|
||||||
|
with pytest.raises(m.Refused, match='live_policy_drifted'):
|
||||||
|
m.sync(NAME, m.digest('path "old" {}'), read, write)
|
||||||
|
assert writes == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_readback_mismatch_is_refused():
|
||||||
|
live = {'rules': 'path "old" {}'}
|
||||||
|
with pytest.raises(m.Refused, match='readback_mismatch'):
|
||||||
|
m.sync(NAME, m.digest('path "old" {}'), lambda n: live['rules'], lambda n, p: None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_new_paths_are_exact_and_read_only():
|
||||||
|
for worker in ('rein-aharness-railiance01', 'rein-aharness-metered-railiance01'):
|
||||||
|
block = 'path "platform/data/workloads/activity-core/ops-run-workers/%s" {\n capabilities = ["read"]\n}' % worker
|
||||||
|
assert block in DECLARED
|
||||||
|
assert 'ops-run-workers/*' not in DECLARED and '"list"' not in DECLARED
|
||||||
|
|
||||||
|
|
||||||
|
def test_refuses_outside_envelope_and_bad_args(tmp_path, monkeypatch):
|
||||||
|
monkeypatch.setenv('HOME', str(tmp_path))
|
||||||
|
receipt = tmp_path / 'r.json'
|
||||||
|
assert m.main(['--policy', NAME, '--expect-live-sha256', 'x', '--receipt', str(receipt)]) == 1
|
||||||
|
assert json.loads(receipt.read_text())['status'] == 'attended_envelope_required'
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
m.parse(['--policy', '../etc', '--expect-live-sha256', 'x', '--receipt', 'r'])
|
||||||
|
|
@ -10,6 +10,7 @@ topic_slug: railiance
|
||||||
created: "2026-09-23"
|
created: "2026-09-23"
|
||||||
updated: "2026-09-23"
|
updated: "2026-09-23"
|
||||||
related: [RPF-WP-0045, RPF-WP-0037, RPF-WP-0036]
|
related: [RPF-WP-0045, RPF-WP-0037, RPF-WP-0036]
|
||||||
|
state_hub_workstream_id: "cdb309e0-adb4-58aa-9c98-5ea270780e8d"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Problem
|
## Problem
|
||||||
|
|
@ -56,6 +57,7 @@ Design:
|
||||||
id: RPF-WP-0046-T01
|
id: RPF-WP-0046-T01
|
||||||
status: done
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "25592a83-d2e5-5aa2-b23e-5d563a621f19"
|
||||||
```
|
```
|
||||||
|
|
||||||
`argocd/platform-addons/eso-token-renewer/` holds `renew.py`, the
|
`argocd/platform-addons/eso-token-renewer/` holds `renew.py`, the
|
||||||
|
|
@ -73,6 +75,7 @@ AppProject is applied by hand, so the git change alone is not live. Tests:
|
||||||
id: RPF-WP-0046-T02
|
id: RPF-WP-0046-T02
|
||||||
status: done
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "a318792f-82ba-568b-8633-fba3513aa978"
|
||||||
```
|
```
|
||||||
|
|
||||||
The script is `scripts/openbao-eso-db-token-periodic-attended.sh`. It is
|
The script is `scripts/openbao-eso-db-token-periodic-attended.sh`. It is
|
||||||
|
|
@ -87,6 +90,7 @@ parity with the CronJob mounts is tested.
|
||||||
id: RPF-WP-0046-T03
|
id: RPF-WP-0046-T03
|
||||||
status: wait
|
status: wait
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "211bc548-34af-5f3a-ab2c-bf29d33f97ad"
|
||||||
```
|
```
|
||||||
|
|
||||||
This step is `ADMINISTER @ realm:kubernetes/railiance01`, `activation=APPROVED`,
|
This step is `ADMINISTER @ realm:kubernetes/railiance01`, `activation=APPROVED`,
|
||||||
|
|
@ -109,6 +113,7 @@ ExternalSecrets are `SecretSynced`.
|
||||||
id: RPF-WP-0046-T04
|
id: RPF-WP-0046-T04
|
||||||
status: wait
|
status: wait
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "28726b1b-3777-5945-859b-9e563b12fee6"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Apply `argocd/railiance01/bootstrap/02-railiance-platform-addons-project.yaml`
|
1. Apply `argocd/railiance01/bootstrap/02-railiance-platform-addons-project.yaml`
|
||||||
|
|
@ -125,6 +130,7 @@ priority: high
|
||||||
id: RPF-WP-0046-T05
|
id: RPF-WP-0046-T05
|
||||||
status: wait
|
status: wait
|
||||||
priority: high
|
priority: high
|
||||||
|
state_hub_task_id: "acf49bee-e40a-533c-b1f0-4152c989211d"
|
||||||
```
|
```
|
||||||
|
|
||||||
The replaced 768h tokens still expire around 2026-10-25T17:00Z, and they
|
The replaced 768h tokens still expire around 2026-10-25T17:00Z, and they
|
||||||
|
|
@ -144,6 +150,7 @@ ready.
|
||||||
id: RPF-WP-0046-T06
|
id: RPF-WP-0046-T06
|
||||||
status: todo
|
status: todo
|
||||||
priority: medium
|
priority: medium
|
||||||
|
state_hub_task_id: "2f528e56-ebf0-547e-8d4f-3cb34c348d46"
|
||||||
```
|
```
|
||||||
|
|
||||||
- rapp-postgres: the 768h mint scripts (`apply-*-eso-token*.sh`) would undo
|
- rapp-postgres: the 768h mint scripts (`apply-*-eso-token*.sh`) would undo
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue