2026-04-18 台北時區 —— ogt + Claude Opus 4.7 (1M) 本 commit 響應本 Session 兩次憑證外洩事故 (feedback_secrets_leak_incidents_2026-04-18.md), 交付統帥可直接部署的零信任基礎設施範本. 檔案清單: 1. scripts/host-ops/awoooi-hosts-add.sh - 110 主機 /etc/hosts 白名單 wrapper - 只允許預定義主機名,idempotent,帶 IP 格式驗證 - 安裝: /usr/local/bin/awoooi-hosts-add (root:root 0755) 2. scripts/host-ops/awoooi-wrapper.sudoers - 配套 sudoers 規則 (NOPASSWD for wrapper + SIGHUP only) - 安裝: /etc/sudoers.d/awoooi-wrapper (root:root 0440) - 禁 tee / bash / sh 這類 generic shell access 3. apps/api/migrations/adr090b_awoooi_migrator_role.sql - PG 限權角色 awoooi_migrator - 只能 DDL (CREATE/ALTER/DROP/INDEX/COMMENT) - 明確 REVOKE 所有 DML + default privileges 鎖死 - 本檔由統帥執行 (需 superuser),不由 Claude 執行 4. k8s/awoooi-prod/awoooi-migrator-secret.template.yaml - K8s Secret patch 範本 - 新增 MIGRATION_DATABASE_URL key (awoooi_migrator 連線串) - 與應用 DATABASE_URL 拆開 5. .gitea/workflows/run-migration.yml - CI 自動套用新 migration (單 transaction + ON_ERROR_STOP) - 用 Gitea secret MIGRATION_DATABASE_URL,不走明碼 - 每次成功寫一筆 asset_discovery_run (audit trail) 零信任三層防線 (對應 feedback_secrets_leak_incidents): L1 對話無密碼 -> wrapper 內建白名單 L2 操作經 wrapper -> sudoers + awoooi_migrator L3 顯示強制遮蔽 -> CI 走 secret,不走 env 本 Session 發現的 3 次憑證外洩全部在 feedback_secrets_leak memory 登記,並有對應 P0 輪替計畫. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
106 lines
4.6 KiB
SQL
106 lines
4.6 KiB
SQL
-- ADR-090-B: awoooi_migrator 限權角色 + 憑證分離
|
|
-- 建立時間: 2026-04-18 台北時區
|
|
-- 建立者: ogt + Claude Opus 4.7 (1M)
|
|
--
|
|
-- 上游: ADR-090 主檔 + feedback_secrets_leak_incidents_2026-04-18
|
|
--
|
|
-- 目的:
|
|
-- 1. 把 migration 操作從「應用 superuser」(awoooi) 拆出,避免 CI / AI 腳本需要生產密碼
|
|
-- 2. awoooi_migrator 只能 CREATE / ALTER / DROP / INDEX / COMMENT,不能 SELECT / DML
|
|
-- 3. 若 migrator 帳號外洩,攻擊者也無法讀取資料,只能結構性破壞 (可 rollback)
|
|
--
|
|
-- 執行者: 統帥 (需 superuser 權限 postgres 執行) — Claude 只起草,不執行
|
|
--
|
|
-- 執行步驟 (請統帥在 188 主機上 psql as postgres 超級使用者):
|
|
-- 1. 以 postgres 連上 awoooi_prod
|
|
-- 2. 把下方 <RANDOM_STRONG_PASSWORD> 替換為您親自產生的密碼
|
|
-- 3. 執行本檔
|
|
-- 4. 更新 K8s secret awoooi-secrets 新增 MIGRATION_DATABASE_URL
|
|
-- 5. 測試: PGPASSWORD='<new>' psql -h 188 -U awoooi_migrator -d awoooi_prod
|
|
-- → 應可 CREATE TABLE x(); 但不能 SELECT * FROM incidents;
|
|
--
|
|
-- 回滾: DROP OWNED BY awoooi_migrator; DROP ROLE awoooi_migrator;
|
|
|
|
-- ============================================================================
|
|
-- Step 1: 建立 migrator 角色 (預設無密碼,立即設定)
|
|
-- ============================================================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'awoooi_migrator') THEN
|
|
CREATE ROLE awoooi_migrator WITH LOGIN;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ★ 替換為您親自產生的 32+ 字元隨機密碼 (建議 openssl rand -base64 32) ★
|
|
ALTER ROLE awoooi_migrator WITH PASSWORD '<RANDOM_STRONG_PASSWORD>';
|
|
-- 註: ALTER ROLE 不會寫入 pg_stat_statements log (若有 log_statement=all 請先關掉)
|
|
|
|
-- ============================================================================
|
|
-- Step 2: 授予 DDL 權限 (CREATE / ALTER / DROP / INDEX / COMMENT)
|
|
-- ============================================================================
|
|
|
|
-- 允許連線 awoooi_prod
|
|
GRANT CONNECT ON DATABASE awoooi_prod TO awoooi_migrator;
|
|
|
|
-- 允許在 public schema 建表 / 建 index
|
|
GRANT USAGE, CREATE ON SCHEMA public TO awoooi_migrator;
|
|
|
|
-- 允許管理所有現有表 (ALTER / DROP / INDEX / COMMENT)
|
|
-- 注意: 這不包含 SELECT / INSERT / UPDATE / DELETE
|
|
GRANT REFERENCES, TRIGGER ON ALL TABLES IN SCHEMA public TO awoooi_migrator;
|
|
|
|
-- 允許執行所有 funcs (ALTER FUNCTION / DROP FUNCTION 需要)
|
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO awoooi_migrator;
|
|
|
|
-- 未來新建物件自動繼承上述權限 (對 awoooi 這個 owner 建的物件)
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT REFERENCES, TRIGGER ON TABLES TO awoooi_migrator;
|
|
|
|
-- 允許使用 pgcrypto / vector 等 extension
|
|
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO awoooi_migrator;
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO awoooi_migrator;
|
|
|
|
-- ============================================================================
|
|
-- Step 3: 明確撤銷 DML 權限 (雙重保險,即使以後有誤 grant 也攔得住)
|
|
-- ============================================================================
|
|
|
|
REVOKE SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM awoooi_migrator;
|
|
|
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
REVOKE SELECT, INSERT, UPDATE, DELETE ON TABLES FROM awoooi_migrator;
|
|
|
|
-- ============================================================================
|
|
-- Step 4: 驗收查詢 (執行後手動檢查)
|
|
-- ============================================================================
|
|
|
|
-- 4.1 角色存在?
|
|
-- SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolcanlogin
|
|
-- FROM pg_roles WHERE rolname = 'awoooi_migrator';
|
|
-- -- 預期: rolname=awoooi_migrator, rolcanlogin=t, rolsuper=f
|
|
|
|
-- 4.2 schema 權限?
|
|
-- SELECT has_schema_privilege('awoooi_migrator','public','CREATE');
|
|
-- -- 預期: t
|
|
|
|
-- 4.3 DML 權限應該沒有?
|
|
-- SET ROLE awoooi_migrator;
|
|
-- SELECT * FROM incidents LIMIT 1; -- 預期: ERROR permission denied
|
|
-- RESET ROLE;
|
|
|
|
-- 4.4 DDL 權限應該有?
|
|
-- SET ROLE awoooi_migrator;
|
|
-- CREATE TABLE test_migrator_check (id INT);
|
|
-- DROP TABLE test_migrator_check;
|
|
-- RESET ROLE;
|
|
-- -- 預期: 兩條都成功
|
|
|
|
-- ============================================================================
|
|
-- END OF MIGRATION adr090b_awoooi_migrator_role.sql
|
|
-- 安裝後 CI / AI 腳本憑證路徑:
|
|
-- 未來所有 migration 使用 MIGRATION_DATABASE_URL (awoooi_migrator)
|
|
-- 應用 pod 繼續用 DATABASE_URL (awoooi, 限 DML)
|
|
-- 兩條 URL 分別存 K8s secret 的不同 key
|
|
-- ============================================================================
|