fix(security): isolate workload database identities
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m25s
CD Pipeline / build-and-deploy (push) Successful in 6m42s
CD Pipeline / post-deploy-checks (push) Successful in 1m50s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m25s
CD Pipeline / build-and-deploy (push) Successful in 6m42s
CD Pipeline / post-deploy-checks (push) Successful in 1m50s
This commit is contained in:
@@ -551,7 +551,7 @@ _AI_AUTOMATION_PROGRAM_WORK_ITEMS: list[dict[str, Any]] = [
|
||||
"order": 2,
|
||||
"status": "in_progress",
|
||||
"title": "收斂 API、Worker 與 executor 的最小權限信任邊界",
|
||||
"problem": "API/Worker 的 RBAC 與 SSH mount 已拆分並由 production receipt 證明;剩餘缺口是 SSH egress policy、每 workload DB role/secret 隔離,以及首條成功 capability lifecycle 尚未同 run 關閉。",
|
||||
"problem": "NetworkPolicy、RBAC、SSH mount 與 capability lifecycle 已由 production receipt 證明;剩餘缺口只有每 workload DB identity、Secret projection 與 connection budget 隔離。",
|
||||
"professional_practice": "zero trust workload identity、least privilege RBAC、namespace-scoped broker、capability allowlist、short-lived credentials",
|
||||
"asset_scope_ids": ["hosts", "services", "products", "tools", "observability_and_security"],
|
||||
"acceptance": [
|
||||
@@ -562,7 +562,7 @@ _AI_AUTOMATION_PROGRAM_WORK_ITEMS: list[dict[str, Any]] = [
|
||||
"API/Worker 到 host:22 的 socket verifier 必須 denied,只有 broker 可連 allowlisted SSH endpoints",
|
||||
"API、Worker 與 broker 使用分離的 DB identity/secret projection 與可驗證 connection budget",
|
||||
],
|
||||
"next_action": "部署 broker-only SSH NetworkPolicy 與 v2 public verifier;再修復 allowlisted canary 的 returncode=2,完成同 run capability issue/check/apply/verify/revoke,並拆分 workload DB role/secret。",
|
||||
"next_action": "建立 API、Worker、broker 專用 LOGIN role 與獨立 Secret projection,執行 representative DB preflight、受控 rollout、live snapshot rollback 與 public-safe budget verifier。",
|
||||
},
|
||||
{
|
||||
"id": "AIA-P0-002",
|
||||
|
||||
@@ -82,6 +82,17 @@ def _build_database_identity_boundary(
|
||||
f"{workload_id}_monolithic_secret_env_from_absent"
|
||||
)
|
||||
),
|
||||
"representative_preflight_passed": _truthy(
|
||||
receipt.get(
|
||||
f"{workload_id}_representative_db_preflight_passed"
|
||||
)
|
||||
),
|
||||
"table_privilege_gap_count": _int_or_none(
|
||||
receipt.get(f"{workload_id}_table_privilege_gap_count")
|
||||
),
|
||||
"sequence_privilege_gap_count": _int_or_none(
|
||||
receipt.get(f"{workload_id}_sequence_privilege_gap_count")
|
||||
),
|
||||
}
|
||||
|
||||
secret_refs = [
|
||||
@@ -119,6 +130,15 @@ def _build_database_identity_boundary(
|
||||
and workloads[workload_id]["required_connection_budget"] > 0
|
||||
for workload_id in _DB_WORKLOAD_IDS
|
||||
)
|
||||
representative_preflights_verified = bool(
|
||||
all(
|
||||
workloads[workload_id]["representative_preflight_passed"] is True
|
||||
and workloads[workload_id]["table_privilege_gap_count"] == 0
|
||||
and workloads[workload_id]["sequence_privilege_gap_count"] == 0
|
||||
for workload_id in _DB_WORKLOAD_IDS
|
||||
)
|
||||
and _truthy(receipt.get("representative_db_preflights_verified"))
|
||||
)
|
||||
db_identity_isolated = bool(
|
||||
distinct_secret_refs
|
||||
and distinct_role_fingerprints
|
||||
@@ -129,6 +149,7 @@ def _build_database_identity_boundary(
|
||||
db_identity_isolated
|
||||
and null_pool_verified
|
||||
and workload_budget_verified
|
||||
and representative_preflights_verified
|
||||
and _truthy(receipt.get("connection_budget_verified"))
|
||||
)
|
||||
|
||||
@@ -147,6 +168,14 @@ def _build_database_identity_boundary(
|
||||
blockers.append(f"{workload_id}_database_role_fingerprint_unavailable")
|
||||
if workload["database_null_pool"] is not True:
|
||||
blockers.append(f"{workload_id}_database_null_pool_not_verified")
|
||||
if (
|
||||
workload["representative_preflight_passed"] is not True
|
||||
or workload["table_privilege_gap_count"] != 0
|
||||
or workload["sequence_privilege_gap_count"] != 0
|
||||
):
|
||||
blockers.append(
|
||||
f"{workload_id}_representative_db_preflight_not_verified"
|
||||
)
|
||||
connection_limit = workload["connection_limit"]
|
||||
required_budget = workload["required_connection_budget"]
|
||||
if (
|
||||
@@ -170,7 +199,7 @@ def _build_database_identity_boundary(
|
||||
for workload_id in _DB_WORKLOAD_IDS
|
||||
]
|
||||
return {
|
||||
"schema_version": "awoooi_workload_db_identity_budget_readback_v1",
|
||||
"schema_version": "awoooi_workload_db_identity_budget_readback_v2",
|
||||
"status": "verified_ready" if not blockers else "in_progress",
|
||||
"receipt_ref": receipt_ref,
|
||||
"verifier": receipt.get("db_identity_verifier") or None,
|
||||
@@ -188,6 +217,9 @@ def _build_database_identity_boundary(
|
||||
"distinct_db_role_fingerprints": distinct_role_fingerprints,
|
||||
"database_null_pool_verified": null_pool_verified,
|
||||
"workload_connection_budgets_verified": workload_budget_verified,
|
||||
"representative_db_preflights_verified": (
|
||||
representative_preflights_verified
|
||||
),
|
||||
},
|
||||
"connection_budget": {
|
||||
"observed_total": (
|
||||
|
||||
Reference in New Issue
Block a user