572 lines
21 KiB
Python
572 lines
21 KiB
Python
"""Contracts for Hermes' Vault-only runtime access boundary."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
import sys
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
ROOT = Path(__file__).parents[2]
|
|
HERMES = ROOT / "services" / "hermes"
|
|
SCRIPTS = HERMES / "scripts"
|
|
SCM_SCRIPTS = HERMES / "scm-common" / "scripts"
|
|
if str(SCRIPTS) not in sys.path:
|
|
sys.path.insert(0, str(SCRIPTS))
|
|
if str(SCM_SCRIPTS) not in sys.path:
|
|
sys.path.insert(0, str(SCM_SCRIPTS))
|
|
|
|
|
|
def _load(name: str):
|
|
root = SCM_SCRIPTS if name == "gitea_api" else SCRIPTS
|
|
spec = importlib.util.spec_from_file_location(name, root / f"{name}.py")
|
|
assert spec and spec.loader
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[spec.name] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def test_gitea_api_builds_runtime_authenticated_same_origin_requests():
|
|
gitea_api = _load("gitea_api")
|
|
|
|
request = gitea_api.build_request(
|
|
"POST",
|
|
"/api/v1/repos/titan/cassandra/pulls",
|
|
base_url="https://scm.bstein.dev",
|
|
token="runtime-only-value",
|
|
data={
|
|
"base": "main",
|
|
"body": "Ready for review.",
|
|
"head": "hermes/repair",
|
|
"title": "WIP: Repair semantic review findings",
|
|
},
|
|
)
|
|
|
|
assert isinstance(request, urllib.request.Request)
|
|
assert request.full_url == "https://scm.bstein.dev/api/v1/repos/titan/cassandra/pulls"
|
|
assert request.method == "POST"
|
|
assert request.get_header("Authorization") == "token runtime-only-value"
|
|
assert json.loads(request.data)["title"].startswith("WIP: ")
|
|
assert "draft" not in json.loads(request.data)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"path",
|
|
[
|
|
"https://evil.example/api/v1/repos/titan/cassandra",
|
|
"/repos/atlas/cassandra",
|
|
"/api/v1/repos/titan/cassandra#fragment",
|
|
],
|
|
)
|
|
def test_gitea_api_rejects_foreign_or_non_api_targets(path: str):
|
|
gitea_api = _load("gitea_api")
|
|
|
|
with pytest.raises(ValueError):
|
|
gitea_api.api_url("https://scm.bstein.dev", path)
|
|
|
|
|
|
@pytest.mark.parametrize("method", ["DELETE", "PUT", "OPTIONS"])
|
|
def test_gitea_api_rejects_unavailable_methods(method: str):
|
|
gitea_api = _load("gitea_api")
|
|
|
|
with pytest.raises(gitea_api.PolicyError):
|
|
gitea_api.authorize_request(
|
|
method, "/api/v1/repos/titan/cassandra/pulls/1", None
|
|
)
|
|
|
|
|
|
def test_agent_runtime_stage_keeps_credentials_in_memory(tmp_path: Path, monkeypatch):
|
|
stage = _load("stage_runtime_access")
|
|
vault = tmp_path / "vault"
|
|
runtime = tmp_path / "runtime"
|
|
home = tmp_path / "home"
|
|
claude_oauth_access = tmp_path / "claude-oauth-access"
|
|
oauth2_config = tmp_path / "oauth2-config"
|
|
vault.mkdir()
|
|
(home / ".claude").mkdir(parents=True)
|
|
(home / ".claude" / "backups").mkdir()
|
|
(home / ".codex" / "skills").mkdir(parents=True)
|
|
(home / ".codex" / "sessions").mkdir()
|
|
(home / ".claude" / "settings.json").write_text("{}\n", encoding="utf-8")
|
|
(home / ".claude" / "backups" / "credentials.json").write_text(
|
|
"do-not-link\n", encoding="utf-8"
|
|
)
|
|
values = {
|
|
"agent-api-key": "agent-key",
|
|
"claude-oauth-token": "long-lived-claude-token",
|
|
"execution-pool-key": "e" * 64,
|
|
"chat-relay-key": "relay-key",
|
|
# gitea-token/username are present in Vault but the agent staging must
|
|
# deliberately not copy them (the least-authority broker holds Gitea
|
|
# access); the assertions below prove they never reach the runtime.
|
|
"gitea-token": "gitea-key",
|
|
"gitea-username": "hermes-automation",
|
|
"jenkins-image-build-token": "job-scoped-token",
|
|
"node-ssh-private-key": "private-key",
|
|
"node-ssh-config": "host-config",
|
|
"node-ssh-known-hosts": "known-hosts",
|
|
"oidc-config": "client_id = \"hermes-agent\"",
|
|
"claude-credentials": json.dumps(
|
|
{"claudeAiOauth": {"refreshToken": "claude-refresh"}}
|
|
),
|
|
"codex-auth": json.dumps({"tokens": {"refresh_token": "codex-refresh"}}),
|
|
}
|
|
for name, value in values.items():
|
|
(vault / name).write_text(value + "\n", encoding="utf-8")
|
|
monkeypatch.setattr(stage, "VAULT_ROOT", vault)
|
|
monkeypatch.setattr(stage, "RUNTIME_ROOT", runtime)
|
|
monkeypatch.setattr(stage, "PERSISTENT_HOME", home)
|
|
monkeypatch.setattr(stage, "CLAUDE_OAUTH_ACCESS_ROOT", claude_oauth_access)
|
|
monkeypatch.setattr(stage, "OAUTH2_CONFIG_ROOT", oauth2_config)
|
|
monkeypatch.setattr(stage.os, "chown", lambda *_args: None)
|
|
monkeypatch.setattr(stage.os, "fchown", lambda *_args: None)
|
|
|
|
stage.stage_agent()
|
|
|
|
assert (runtime / "claude/.credentials.json").stat().st_mode & 0o777 == 0o600
|
|
assert (claude_oauth_access / "token").read_text(encoding="utf-8").strip() == (
|
|
"long-lived-claude-token"
|
|
)
|
|
assert (claude_oauth_access / "token").stat().st_mode & 0o777 == 0o600
|
|
assert (runtime / "codex/auth.json").stat().st_mode & 0o777 == 0o600
|
|
assert (runtime / "jenkins-image-build-token").stat().st_mode & 0o777 == 0o600
|
|
assert (oauth2_config / "oidc-config").stat().st_mode & 0o777 == 0o600
|
|
assert (runtime / "claude/settings.json").is_symlink()
|
|
assert (runtime / "codex/skills").is_symlink()
|
|
assert not (runtime / "claude/backups").exists()
|
|
assert not (runtime / "codex/sessions").exists()
|
|
assert not (runtime / "gitea-token").exists()
|
|
assert not (runtime / "gitea-username").exists()
|
|
ssh_config = (runtime / "node-ssh-config").read_text(encoding="utf-8")
|
|
assert ssh_config.startswith("Host titan-*\n User hermes-agent\n")
|
|
auth = json.loads((runtime / "hermes-auth.json").read_text(encoding="utf-8"))
|
|
assert auth == {"version": 1, "providers": {}, "credential_pool": {}}
|
|
|
|
|
|
def test_execution_worker_and_mediator_separate_claude_token_and_hmac(
|
|
tmp_path: Path, monkeypatch
|
|
):
|
|
stage = _load("stage_runtime_access")
|
|
vault = tmp_path / "vault"
|
|
runtime = tmp_path / "runtime"
|
|
worker = tmp_path / "worker"
|
|
provider_access = tmp_path / "provider-access"
|
|
claude_oauth_access = tmp_path / "claude-oauth-access"
|
|
pool_access = tmp_path / "pool-access"
|
|
vault.mkdir()
|
|
(vault / "claude-oauth-token").write_text("claude-setup-token")
|
|
monkeypatch.setattr(stage, "VAULT_ROOT", vault)
|
|
monkeypatch.setattr(stage, "RUNTIME_ROOT", runtime)
|
|
monkeypatch.setattr(stage, "WORKER_ROOT", worker)
|
|
monkeypatch.setattr(stage, "PROVIDER_ACCESS_ROOT", provider_access)
|
|
monkeypatch.setattr(stage, "CLAUDE_OAUTH_ACCESS_ROOT", claude_oauth_access)
|
|
monkeypatch.setattr(stage, "POOL_ACCESS_ROOT", pool_access)
|
|
monkeypatch.setenv("HERMES_WORKER_ORDINAL", "1")
|
|
monkeypatch.setattr(stage.os, "chown", lambda *_args: None)
|
|
monkeypatch.setattr(stage.os, "fchown", lambda *_args: None)
|
|
|
|
stage.stage_execution_worker()
|
|
|
|
assert not pool_access.exists()
|
|
assert not (runtime / "execution-pool-key").exists()
|
|
assert not (runtime / "codex/sessions").exists()
|
|
assert not (runtime / "claude/projects").exists()
|
|
assert (claude_oauth_access / "token").read_text().strip() == (
|
|
"claude-setup-token"
|
|
)
|
|
assert not (provider_access / "claude/.credentials.json").exists()
|
|
stage.stage_execution_worker()
|
|
assert (claude_oauth_access / "token").read_text().strip() == (
|
|
"claude-setup-token"
|
|
)
|
|
|
|
master = "e" * 64
|
|
(vault / "execution-pool-key").write_text(master)
|
|
stage.stage_execution_mediator()
|
|
expected = hmac.new(
|
|
master.encode(), b"hermes-execution-pool-v2:worker:1", hashlib.sha256
|
|
).hexdigest()
|
|
assert (pool_access / "execution-pool-key").read_text().strip() == expected
|
|
|
|
|
|
def test_execution_worker_fails_closed_without_channel_credential(
|
|
tmp_path: Path, monkeypatch
|
|
):
|
|
stage = _load("stage_runtime_access")
|
|
vault = tmp_path / "vault"
|
|
vault.mkdir()
|
|
monkeypatch.setattr(stage, "VAULT_ROOT", vault)
|
|
monkeypatch.setattr(stage, "RUNTIME_ROOT", tmp_path / "runtime")
|
|
monkeypatch.setattr(stage, "WORKER_ROOT", tmp_path / "worker")
|
|
monkeypatch.setattr(stage, "PROVIDER_ACCESS_ROOT", tmp_path / "provider-access")
|
|
monkeypatch.setattr(
|
|
stage, "CLAUDE_OAUTH_ACCESS_ROOT", tmp_path / "claude-oauth-access"
|
|
)
|
|
monkeypatch.setenv("HERMES_WORKER_ORDINAL", "0")
|
|
monkeypatch.setattr(stage.os, "chown", lambda *_args: None)
|
|
monkeypatch.setattr(stage.os, "fchown", lambda *_args: None)
|
|
|
|
with pytest.raises(FileNotFoundError):
|
|
stage.stage_execution_worker()
|
|
|
|
|
|
def test_execution_worker_rejects_durable_credential_symlink(
|
|
tmp_path: Path, monkeypatch
|
|
):
|
|
stage = _load("stage_runtime_access")
|
|
provider = tmp_path / "provider"
|
|
provider.mkdir()
|
|
(provider / "claude").symlink_to(tmp_path, target_is_directory=True)
|
|
monkeypatch.setattr(stage, "WORKER_ROOT", tmp_path / "worker")
|
|
monkeypatch.setattr(stage, "PROVIDER_ACCESS_ROOT", provider)
|
|
monkeypatch.setenv("HERMES_WORKER_ORDINAL", "0")
|
|
monkeypatch.setattr(stage.os, "chown", lambda *_args: None)
|
|
with pytest.raises(RuntimeError, match="symlink"):
|
|
stage.stage_execution_worker()
|
|
|
|
|
|
def test_invalid_runtime_json_is_removed(tmp_path: Path, monkeypatch):
|
|
stage = _load("stage_runtime_access")
|
|
vault = tmp_path / "vault"
|
|
runtime = tmp_path / "runtime"
|
|
vault.mkdir()
|
|
runtime.mkdir()
|
|
(vault / "credential").write_text("not-json\n", encoding="utf-8")
|
|
monkeypatch.setattr(stage, "VAULT_ROOT", vault)
|
|
monkeypatch.setattr(stage.os, "chown", lambda *_args: None)
|
|
monkeypatch.setattr(stage.os, "fchown", lambda *_args: None)
|
|
destination = runtime / "credential.json"
|
|
|
|
try:
|
|
stage._validated_json("credential", destination, ("token",))
|
|
except RuntimeError:
|
|
pass
|
|
else:
|
|
raise AssertionError("invalid credential JSON should fail staging")
|
|
|
|
assert not destination.exists()
|
|
|
|
|
|
def test_runtime_refresh_sync_uses_cas_and_preserves_other_fields(
|
|
tmp_path: Path, monkeypatch
|
|
):
|
|
sync = _load("sync_runtime_credentials")
|
|
claude = tmp_path / "claude.json"
|
|
codex = tmp_path / "codex.json"
|
|
claude.write_text(
|
|
json.dumps({"claudeAiOauth": {"refreshToken": "new-claude"}}),
|
|
encoding="utf-8",
|
|
)
|
|
codex.write_text(
|
|
json.dumps({"tokens": {"refresh_token": "new-codex"}}),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setattr(
|
|
sync,
|
|
"CREDENTIALS",
|
|
{
|
|
"claude_credentials_json": (
|
|
claude,
|
|
("claudeAiOauth", "refreshToken"),
|
|
),
|
|
"codex_auth_json": (codex, ("tokens", "refresh_token")),
|
|
},
|
|
)
|
|
writes = []
|
|
|
|
def request(method, path, payload=None, *, token=""):
|
|
assert token == "vault-token"
|
|
if method == "GET":
|
|
return {
|
|
"data": {
|
|
"data": {
|
|
"claude_credentials_json": "old",
|
|
"codex_auth_json": "old",
|
|
"agent_api_key": "preserve-me",
|
|
},
|
|
"metadata": {"version": 7},
|
|
}
|
|
}
|
|
writes.append((path, payload))
|
|
return {}
|
|
|
|
monkeypatch.setattr(sync, "_request", request)
|
|
|
|
assert sync.sync_once("vault-token") == (
|
|
["claude_credentials_json", "codex_auth_json"],
|
|
[],
|
|
)
|
|
assert writes[0][1]["options"] == {"cas": 7}
|
|
assert writes[0][1]["data"]["agent_api_key"] == "preserve-me"
|
|
|
|
|
|
def test_runtime_refresh_sync_restores_incomplete_credentials_from_vault(
|
|
tmp_path: Path, monkeypatch
|
|
):
|
|
sync = _load("sync_runtime_credentials")
|
|
claude = tmp_path / "claude.json"
|
|
claude.write_text(
|
|
json.dumps(
|
|
{"claudeAiOauth": {"accessToken": "", "refreshToken": ""}}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
vault_value = json.dumps(
|
|
{
|
|
"claudeAiOauth": {
|
|
"accessToken": "vault-access",
|
|
"refreshToken": "vault-refresh",
|
|
}
|
|
}
|
|
)
|
|
monkeypatch.setattr(
|
|
sync,
|
|
"CREDENTIALS",
|
|
{
|
|
"claude_credentials_json": (
|
|
claude,
|
|
("claudeAiOauth", "refreshToken"),
|
|
)
|
|
},
|
|
)
|
|
|
|
def request(method, _path, payload=None, *, token=""):
|
|
assert method == "GET"
|
|
assert payload is None
|
|
assert token == "vault-token"
|
|
return {
|
|
"data": {
|
|
"data": {"claude_credentials_json": vault_value},
|
|
"metadata": {"version": 8},
|
|
}
|
|
}
|
|
|
|
monkeypatch.setattr(sync, "_request", request)
|
|
|
|
assert sync.sync_once("vault-token") == ([], ["claude_credentials_json"])
|
|
restored = json.loads(claude.read_text(encoding="utf-8"))
|
|
assert restored["claudeAiOauth"]["refreshToken"] == "vault-refresh"
|
|
assert claude.stat().st_mode & 0o077 == 0
|
|
|
|
|
|
def test_claude_clients_use_the_vault_staged_long_lived_token():
|
|
wrapper = (SCRIPTS / "claude").read_text(encoding="utf-8")
|
|
oauth_exec = (SCRIPTS / "claude_oauth_exec").read_text(encoding="utf-8")
|
|
deployment = yaml.safe_load((HERMES / "agent-deployment.yaml").read_text())
|
|
worker = yaml.safe_load(
|
|
(HERMES / "execution-worker-statefulset.yaml").read_text()
|
|
)
|
|
runner = next(
|
|
item
|
|
for item in deployment["spec"]["template"]["spec"]["containers"]
|
|
if item["name"] == "cli-lane-runner"
|
|
)
|
|
environment = {item["name"]: item["value"] for item in runner["env"]}
|
|
|
|
assert "/opt/coordinator/claude_oauth_exec" in wrapper
|
|
assert 'CLAUDE_CODE_OAUTH_TOKEN_FILE:-/claude-oauth-access/token' in oauth_exec
|
|
assert 'export CLAUDE_CODE_OAUTH_TOKEN' in oauth_exec
|
|
assert environment["HERMES_CLAUDE_BIN"] == "/opt/coordinator/claude"
|
|
worker_pod = worker["spec"]["template"]["spec"]
|
|
worker_annotations = worker["spec"]["template"]["metadata"]["annotations"]
|
|
assert worker_annotations[
|
|
"vault.hashicorp.com/agent-inject-secret-claude-oauth-token"
|
|
] == "kv/data/atlas/hermes/agent-tokens"
|
|
assert not any("claude-credentials" in key for key in worker_annotations)
|
|
worker_container = worker_pod["containers"][0]
|
|
worker_environment = {
|
|
item["name"]: item["value"]
|
|
for item in worker_container["env"]
|
|
if "value" in item
|
|
}
|
|
assert worker_environment["HERMES_CLAUDE_BIN"] == (
|
|
"/opt/coordinator/claude_oauth_exec"
|
|
)
|
|
assert worker_environment["HERMES_CLAUDE_NATIVE_BIN"] == (
|
|
"/worker-data/tools/bin/claude"
|
|
)
|
|
assert worker_environment["HERMES_EXECUTION_DISABLED_PROVIDER"] == "codex"
|
|
assert not any("codex-auth" in key for key in worker_annotations)
|
|
oauth_volume = next(
|
|
item for item in worker_pod["volumes"] if item["name"] == "claude-oauth-access"
|
|
)
|
|
assert oauth_volume["emptyDir"] == {"medium": "Memory", "sizeLimit": "1Mi"}
|
|
|
|
|
|
def test_subprocess_patches_strip_and_redact_runtime_credentials(tmp_path: Path):
|
|
boundary = _load("patch_subprocess_secret_boundary")
|
|
process = _load("patch_process_output_redaction")
|
|
local_source = tmp_path / "local.py"
|
|
local_output = tmp_path / "patched-local.py"
|
|
local_source.write_text("prefix\n" + boundary.BEFORE + "suffix\n", encoding="utf-8")
|
|
boundary.patch(local_source, local_output)
|
|
patched_local = local_output.read_text(encoding="utf-8")
|
|
for name in (
|
|
"API_SERVER_KEY",
|
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
"GITEA_TOKEN",
|
|
"HERMES_IMAGE_BROKER_KEY",
|
|
):
|
|
assert f'"{name}"' in patched_local
|
|
|
|
process_source = tmp_path / "process.py"
|
|
process_output = tmp_path / "patched-process.py"
|
|
process_source.write_text(
|
|
"prefix\n" + process.BEFORE + "suffix\n", encoding="utf-8"
|
|
)
|
|
process.patch(process_source, process_output)
|
|
assert "code_file=False" in process_output.read_text(encoding="utf-8")
|
|
|
|
|
|
def test_manifests_never_seed_access_material_into_persistent_env():
|
|
agent = yaml.safe_load((HERMES / "agent-deployment.yaml").read_text())
|
|
chat = yaml.safe_load((HERMES / "chat-statefulset.yaml").read_text())
|
|
triage = yaml.safe_load((HERMES / "deployment.yaml").read_text())
|
|
for workload in (agent, chat, triage):
|
|
pod = workload["spec"]["template"]["spec"]
|
|
runtime = next(item for item in pod["volumes"] if item["name"] == "runtime-access")
|
|
assert runtime["emptyDir"]["medium"] == "Memory"
|
|
assert not any(item["name"] == "provider-auth" for item in pod["volumes"])
|
|
init = next(item for item in pod["initContainers"] if item["name"] == "init-config")
|
|
command = init["command"][2]
|
|
for key in (
|
|
"ANTHROPIC_API_KEY",
|
|
"API_SERVER_KEY",
|
|
"CLAUDE_API_KEY",
|
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
"GITEA_TOKEN",
|
|
"HERMES_IMAGE_BROKER_KEY",
|
|
"OPENAI_API_KEY",
|
|
):
|
|
assert f"printf '{key}=" not in command
|
|
assert f"upsert_env {key}" not in command
|
|
|
|
annotations = agent["spec"]["template"]["metadata"]["annotations"]
|
|
assert annotations["vault.hashicorp.com/agent-inject-containers"] == (
|
|
"stage-runtime-access"
|
|
)
|
|
pod = agent["spec"]["template"]["spec"]
|
|
token_containers = {
|
|
item["name"]
|
|
for item in pod["containers"]
|
|
if any(
|
|
mount["name"] == "claude-oauth-access"
|
|
for mount in item.get("volumeMounts", [])
|
|
)
|
|
}
|
|
assert token_containers == {
|
|
"hermes",
|
|
"terminal",
|
|
"cli-lane-runner",
|
|
"model-steward",
|
|
"claude-broker",
|
|
"ai-usage-exporter",
|
|
}
|
|
token_init_containers = {
|
|
item["name"]
|
|
for item in pod["initContainers"]
|
|
if any(
|
|
mount["name"] == "claude-oauth-access"
|
|
for mount in item.get("volumeMounts", [])
|
|
)
|
|
}
|
|
assert token_init_containers == {
|
|
"stage-runtime-access",
|
|
"bootstrap-coordinator",
|
|
"configure-agent-clients",
|
|
}
|
|
agent_runtime = next(
|
|
item
|
|
for item in agent["spec"]["template"]["spec"]["volumes"]
|
|
if item["name"] == "runtime-access"
|
|
)
|
|
assert agent_runtime["emptyDir"] == {
|
|
"medium": "Memory",
|
|
"sizeLimit": "128Mi",
|
|
}
|
|
assert "vault.hashicorp.com/agent-inject-secret-anthropic-token" not in annotations
|
|
assert annotations["vault.hashicorp.com/agent-inject-secret-claude-credentials"] == (
|
|
"kv/data/atlas/hermes/agent-tokens"
|
|
)
|
|
assert annotations["vault.hashicorp.com/agent-inject-secret-claude-oauth-token"] == (
|
|
"kv/data/atlas/hermes/agent-tokens"
|
|
)
|
|
assert annotations["vault.hashicorp.com/agent-inject-secret-codex-auth"] == (
|
|
"kv/data/atlas/hermes/agent-tokens"
|
|
)
|
|
credential_sync = next(
|
|
item
|
|
for item in agent["spec"]["template"]["spec"]["containers"]
|
|
if item["name"] == "credential-sync"
|
|
)
|
|
assert {
|
|
(item["mountPath"], item.get("subPath"))
|
|
for item in credential_sync["volumeMounts"]
|
|
if item["name"] == "runtime-access"
|
|
} == {
|
|
("/runtime-access/claude", "claude"),
|
|
("/runtime-access/codex", "codex"),
|
|
}
|
|
triage_annotations = triage["spec"]["template"]["metadata"]["annotations"]
|
|
assert "vault.hashicorp.com/agent-inject-secret-anthropic-token" not in (
|
|
triage_annotations
|
|
)
|
|
assert triage_annotations[
|
|
"vault.hashicorp.com/agent-inject-secret-triage-api-key"
|
|
] == "kv/data/atlas/hermes/triage-api"
|
|
gitea_api = (
|
|
ROOT / "services/hermes/scm-common/scripts/gitea_api.py"
|
|
).read_text(encoding="utf-8")
|
|
assert "scm_broker_client" in gitea_api
|
|
assert "GITEA_TOKEN" not in gitea_api
|
|
assert "gitea-token" not in annotations
|
|
|
|
|
|
def test_chat_media_reads_one_raw_runtime_secret(tmp_path: Path):
|
|
module = _load("telegram_media_server")
|
|
secret = tmp_path / "relay"
|
|
secret.write_text("relay-value\n", encoding="utf-8")
|
|
assert module.read_relay_key(secret) == "relay-value"
|
|
|
|
|
|
def test_ariadne_receives_the_triage_key_directly_from_vault():
|
|
maintenance = ROOT / "services" / "maintenance" / "apps" / "ariadne-deployment.yaml"
|
|
deployment = yaml.safe_load(maintenance.read_text(encoding="utf-8"))
|
|
annotations = deployment["spec"]["template"]["metadata"]["annotations"]
|
|
template = annotations["vault.hashicorp.com/agent-inject-template-ariadne-env.sh"]
|
|
assert 'secret "kv/data/atlas/hermes/triage-api"' in template
|
|
assert 'export ARIADNE_HERMES_API_KEY="{{ .Data.data.api_key }}"' in template
|
|
assert 'secret "kv/data/atlas/hermes/developer-gitea"' in template
|
|
assert 'export ARIADNE_HERMES_GITEA_TOKEN="{{ .Data.data.token }}"' in template
|
|
container = deployment["spec"]["template"]["spec"]["containers"][0]
|
|
assert not any(item["name"] == "ARIADNE_HERMES_API_KEY" for item in container["env"])
|
|
assert not any(
|
|
item["name"] == "ARIADNE_HERMES_GITEA_TOKEN" for item in container["env"]
|
|
)
|
|
|
|
vault_policy = (
|
|
ROOT / "services" / "vault" / "scripts" / "vault_k8s_auth_configure.sh"
|
|
).read_text(encoding="utf-8")
|
|
assert '"hermes/triage-oidc hermes/agent-tokens hermes/triage-api"' in vault_policy
|
|
assert 'hermes-credential-sync' in vault_policy
|
|
assert 'hermes/triage-api hermes/developer-gitea' in vault_policy
|
|
|
|
|
|
def test_gitea_bootstrap_reuses_valid_vault_tokens():
|
|
bootstrap = (
|
|
ROOT / "services" / "gitea" / "scripts" / "gitea_atlas_identity_ensure.sh"
|
|
).read_text(encoding="utf-8")
|
|
|
|
assert "vault_login\n\nreconciler_token=$(vault_read_field" in bootstrap
|
|
assert 'if ! token_is_valid "${reconciler_user}"' in bootstrap
|
|
assert 'if ! token_is_valid "${hermes_user}"' in bootstrap
|
|
assert bootstrap.count('generate_token "${reconciler_user}"') == 1
|
|
assert bootstrap.count('generate_token "${hermes_user}"') == 1
|