docs(rls): 記錄 canary wave1 production apply
This commit is contained in:
@@ -1,3 +1,51 @@
|
||||
## 2026-05-12 | RLS Canary Wave1 已套用
|
||||
|
||||
**背景**:上一輪已產出 `scripts/ops/awooop-rls-canary-wave1-empty-tables.sql` 與 rollback SQL;使用者批准後,本輪只套用六張 live preflight 顯示為空表的 Wave1 canary policy,不碰 `incidents` / `knowledge_entries` / `playbooks` / `audit_logs` 等高流量或非空表。
|
||||
|
||||
**套用前 gate**:
|
||||
- `python3 scripts/ops/awooop-rls-access-audit.py` → `BLOCKED=0 ALLOW=10`。
|
||||
- `python3 scripts/ops/awooop-rls-manual-script-audit.py` → `BLOCKED=0 REVIEW=5 PASS=13`。
|
||||
- `scripts/ops/awooop-rls-preflight.sh --exact-counts` → `PASS=7 WARN=0 BLOCKED=1`;唯一 blocker 為尚未啟用 policy。
|
||||
- 六張 Wave1 target 仍為 `total_rows=0 null_project_id_rows=0`:
|
||||
- `awooop_contract_revisions`
|
||||
- `awooop_conversation_event`
|
||||
- `awooop_mcp_credential_refs`
|
||||
- `awooop_mcp_gateway_audit`
|
||||
- `awooop_mcp_grants`
|
||||
- `budget_ledger`
|
||||
|
||||
**production apply**:
|
||||
- 已同步到 188:
|
||||
- `/home/ollama/awoooi-ops/awooop-rls-canary-wave1-empty-tables.sql`
|
||||
- `/home/ollama/awoooi-ops/awooop-rls-canary-wave1-empty-tables-rollback.sql`
|
||||
- 以 postgres/operator socket path 執行:
|
||||
- Docker image:`pgvector/pgvector:pg14`
|
||||
- UID/GID:`115:121` (`postgres:postgres`)
|
||||
- DB:`awoooi_prod`
|
||||
- Apply result:`COMMIT`,六張 target table 均 `ENABLE ROW LEVEL SECURITY` + `FORCE ROW LEVEL SECURITY` + fail-closed `FOR ALL TO awooop_app` policy。
|
||||
|
||||
**套用後驗證**:
|
||||
- `scripts/ops/awooop-rls-preflight.sh --exact-counts`:
|
||||
- Wave1 六張表皆為 `rls=True force=True policies=1 fail_open_null=False fail_open_empty=False`。
|
||||
- 全域 preflight 仍為 `PASS=7 WARN=0 BLOCKED=1`,剩餘 blocker 只列未套用的非空/後續 wave 表:`audit_logs`、`awooop_mcp_tool_registry`、`awooop_outbound_message`、`awooop_projects`、`awooop_run_state`、`incidents`、`knowledge_entries`、`playbooks`。
|
||||
- production health `/api/v1/health` → 200 healthy。
|
||||
- runtime/manual audits 仍為:
|
||||
- runtime access audit:`BLOCKED=0 ALLOW=10`
|
||||
- manual script audit:`BLOCKED=0 REVIEW=5 PASS=13`
|
||||
- RLS 行為 rollback-only 測試(API pod / current app DB user):
|
||||
- 未設 `app.project_id` 寫 `budget_ledger` → `InsufficientPrivilegeError`,符合 fail-closed。
|
||||
- 設 `app.project_id='awoooi'` 後寫 `budget_ledger` → allowed,隨即 rollback。
|
||||
- `budget_ledger_count_after=0`,未留下測試資料。
|
||||
|
||||
**整體進度**:
|
||||
- Wave 0:MOMO PostgreSQL backup → AwoooP 失敗通知接線完成並已推 Gitea。
|
||||
- Wave 1:Claude P0 紅燈驗證已完成多項:GitHub production deploy disabled、RLS production 0 policy 已證實、RLS role bootstrap 已套用、API runtime access path 已收斂、manual script gate 已建立、Wave1 空表 canary RLS 已套用。
|
||||
- 尚未完成:token rotation(需外部輪換)、188 certbot 正式修復、剩餘 RLS waves、高流量表 canary/rollout、188 local Ollama stop window。
|
||||
|
||||
**下一步**:
|
||||
- Wave1.1:選擇下一批低行數但非空表 canary(候選:`awooop_projects` 2 rows、`awooop_mcp_tool_registry` 4 rows),先做 explicit read/write rollback tests,再產出 apply/rollback SQL。
|
||||
- 高流量表 (`incidents` / `knowledge_entries` / `playbooks` / `audit_logs`) 暫不熱開,需另做 query-path 與 rollback rehearsal。
|
||||
|
||||
## 2026-05-12 | RLS Manual Script Gate 與 Canary Wave1 套件
|
||||
|
||||
**背景**:API runtime DB access path 已收斂後,下一個風險是人工腳本在 RLS fail-closed 後直接用 `DATABASE_URL` 讀寫 tenant tables;同時需要第一批低風險 RLS policy 套件,但不可直接熱開高流量表。
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# AwoooP RLS Canary Wave 1
|
||||
|
||||
This wave is a staged apply package only. It enables fail-closed RLS on tables
|
||||
that were empty in the latest production preflight.
|
||||
This wave enables fail-closed RLS on tables that were empty in the production
|
||||
preflight.
|
||||
|
||||
Status: applied to production on 2026-05-12.
|
||||
|
||||
Apply script:
|
||||
|
||||
@@ -67,6 +69,29 @@ The global preflight will still be blocked until later waves cover the remaining
|
||||
tables. The six wave1 tables should show `rls=true`, `force=true`, and
|
||||
`policies=1`, with no fail-open policy expression.
|
||||
|
||||
## 2026-05-12 Production Evidence
|
||||
|
||||
Apply completed with `COMMIT` through the 188 postgres/operator socket path.
|
||||
|
||||
Post-apply preflight:
|
||||
|
||||
- `awooop_contract_revisions`: `rls=true`, `force=true`, `policies=1`
|
||||
- `awooop_conversation_event`: `rls=true`, `force=true`, `policies=1`
|
||||
- `awooop_mcp_credential_refs`: `rls=true`, `force=true`, `policies=1`
|
||||
- `awooop_mcp_gateway_audit`: `rls=true`, `force=true`, `policies=1`
|
||||
- `awooop_mcp_grants`: `rls=true`, `force=true`, `policies=1`
|
||||
- `budget_ledger`: `rls=true`, `force=true`, `policies=1`
|
||||
|
||||
All six showed `fail_open_null=false` and `fail_open_empty=false`.
|
||||
|
||||
Rollback-only behavior test from the API pod:
|
||||
|
||||
```text
|
||||
budget_ledger_no_context=InsufficientPrivilegeError
|
||||
budget_ledger_with_context=allowed_and_rolled_back
|
||||
budget_ledger_count_after=0
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user