feat(ops): ADR-090-B 零信任收尾範本 — wrapper / sudoers / migrator / CI
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>
This commit is contained in:
106
.gitea/workflows/run-migration.yml
Normal file
106
.gitea/workflows/run-migration.yml
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# ADR-090-B: Gitea CI 自動 migration workflow
|
||||||
|
# 建立時間: 2026-04-18 台北時區
|
||||||
|
# 建立者: ogt + Claude Opus 4.7 (1M)
|
||||||
|
#
|
||||||
|
# 目的: 每次 main 分支有新 migration SQL 檔,自動:
|
||||||
|
# 1. 用 MIGRATION_DATABASE_URL (awoooi_migrator 限權帳號) 連 PG
|
||||||
|
# 2. 只跑「新增」的 migration (比對已執行列表)
|
||||||
|
# 3. 跑後寫 asset_discovery_run + automation_operation_log 記錄
|
||||||
|
# 4. 失敗自動 rollback (single transaction + ON_ERROR_STOP)
|
||||||
|
#
|
||||||
|
# 觸發: push to main,且 apps/api/migrations/ 有變更
|
||||||
|
|
||||||
|
name: run-migration
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'apps/api/migrations/*.sql'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
migrate:
|
||||||
|
runs-on: ubuntu-latest # 或 self-hosted runner on 110
|
||||||
|
container:
|
||||||
|
image: postgres:15-alpine # 帶 psql
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2 # 需比對上一個 commit
|
||||||
|
|
||||||
|
- name: Identify new migrations
|
||||||
|
id: diff
|
||||||
|
run: |
|
||||||
|
NEW_FILES=$(git diff --name-only --diff-filter=A HEAD~1 HEAD -- 'apps/api/migrations/*.sql' || true)
|
||||||
|
echo "new_files<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "$NEW_FILES" >> $GITHUB_OUTPUT
|
||||||
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
echo "=== New migration files ==="
|
||||||
|
echo "$NEW_FILES"
|
||||||
|
|
||||||
|
- name: Apply new migrations
|
||||||
|
if: steps.diff.outputs.new_files != ''
|
||||||
|
env:
|
||||||
|
# 從 Gitea secrets 取,不直接明碼
|
||||||
|
PGURL: ${{ secrets.MIGRATION_DATABASE_URL }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ -z "$PGURL" ]; then
|
||||||
|
echo "::error::MIGRATION_DATABASE_URL secret not set in Gitea"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 套用每個新檔 (single transaction per file)
|
||||||
|
echo "${{ steps.diff.outputs.new_files }}" | while IFS= read -r file; do
|
||||||
|
[ -z "$file" ] && continue
|
||||||
|
echo "=== Applying: $file ==="
|
||||||
|
psql "$PGURL" \
|
||||||
|
-v ON_ERROR_STOP=1 \
|
||||||
|
--single-transaction \
|
||||||
|
-f "$file"
|
||||||
|
echo "=== OK: $file ==="
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Seed asset_discovery_run (audit)
|
||||||
|
if: steps.diff.outputs.new_files != ''
|
||||||
|
env:
|
||||||
|
PGURL: ${{ secrets.MIGRATION_DATABASE_URL }}
|
||||||
|
run: |
|
||||||
|
FILES_JSON=$(echo "${{ steps.diff.outputs.new_files }}" | jq -Rn '[inputs | select(length > 0)]')
|
||||||
|
psql "$PGURL" -c "
|
||||||
|
INSERT INTO asset_discovery_run (
|
||||||
|
run_id, triggered_by, scope, scan_depth, status,
|
||||||
|
started_at, ended_at, tools_used, summary
|
||||||
|
) VALUES (
|
||||||
|
gen_random_uuid(),
|
||||||
|
'ci:gitea',
|
||||||
|
ARRAY['schema_migration'],
|
||||||
|
'full',
|
||||||
|
'success',
|
||||||
|
NOW(),
|
||||||
|
NOW(),
|
||||||
|
'{\"psql\": 1, \"gitea_ci\": 1}'::jsonb,
|
||||||
|
jsonb_build_object(
|
||||||
|
'type', 'ci_migration',
|
||||||
|
'commit_sha', '${{ github.sha }}',
|
||||||
|
'files', $FILES_JSON
|
||||||
|
)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Notify Telegram (if configured)
|
||||||
|
if: always()
|
||||||
|
env:
|
||||||
|
TG_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
|
TG_CHAT: ${{ secrets.TELEGRAM_OPS_CHAT_ID }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$TG_TOKEN" ] && [ -n "$TG_CHAT" ]; then
|
||||||
|
STATUS="${{ job.status }}"
|
||||||
|
MSG="🗄️ Migration CI: \`${STATUS}\` — commit ${{ github.sha }}"
|
||||||
|
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|
||||||
|
-d chat_id="${TG_CHAT}" \
|
||||||
|
-d parse_mode="Markdown" \
|
||||||
|
-d text="${MSG}" || true
|
||||||
|
fi
|
||||||
105
apps/api/migrations/adr090b_awoooi_migrator_role.sql
Normal file
105
apps/api/migrations/adr090b_awoooi_migrator_role.sql
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
-- 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
|
||||||
|
-- ============================================================================
|
||||||
41
k8s/awoooi-prod/awoooi-migrator-secret.template.yaml
Normal file
41
k8s/awoooi-prod/awoooi-migrator-secret.template.yaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# ADR-090-B: awoooi-secrets 新增 MIGRATION_DATABASE_URL
|
||||||
|
# 建立時間: 2026-04-18 台北時區
|
||||||
|
# 建立者: ogt + Claude Opus 4.7 (1M)
|
||||||
|
#
|
||||||
|
# 目的: 把 migration 憑證 (awoooi_migrator, 限 DDL) 從應用憑證拆開
|
||||||
|
#
|
||||||
|
# 套用方式 (兩選一):
|
||||||
|
# (A) 手動 patch K8s secret (推薦,不改 manifest):
|
||||||
|
# kubectl patch secret awoooi-secrets -n awoooi-prod \
|
||||||
|
# -p "$(cat <<EOF
|
||||||
|
# {
|
||||||
|
# \"stringData\": {
|
||||||
|
# \"MIGRATION_DATABASE_URL\": \"postgresql+asyncpg://awoooi_migrator:<NEW_PWD>@192.168.0.188:5432/awoooi_prod\"
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# EOF
|
||||||
|
# )"
|
||||||
|
#
|
||||||
|
# (B) 用 sealed-secrets (若有 GitOps 路徑):
|
||||||
|
# 1. 先用 kubeseal 把 stringData 加密
|
||||||
|
# 2. commit 加密後的 SealedSecret manifest 進 k8s/awoooi-prod/
|
||||||
|
# 3. argocd sync
|
||||||
|
#
|
||||||
|
# 驗收:
|
||||||
|
# kubectl get secret awoooi-secrets -n awoooi-prod -o jsonpath='{.data.MIGRATION_DATABASE_URL}' | base64 -d
|
||||||
|
# → 應顯示 postgresql+asyncpg://awoooi_migrator:...
|
||||||
|
|
||||||
|
---
|
||||||
|
# 本範本為 patch-style,只列新增欄位,其他欄位保持原樣
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: awoooi-secrets
|
||||||
|
namespace: awoooi-prod
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
# 既有 24 個 key 不列,避免意外覆蓋
|
||||||
|
# ... (既有 DATABASE_URL / TELEGRAM_TOKEN / ... 皆維持原值)
|
||||||
|
|
||||||
|
# 新增:
|
||||||
|
MIGRATION_DATABASE_URL: "postgresql+asyncpg://awoooi_migrator:<FILLED_BY_STATESMAN>@192.168.0.188:5432/awoooi_prod"
|
||||||
68
scripts/host-ops/awoooi-hosts-add.sh
Normal file
68
scripts/host-ops/awoooi-hosts-add.sh
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# AWOOOI Hosts White-list Wrapper (ADR-090)
|
||||||
|
# 建立時間: 2026-04-18 台北時區
|
||||||
|
# 建立者: ogt + Claude Opus 4.7 (1M)
|
||||||
|
#
|
||||||
|
# 目的: 取代「AI 有全域 /etc/hosts sudo 權限」的安全破口
|
||||||
|
# 只允許預定義白名單主機名被寫入,且 idempotent 不重複
|
||||||
|
#
|
||||||
|
# 安裝位置: /usr/local/bin/awoooi-hosts-add
|
||||||
|
# 安裝權限: root:root 0755
|
||||||
|
# 呼叫方式 (需搭配 sudoers): sudo /usr/local/bin/awoooi-hosts-add <IP> <HOSTNAME>
|
||||||
|
#
|
||||||
|
# 例: sudo /usr/local/bin/awoooi-hosts-add 114.32.151.246 mo.wooo.work
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ─── 白名單 ───────────────────────────────────────────────────────────────
|
||||||
|
# 新增主機名到這裡,需統帥審查並 git commit
|
||||||
|
ALLOWED_HOSTS=(
|
||||||
|
"mo.wooo.work"
|
||||||
|
"aiops.wooo.work"
|
||||||
|
"bitan.wooo.work"
|
||||||
|
"stock.wooo.work"
|
||||||
|
"tsenyang.com"
|
||||||
|
"www.tsenyang.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ─── 參數驗證 ─────────────────────────────────────────────────────────────
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
echo "Usage: $0 <IP> <HOSTNAME>" >&2
|
||||||
|
echo "Whitelist: ${ALLOWED_HOSTS[*]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IP="$1"
|
||||||
|
HOST="$2"
|
||||||
|
|
||||||
|
# IP 格式驗證 (基本 IPv4)
|
||||||
|
if [[ ! "$IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||||
|
echo "Invalid IP format: $IP" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 主機名白名單檢查
|
||||||
|
ALLOWED=0
|
||||||
|
for allowed in "${ALLOWED_HOSTS[@]}"; do
|
||||||
|
if [[ "$HOST" == "$allowed" ]]; then
|
||||||
|
ALLOWED=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $ALLOWED -eq 0 ]]; then
|
||||||
|
echo "Host not whitelisted: $HOST" >&2
|
||||||
|
echo "Contact statesman to update script whitelist + git commit." >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Idempotent 寫入 ──────────────────────────────────────────────────────
|
||||||
|
# 若 /etc/hosts 已有此主機名 (不限 IP),視為已設定,不重複寫
|
||||||
|
if grep -qE "^[0-9.]+[[:space:]]+${HOST}\$" /etc/hosts; then
|
||||||
|
echo "Host $HOST already in /etc/hosts, no change."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 寫入 (原子 append)
|
||||||
|
echo "$IP $HOST" >> /etc/hosts
|
||||||
|
echo "Added: $IP $HOST to /etc/hosts"
|
||||||
27
scripts/host-ops/awoooi-wrapper.sudoers
Normal file
27
scripts/host-ops/awoooi-wrapper.sudoers
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# AWOOOI L4 自動化代理人最小權限 sudoers (ADR-090)
|
||||||
|
# 建立時間: 2026-04-18 台北時區
|
||||||
|
# 建立者: ogt + Claude Opus 4.7 (1M)
|
||||||
|
#
|
||||||
|
# 安裝位置: /etc/sudoers.d/awoooi-wrapper
|
||||||
|
# 安裝權限: root:root 0440
|
||||||
|
# 安裝前驗證: sudo visudo -c -f /path/to/this/file
|
||||||
|
#
|
||||||
|
# 設計原則:
|
||||||
|
# - 以 command path 精確指定,不開 tee / bash / sh 這類 generic shell
|
||||||
|
# - /etc/hosts 經 awoooi-hosts-add wrapper 白名單限制主機名
|
||||||
|
# - docker kill 只允許 SIGHUP (reload signal),不允許 SIGTERM/KILL
|
||||||
|
# - 不放 systemctl / apt / reboot / shutdown 任何系統級指令
|
||||||
|
|
||||||
|
# 1. /etc/hosts 白名單式寫入 (wrapper 自己驗證主機名)
|
||||||
|
wooo ALL=(root) NOPASSWD: /usr/local/bin/awoooi-hosts-add
|
||||||
|
|
||||||
|
# 2. Prometheus / Blackbox / Alertmanager config reload (SIGHUP only)
|
||||||
|
wooo ALL=(root) NOPASSWD: /usr/bin/docker kill -s SIGHUP prometheus
|
||||||
|
wooo ALL=(root) NOPASSWD: /usr/bin/docker kill -s SIGHUP blackbox-exporter
|
||||||
|
wooo ALL=(root) NOPASSWD: /usr/bin/docker kill -s SIGHUP alertmanager
|
||||||
|
|
||||||
|
# 3. Prometheus / Alertmanager config 檔驗證 (只讀,防禦性)
|
||||||
|
wooo ALL=(root) NOPASSWD: /usr/bin/docker exec prometheus promtool check config /etc/prometheus/prometheus.yml
|
||||||
|
wooo ALL=(root) NOPASSWD: /usr/bin/docker exec alertmanager amtool check-config /etc/alertmanager/alertmanager.yml
|
||||||
|
|
||||||
|
# 註: 未來新增命令必經 git commit 的 PR review,不可直接手動加
|
||||||
Reference in New Issue
Block a user