feat(secrets): pluggable secret backend with OpenBao read (Phase 4)
- secret_backends.py: SecretBackend protocol, FileSecretBackend (historical), OpenBaoBackend (KV-v2 read + file fallback), build_backend() env-selected - _read_secret delegates to the active backend; default is file, so production is unchanged until CASTLE_SECRET_BACKEND=openbao - OpenBao token bootstraps from the file backend; missing/unreachable falls back - tests: file hit/miss, backend selection, unreachable + empty-token fallback Read path verified live against castle-openbao. Write path, auto-unseal-on-boot, and TLS hardening documented as remaining for full OpenBao production use.
This commit is contained in:
@@ -319,10 +319,15 @@ def resolve_env_vars(
|
||||
|
||||
|
||||
def _read_secret(name: str) -> str:
|
||||
"""Read a secret from ~/.castle/secrets/<name>. Returns placeholder if not found."""
|
||||
secret_path = SECRETS_DIR / name
|
||||
if secret_path.exists():
|
||||
return secret_path.read_text().strip()
|
||||
"""Resolve a secret via the active backend (file by default; OpenBao opt-in).
|
||||
|
||||
Returns a ``<MISSING_SECRET:...>`` placeholder if unresolved (never raises).
|
||||
"""
|
||||
from castle_core.secret_backends import build_backend
|
||||
|
||||
value = build_backend(SECRETS_DIR).read(name)
|
||||
if value is not None:
|
||||
return value
|
||||
return f"<MISSING_SECRET:{name}>"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user