diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index d4cffecb..9f70b14f 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,31 @@ +## 2026-06-14|SSH / Firewall / Network Access owner request draft 本地完成 + +**背景**:110 端口關閉曾造成多個服務異常,代表 SSH、known_hosts、firewall、NodePort、NetworkPolicy、WireGuard 與 deploy SSH 不能只靠口頭管理。既有 SSH / network repo-only 清冊已納入 16 個 surface;本段先建立 owner request draft,要求 owner 提供脫敏 live access state、allowed source CIDR、維護窗口、rollback owner 與 validation plan,不直接改端口。 + +**完成項目**: +- 新增 `scripts/security/ssh-network-owner-request-draft.py`,從 `ssh-network-access-inventory.snapshot.json` 產生 metadata-only owner request draft。 +- 新增 `docs/security/ssh-network-owner-request-draft.snapshot.json`,固定 `request_draft_count=16`、`write_capable_request_draft_count=6`、`live_evidence_required_request_count=16`、`request_field_count=23`、`required_owner_field_count=13`、`blocked_action_count=16`。 +- 新增 `docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md`,說明 16 份 request draft、owner 必填欄位、blocked actions、完成度與 0 / false 邊界。 +- `security-mirror-progress-guard.py` 已鎖住新 snapshot 的 schema、summary、execution false flags、request ids、blocked actions 與每份草稿 false flags。 +- `SSH-NETWORK-ACCESS-INVENTORY.md`、`HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md`、`IWOOOS-CONFIG-CONTROL-INVENTORY.md`、`SECURITY-SUPPLY-CHAIN-PROGRESS.md`、P0 主控板與 MASTER 已同步。 + +**本地驗證**: +- `python3 -m json.tool docs/security/ssh-network-owner-request-draft.snapshot.json` 通過。 +- `python3 -m py_compile scripts/security/ssh-network-owner-request-draft.py scripts/security/security-mirror-progress-guard.py` 通過。 +- 產生器 smoke:`SSH_NETWORK_OWNER_REQUEST_DRAFT_OK drafts=16 write_capable=6 fields=13 sent=0 runtime_gate=0`。 +- `python3 scripts/security/security-mirror-progress-guard.py --root .` → `SECURITY_MIRROR_PROGRESS_GUARD_OK`。 +- `python3 scripts/security/source-control-owner-response-guard.py --root .` → `SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`。 +- `python3 scripts/ops/doc-secrets-sanity-check.py docs .gitea` → `DOC_SECRET_SANITY_OK scanned_files=828`。 +- `git diff --check` 通過。 +- diff-only 新增行工作溝通片語掃描通過;未加入工作溝通原文、委派 XML、內嵌瀏覽器上下文或內部抱怨內容。 +- Snapshot assertion 通過:`SSH_NETWORK_OWNER_REQUEST_DRAFT_ASSERTIONS_OK drafts=16 write_capable=6 fields=13 sent=0 runtime_gate=0`。 + +**完成度與邊界**: +- SSH / Firewall / Network Access owner request draft artifact:`100%`。 +- SSH / network 類別成熟度:維持 `54%`,因為尚未收到 owner response 或 live access evidence。 +- Production browser verification:不適用;本輪只改 repo 內文件、snapshot、guard 與產生器,不變更前端 bundle 或 runtime。 +- request sent、recipient confirmed、owner response received / accepted、live host read、host keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、sudo、deploy SSH、secret collection、host write、active scan、runtime gate、action buttons、production write 全部維持 `0 / false`。 + ## 2026-06-14|Docker / systemd / Host Service owner request draft 本地完成 **背景**:Docker Compose、systemd、repair-bot、Ansible service role 與 host config backup capture 都屬於容易影響 110 / 188 服務穩定性的高價值配置。既有 repo-only 清冊已納入 9 個 surface,但尚未轉成 owner 可回覆的欄位包;因此本段先建立人工送件前 request draft,不碰 live host。 diff --git a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md index 4a1db90f..fdff55b3 100644 --- a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md +++ b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md @@ -54,7 +54,7 @@ | 優先 | 類別 | 目前成熟度 | 下一步 | |------|------|------------|--------| | P1-1 | Docker Compose / systemd / host service config | `50%` | repo-only 清冊已納入 9 個 surface,owner request draft 已轉成 9 份草稿;仍缺 owner response、110 / 188 live hash、restart window、rollback owner 與 post-check 指標 | -| P1-2 | SSH / sudoers / known_hosts / firewall / WireGuard / NodePort | `54%` | repo-only 清冊已納入 16 個 SSH / network access surface;仍缺 live evidence、owner 與 rollback | +| P1-2 | SSH / sudoers / known_hosts / firewall / WireGuard / NodePort | `54%` | repo-only 清冊已納入 16 個 SSH / network access surface,owner request draft 已轉成 16 份草稿;仍缺 owner response、live access state、allowed source CIDR、maintenance window、rollback owner 與 validation plan | | P1-3 | Backup / restore / escrow / retention | `58%` | repo-only 清冊已納入 38 個 surface;仍缺 restore drill approval package、offsite / escrow owner、retention owner、rollback owner 與 no-secret-value evidence | | P1-4 | Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse | `62%` | repo-only 清冊已納入 60 個 monitoring / alerting / observability surface;仍缺 live drift evidence、reload owner、receiver owner、route smoke 與 receipt proof | @@ -161,6 +161,8 @@ python3 scripts/security/high-value-config-control-coverage.py \ `ssh_network_access_inventory_v1` 已把 SSH target、known_hosts workflow、CI deploy SSH、monitoring SSH、backup SSH capture、sudoers wrapper、NetworkPolicy、NodePort、WireGuard runbook 與 alert SSH action catalog 納入 repo-only 清冊,共 `16` 個 surface、`6` 個 write-capable surface、`2` 個 NetworkPolicy、`2` 個 NodePort、`1` 個 sudoers surface 與 `1` 個 WireGuard surface。此更新只讓 `ssh_firewall_network_access` 從 `48%` 推進到 `54%`;owner response、live evidence、maintenance window、rollback owner、runtime gate 與 action button 仍全部為 `0`。 +2026-06-14 再新增 `ssh_network_owner_request_draft_v1`,把 16 個 surface 轉成 `request_draft_count=16`、`write_capable_request_draft_count=6`、`live_evidence_required_request_count=16`、`required_owner_field_count=13`、`blocked_action_count=16` 的人工送件前草稿。此更新仍不調高類別成熟度,因為 request sent、owner response received / accepted、live access state、firewall / port / NetworkPolicy / NodePort / WireGuard 變更、runtime gate 與 action button 仍全部為 `0`。 + ## 10. P1-3 Backup / restore / escrow / retention 清冊更新 `backup_restore_escrow_inventory_v1` 已把 backup orchestration、service backup scripts、restic retention、offsite sync、credential escrow、Velero restore drill、backup health alert 與 cold-start / DR runbook 納入 repo-only 清冊,共 `38` 個 surface、`15` 個 backup script surface、`8` 個 offsite / escrow surface、`5` 個 Velero surface 與 `27` 個 write-capable surface。此更新只讓 `backup_restore_credential` 從 `52%` 推進到 `58%`;owner response、live evidence、restore drill acceptance、offsite sync acceptance、credential escrow acceptance、retention change acceptance、runtime gate 與 action button 仍全部為 `0`。 diff --git a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md index 39e0acc7..c43a6585 100644 --- a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md +++ b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md @@ -51,6 +51,8 @@ `ssh_network_access_inventory_v1` 已把 SSH target、known_hosts workflow、CI deploy SSH、monitoring SSH、backup SSH capture、sudoers wrapper、NetworkPolicy、NodePort、WireGuard runbook 與 alert SSH action catalog 納入只讀 snapshot。清冊目前共有 `16` 個 surface、`11` 個 SSH source surface、`6` 個 write-capable surface、`2` 個 NetworkPolicy、`2` 個 NodePort、`1` 個 sudoers surface 與 `1` 個 WireGuard surface,讓 SSH / network 類別成熟度從 `48%` 推進到 `54%`。 +2026-06-14 已新增 `docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md` 與 `docs/security/ssh-network-owner-request-draft.snapshot.json`,將 16 個 SSH / network access surface 轉成人工送件前 owner request draft。固定 `drafts=16`、`write_capable=6`、`live_evidence_required=16`、`owner_fields=13`、`blocked_actions=16`,但 request sent、owner response received / accepted、live access state、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、host write、runtime gate 仍全部為 `0 / false`。 + 此更新仍不是 live network truth:live firewall、sudoers、known_hosts、NetworkPolicy、NodePort、WireGuard evidence、network owner、maintenance window、rollback owner 與 owner response received / accepted 全部仍為 `0`,也不得執行 SSH、keyscan、sudo、firewall change、NetworkPolicy apply、NodePort change 或 WireGuard cutover。 ### 0.4 2026-06-11 Backup / restore / escrow / retention repo-only 清冊 @@ -138,8 +140,8 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 | P1 | Harbor / registry | Nginx templates、backup scripts、CD workflows | 110 Harbor / registry domains | robot account owner、image tag immutability、scan policy、TLS | | P1 | PostgreSQL / Redis / MinIO | app config、backup scripts、monitoring config | 188 / 110 / K3s | no plaintext DSN, access boundary, backup, restore, metrics auth | | P1 | Docker Compose / systemd | `docker-compose.yml`、`ops/*/docker-compose.yml`、`scripts/reboot-recovery/*.service` | 110 / 188 / agent-bounty hosts | port / volume / env diff、restart window、rollback owner | -| P1 | SSH / sudoers / known_hosts | Ansible inventory、ops scripts、runner scripts | host owners | pinned or accept-new policy、no host key disable、target whitelist | -| P1 | Firewall / WireGuard / NodePort / VIP | K8s service / network policy、Kali / wg-easy docs | network owner | ingress / egress matrix、no unreviewed port exposure | +| P1 | SSH / sudoers / known_hosts | Ansible inventory、ops scripts、runner scripts | host owners | owner request draft 已固定;下一步只收脫敏 live access state、known_hosts / host-key policy、target whitelist 與 rollback owner | +| P1 | Firewall / WireGuard / NodePort / VIP | K8s service / network policy、Kali / wg-easy docs | network owner | owner request draft 已固定;下一步只收 allowed source CIDR、maintenance window、rollback owner 與 validation plan;no unreviewed port exposure | | P1 | AI provider / model routing | `apps/api/src/services/ai_providers/*`、Ollama runbooks、Nginx proxy | AI owner | dry-run、benchmark、cost / privacy review、fallback order gate | | P1 | Kali 112 scanner config | `docs/security/KALI-112-MAINTENANCE-WINDOW-DRAFT.md`、Kali snapshots | Kali owner | maintenance window、no active scan、no `/execute`、hardening dry-run | | P2 | AWOOOI / AwoooP / IwoooS frontend runtime config | `apps/web/next.config.js`、`apps/web/src/lib/config.ts`、i18n | web owner | NEXT_PUBLIC public-domain only、no internal transcript, desktop/mobile smoke | @@ -192,6 +194,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 | owner packet 前台只讀接入 | `100%` | `/zh-TW/iwooos` 已顯示高價值配置 owner packet 草案、C0/C1 packet 數、request / received / accepted 仍為 0 與禁止執行邊界 | | owner response request / received / accepted | `0%` | Packet 只是草案;尚未送件、尚未收件、尚未 reviewer accepted | | Docker / systemd owner request draft | `100%` | 已將 9 個 host service surface 轉成 owner request draft;request sent / received / accepted 仍為 0 | +| SSH / firewall / network owner request draft | `100%` | 已將 16 個 SSH / network access surface 轉成 owner request draft;request sent / received / accepted、port change、firewall change、NetworkPolicy apply、NodePort change、WireGuard change 仍為 0 | | CI blocking / workflow gate | `0%` | 本階段刻意不修改 `.gitea/workflows`,避免初期資安流程摩擦過大 | | owner-provided live Nginx file compare | `70%` | 工具可吃 owner 匯出的 live conf 檔比較;本階段不主動 SSH 取得 | | live Nginx evidence collection | `0%` | 尚未 SSH / Ansible check-mode / live hash;需 owner 與維護窗口規則 | @@ -207,7 +210,8 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 4. P0:把 workflow / runner / secret name owner response 與高價值配置 C0 gate 串成同一個 IwoooS 狀態。 5. P0:把 agent-bounty-protocol compose / MCP / A2A / treasury 高價值配置欄位接入同一個 owner packet queue;不啟用 runtime。 6. P1:向 owner 收 110 / 188 Docker Compose 與 systemd 脫敏 live hash、restart window、rollback owner 與 post-check 指標;不主動 SSH、不重啟、不跑 repair-bot。 -7. P1:把 backup / restore / offsite / escrow owner response packet 接入 AwoooP 只讀狀態;驗收前 backup run、restore drill、offsite sync、remote delete、escrow marker write、retention change 全部維持 `0 / false`。 +7. P1:向 owner 收 SSH / firewall / WireGuard / NodePort 脫敏 live access state、allowed source CIDR、maintenance window、rollback owner 與 validation plan;不主動 keyscan、不改 firewall、不開關端口。 +8. P1:把 backup / restore / offsite / escrow owner response packet 接入 AwoooP 只讀狀態;驗收前 backup run、restore drill、offsite sync、remote delete、escrow marker write、retention change 全部維持 `0 / false`。 8. P1:把 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse owner response packet 接入 AwoooP 只讀狀態;驗收前 reload、receiver route change、silence change、Telegram send 與 alert chain smoke 全部維持 `0 / false`。 9. P1:補 Kali 112、111、168 維護窗口 owner 欄位,仍不做 upgrade / restart / scan。 10. P2:持續精簡 `/zh-TW/iwooos` 配置控管摘要,但不得顯示內部工作對話、token、secret 或可執行按鈕。 diff --git a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md index 73e93218..f7850ca6 100644 --- a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md +++ b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md @@ -396,6 +396,7 @@ headline 要再往上,需要 S4.9 / S4.10 / S4.11 / S4.12 任一 owner respons | K8s / ArgoCD manifest repo-only 清冊 | 本地完成 | `k8s-argocd-manifest-inventory.snapshot.json` 已固定 `file_count=49`、`c0_file_count=36`、`yaml_manifest_file_count=45`、`unique_kind_count=20`、`top_level_kind_marker_count=56`、`required_owner_field_count=11`、`evidence_gap_count=8`、`blocked_action_count=13`,並由 `security-mirror-progress-guard.py` 鎖住 group ids、kind counts、blocked actions 與 false flags | 這是 repo-only manifest inventory,不是 live cluster read、ArgoCD API read、ArgoCD sync、kubectl apply / patch / delete、Helm upgrade、secret collection、manual pod restart、scale workload、RBAC / NetworkPolicy change、restore backup、production write 或 runtime gate;不需要 production browser smoke | | K8s / ArgoCD Owner Request Draft | 已推送 / 已同步 | `e8de19d7` 已進 `gitea/main`;`k8s-argocd-owner-request-draft.snapshot.json` 已固定 `request_draft_count=4`、`c0_request_draft_count=3`、`request_field_count=20`、`required_owner_field_count=11`、`evidence_gap_count=8`、`blocked_action_count=13`、`request_sent_count=0`、`owner_response_received_count=0`、`runtime_gate_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 request ids、blocked actions 與 false flags;AwoooP 平行 Session 已同步 | 這是人工送件前 request draft,不是 request sent、recipient confirmed、owner response received / accepted、rendered manifest diff、ArgoCD API read、ArgoCD sync、kubectl action、live cluster read、secret collection、production write 或 runtime gate;不需要 production browser smoke | | Docker / systemd / Host Service Owner Request Draft | 本地完成 | `host-service-owner-request-draft.snapshot.json` 已固定 `request_draft_count=9`、`write_capable_request_draft_count=3`、`live_evidence_required_request_count=8`、`request_field_count=22`、`required_owner_field_count=12`、`blocked_action_count=14`、`request_sent_count=0`、`owner_response_received_count=0`、`runtime_gate_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 request ids、blocked actions 與 false flags | 這是 Docker Compose、systemd / repair-bot、Ansible role 與 host config backup 的人工送件前 request draft,不是 request sent、owner response received / accepted、live host read、Docker Compose action、systemctl action、repair-bot execution、Ansible apply、secret collection、host write、production write 或 runtime gate;不需要 production browser smoke | +| SSH / Firewall / Network Access Owner Request Draft | 本地完成 | `ssh-network-owner-request-draft.snapshot.json` 已固定 `request_draft_count=16`、`write_capable_request_draft_count=6`、`live_evidence_required_request_count=16`、`request_field_count=23`、`required_owner_field_count=13`、`blocked_action_count=16`、`request_sent_count=0`、`owner_response_received_count=0`、`runtime_gate_count=0`,並由 `security-mirror-progress-guard.py` 鎖住 request ids、blocked actions 與 false flags | 這是 SSH target、known_hosts、CI deploy SSH、monitoring SSH、backup SSH、sudoers、NetworkPolicy、NodePort、WireGuard 與 alert SSH action 的人工送件前 request draft,不是 request sent、owner response received / accepted、live host read、host keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、secret collection、host write、production write 或 runtime gate;不需要 production browser smoke | | S3 approval gate | 進行中 | `security_approval_gate_v1` 已建立 8 個人工 gate items:7 pending、1 block candidate、0 approved | 不得繞過人工批准;批准後仍需 follow-up runtime gate | | S3.0 人工批准 Gate 契約 | 完成草案 | 定義批准範圍、決策選項、required reviewers、still forbidden 與 follow-up runtime gate | AwoooP 可記錄決策,不可執行 gate item | | S3.1 人工決策紀錄契約 | 完成草案 | `security_approval_decision_record_v1` 已建立;目前 0 筆 decision records、0 個 runtime action 授權 | AwoooP 可稽核決策,不可把決策當執行 | diff --git a/docs/security/SSH-NETWORK-ACCESS-INVENTORY.md b/docs/security/SSH-NETWORK-ACCESS-INVENTORY.md index 6d46f925..ad680060 100644 --- a/docs/security/SSH-NETWORK-ACCESS-INVENTORY.md +++ b/docs/security/SSH-NETWORK-ACCESS-INVENTORY.md @@ -15,6 +15,10 @@ 本階段仍是 repo-only 只讀清冊。它不是 live host truth,不是 firewall approval,不是 known_hosts patch approval,不是 NetworkPolicy apply approval,也不是 WireGuard cutover approval。 +2026-06-14 已新增 `docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md` 與 `docs/security/ssh-network-owner-request-draft.snapshot.json`,將 16 個 SSH / network access surface 轉成 owner request draft。固定 `request_draft_count=16`、`write_capable_request_draft_count=6`、`live_evidence_required_request_count=16`、`required_owner_field_count=13`、`blocked_action_count=16`、`request_sent_count=0`、`owner_response_received_count=0`、`runtime_gate_count=0`。 + +此 artifact 只表示 owner request 的 required shape,不代表 request sent、recipient confirmed、owner response received / accepted、live host read、host keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、sudo、deploy SSH、secret collection、host write、production write 或 runtime gate。 + ## 2. 覆蓋摘要 | 指標 | 目前值 | 說明 | @@ -109,3 +113,4 @@ python3 scripts/security/ssh-network-access-inventory.py \ | owner response 收件 | `0%` | 尚未收到或接受 owner response | | live evidence collection | `0%` | 未 SSH、未 keyscan、未讀 live firewall、未讀 live sudoers | | SSH / sudo / firewall / NetworkPolicy / NodePort / WireGuard gate | `0%` | 全部維持未授權 | +| owner request draft | `100%` | 已新增 16 份 request draft、snapshot、文件與 guard;request sent / received / accepted 仍為 0 | diff --git a/docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md b/docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md new file mode 100644 index 00000000..bfd71e9c --- /dev/null +++ b/docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md @@ -0,0 +1,117 @@ +# IwoooS SSH / Firewall / Network Access Owner Request Draft + +| 項目 | 內容 | +|------|------| +| 日期 | 2026-06-14 | +| 狀態 | `owner_request_draft_ready_not_dispatched` | +| 工具 | `scripts/security/ssh-network-owner-request-draft.py` | +| Snapshot | `docs/security/ssh-network-owner-request-draft.snapshot.json` | +| Source inventory | `docs/security/ssh-network-access-inventory.snapshot.json` | +| runtime gate | `0` | + +## 1. 目的 + +本文件承接 SSH / network access repo-only 清冊,把 16 個 surface 轉成人工送件前 request draft。它讓 SSH target、known_hosts、CI deploy SSH、monitoring SSH、backup SSH、sudoers、NetworkPolicy、NodePort、WireGuard 與 alert SSH action catalog 有一致的 owner 回覆欄位。 + +這不是 live firewall 真相、不是端口關閉 / 開放批准、不是 known_hosts patch、不是 host keyscan、不是 NetworkPolicy apply,也不是 WireGuard cutover。 + +## 2. 摘要 + +| 指標 | 目前值 | 說明 | +|------|--------|------| +| request draft | `16` | 每個 SSH / network access surface 一份草稿 | +| write-capable request draft | `6` | CI deploy SSH、monitoring deploy、sudoers、alert action catalog | +| live evidence required request | `16` | 全部都需 owner 提供脫敏 live access evidence | +| request field | `23` | 草稿欄位總數 | +| required owner field | `13` | owner 必填欄位 | +| blocked action | `16` | SSH、keyscan、known_hosts、firewall、port、NetworkPolicy、NodePort、WireGuard、sudo、deploy SSH、active scan、runtime gate 等 | +| request sent / recipient confirmed | `0 / 0` | 尚未送件 | +| owner response received / accepted | `0 / 0` | 尚未收到或驗收 | +| live evidence received | `0` | 不 SSH、不 keyscan、不讀 live firewall | +| maintenance window / rollback owner / validation accepted | `0 / 0 / 0` | 不得改端口、套 policy 或 cutover | +| runtime gate / action button | `0 / 0` | 不提供操作入口 | + +## 3. Request Draft 範圍 + +| Request | 類型 | 範圍 | 風險焦點 | +|---------|------|------|----------| +| `ssh_network_owner_request:ansible_inventory_ssh_targets` | SSH target inventory | `110_111_112_120_121_188` | host owner、pinned known_hosts、ProxyJump、key owner | +| `ssh_network_owner_request:ansible_common_ssh_args` | SSH client policy | `multi_host` | `accept-new` 是否只限 bootstrap | +| `ssh_network_owner_request:gitea_cd_known_hosts_secret` | known_hosts workflow | `110_120_121_188_known_hosts` | known_hosts secret metadata、缺 120 處置、key rotation owner | +| `ssh_network_owner_request:gitea_cd_deploy_ssh` | CI deploy SSH | `k8s_ssh_host` | deploy SSH host owner、rollback、break-glass | +| `ssh_network_owner_request:gitea_cd_dev_ssh` | CI deploy SSH | `192.168.0.120` | dev/prod 邊界、deploy key scope、host key policy | +| `ssh_network_owner_request:deploy_alerts_ssh_path` | CI deploy SSH | `192.168.0.110` | alert deploy owner、known_hosts pinning、通知路徑 | +| `ssh_network_owner_request:monitoring_discover_docker_ssh` | SSH discovery script | `110_188_docker_hosts` | read-only window、輸出脫敏、失敗處置 | +| `ssh_network_owner_request:monitoring_exporter_deploy_ssh` | monitoring SSH deploy | `192.168.0.188` | exporter deploy owner、maintenance window、post-check | +| `ssh_network_owner_request:backup_config_ssh_capture` | SSH backup capture | `110_188_120_121_cluster` | backup execution owner、secret redaction、restore validation | +| `ssh_network_owner_request:host_ops_sudoers_wrapper` | sudoers policy | `host_ops_minimal_sudo` | live sudoers hash、visudo validation、forbidden command proof | +| `ssh_network_owner_request:k8s_prod_network_policy` | K8s NetworkPolicy | `awoooi_prod_namespace` | ingress / egress owner、live policy diff、route smoke | +| `ssh_network_owner_request:argocd_metrics_network_policy` | K8s NetworkPolicy | `argocd_namespace` | Prometheus scrape owner、NodePort exposure owner | +| `ssh_network_owner_request:argocd_metrics_nodeport` | K8s NodePort | `argocd_nodeport_30882_30883` | NodePort exposure owner、firewall owner、source whitelist | +| `ssh_network_owner_request:velero_metrics_nodeport` | K8s NodePort | `velero_nodeport_30885` | backup metrics exposure、firewall owner | +| `ssh_network_owner_request:wireguard_mesh_runbook` | WireGuard runbook | `110_111_120_121_gcp_a_gcp_b` | WireGuard owner、firewall rule owner、canary / rollback | +| `ssh_network_owner_request:alert_rules_ssh_actions` | alert SSH action rules | `ssh_mcp_action_catalog` | action owner、read/write/admin 分級、cooldown、post-check | + +## 4. Owner 必填欄位 + +1. `owner_role_or_team` +2. `decision` +3. `decision_reason` +4. `affected_scope` +5. `redacted_evidence_refs` +6. `live_access_state_ref` +7. `allowed_source_cidrs_ref` +8. `maintenance_window` +9. `rollback_owner` +10. `validation_plan` +11. `break_glass_owner` +12. `change_freeze_rule` +13. `followup_owner` + +## 5. 禁止動作 + +1. `ssh_read` +2. `ssh_write` +3. `host_keyscan` +4. `known_hosts_patch` +5. `firewall_change` +6. `port_close` +7. `port_open` +8. `network_policy_apply` +9. `nodeport_change` +10. `wireguard_change` +11. `sudo_action` +12. `deploy_ssh_action` +13. `secret_value_collection` +14. `ssh_key_collection` +15. `active_scan` +16. `runtime_gate_open` + +## 6. 指令 + +產生 committed snapshot: + +```bash +python3 scripts/security/ssh-network-owner-request-draft.py \ + --root . \ + --inventory-report docs/security/ssh-network-access-inventory.snapshot.json \ + --output docs/security/ssh-network-owner-request-draft.snapshot.json \ + --generated-at 2026-06-14T22:45:00+08:00 +``` + +驗證 guard: + +```bash +python3 scripts/security/security-mirror-progress-guard.py --root . +``` + +## 7. 完成度 + +| 工作 | 完成度 | 說明 | +|------|--------|------| +| owner request draft artifact | `100%` | 16 份 request draft、snapshot、文件與 guard 已固定 | +| request dispatch | `0%` | 尚未送件 | +| owner response received / accepted | `0%` | 尚未收到,尚未驗收 | +| live evidence collection | `0%` | 未 SSH、未 keyscan、未讀 live firewall | +| SSH / firewall / NetworkPolicy / NodePort / WireGuard gate | `0%` | 未授權且未執行 | +| runtime gate / production write | `0%` | 未授權且未執行 | diff --git a/docs/security/ssh-network-owner-request-draft.snapshot.json b/docs/security/ssh-network-owner-request-draft.snapshot.json new file mode 100644 index 00000000..b8321f1c --- /dev/null +++ b/docs/security/ssh-network-owner-request-draft.snapshot.json @@ -0,0 +1,2014 @@ +{ + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "execution_boundaries": { + "action_buttons_allowed": false, + "active_scan_authorized": false, + "deploy_ssh_action_authorized": false, + "firewall_change_authorized": false, + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "live_evidence_received": false, + "live_host_read_authorized": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_authorization": true, + "owner_response_accepted": false, + "owner_response_received": false, + "port_change_authorized": false, + "recipient_confirmed": false, + "request_sent": false, + "runtime_execution_authorized": false, + "secret_value_collection_allowed": false, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "sudo_action_authorized": false, + "wireguard_change_authorized": false + }, + "generated_at": "2026-06-14T22:45:00+08:00", + "git_commit": "4c847093", + "next_steps": [ + "人工送件前確認 network / firewall / deploy owner role 與回覆窗口。", + "owner 只能提供脫敏 live access state、allowed source CIDR metadata、maintenance window、rollback owner 與 validation plan。", + "收到回覆後先做欄位完整性、敏感 payload 隔離、port close/open 影響範圍與 rollback gate 檢查,不得直接改 firewall 或套用 NetworkPolicy。" + ], + "request_drafts": [ + { + "access_scope": [ + "192.168.0.110", + "192.168.0.111", + "192.168.0.112", + "192.168.0.120", + "192.168.0.121", + "192.168.0.188" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ssh_target_inventory", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "110_111_112_120_121_188", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Ansible inventory SSH targets", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "86108dce9174b5c0a794d240dd40518966d9c340950fc6306845b704f12e6536", + "repo_source_path": "infra/ansible/inventory/hosts.yml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:ansible_inventory_ssh_targets", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 48, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "ansible_inventory_ssh_targets", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "StrictHostKeyChecking=accept-new", + "ConnectTimeout=10" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ssh_client_policy", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "multi_host", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Ansible common SSH host key policy", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "c3d5cb63cf84dea98195aa075e69ca90be7422b5805c0cfc50c1d97b832ad86e", + "repo_source_path": "infra/ansible/inventory/group_vars/all.yml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:ansible_common_ssh_args", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 20, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "ansible_common_ssh_args", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "192.168.0.110", + "192.168.0.120", + "192.168.0.121", + "192.168.0.188" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "known_hosts_secret_workflow", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "110_120_121_188_known_hosts", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Gitea CD repair known_hosts secret", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "5b41cdc34c954a383ebea9e4109d10165ceb40589d55df9ee6e808d4092bf593", + "repo_source_path": ".gitea/workflows/cd.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:gitea_cd_known_hosts_secret", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 1562, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "gitea_cd_known_hosts_secret", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "K8S_SSH_HOST", + "deploy_key", + "kubectl apply", + "ArgoCD sync" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ci_deploy_ssh", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "k8s_ssh_host", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Gitea CD K8s deploy SSH path", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "5b41cdc34c954a383ebea9e4109d10165ceb40589d55df9ee6e808d4092bf593", + "repo_source_path": ".gitea/workflows/cd.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:gitea_cd_deploy_ssh", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 1562, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "gitea_cd_deploy_ssh", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": true + }, + { + "access_scope": [ + "192.168.0.120", + "deploy_key", + "kubectl apply" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ci_deploy_ssh", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "192.168.0.120", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Gitea CD dev deploy SSH path", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "e344a4672cb543979c3bb8ea67967c103332587b4a52a939c837457aaeae686d", + "repo_source_path": ".gitea/workflows/cd-dev.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:gitea_cd_dev_ssh", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 262, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "gitea_cd_dev_ssh", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": true + }, + { + "access_scope": [ + "192.168.0.110", + "deploy alert scripts" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ci_deploy_ssh", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "192.168.0.110", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Deploy alerts SSH path", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "b0389fa65da643d411f6961928a276d555ad6a416366bf87f3f5c2c06ee45d13", + "repo_source_path": ".gitea/workflows/deploy-alerts.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:deploy_alerts_ssh_path", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 72, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "deploy_alerts_ssh_path", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": true + }, + { + "access_scope": [ + "192.168.0.110", + "192.168.0.188", + "docker ps" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ssh_discovery_script", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "110_188_docker_hosts", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Monitoring Docker discovery SSH scanner", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "563faf8efcfdbd5a79cc87e0d43c2ba11bebf755a773c97b9c0778f1f0634a15", + "repo_source_path": "ops/monitoring/discover_docker.py", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:monitoring_discover_docker_ssh", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 314, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "monitoring_discover_docker_ssh", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "192.168.0.188", + "scp", + "docker compose up -d" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "monitoring_ssh_deploy_script", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "192.168.0.188", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Monitoring exporter deploy SSH script", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "dbcbca21cf6fd5083177cb8a12c008c1aefed8e6ed05b70d738b3db37699cef3", + "repo_source_path": "ops/monitoring/deploy-exporters.sh", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:monitoring_exporter_deploy_ssh", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 76, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "monitoring_exporter_deploy_ssh", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": true + }, + { + "access_scope": [ + "/etc/ssh", + "/etc/nginx", + "systemd", + "docker", + "k8s" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "ssh_backup_capture", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "110_188_120_121_cluster", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Backup config SSH capture", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "d24301cff44e464bd19ce0792362be16916ccde8c92f92351a19ef4ee988f15e", + "repo_source_path": "scripts/backup/backup-configs.sh", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:backup_config_ssh_capture", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 359, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "backup_config_ssh_capture", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "awoooi-hosts-add", + "docker kill SIGHUP", + "promtool", + "amtool" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "sudoers_policy", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "host_ops_minimal_sudo", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Host ops sudoers wrapper", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "eff02c67402d2f5b2ac8d112dca26a15dc34f03593ca490a0682a6dfa9b0394d", + "repo_source_path": "scripts/host-ops/awoooi-wrapper.sudoers", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:host_ops_sudoers_wrapper", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 27, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "host_ops_sudoers_wrapper", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": true + }, + { + "access_scope": [ + "default deny", + "ingress", + "egress", + "SSH egress", + "Ollama", + "monitoring" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "k8s_network_policy", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "awoooi_prod_namespace", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "K8s production NetworkPolicy", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "f5ea6a9f5fb0cc44664d97a3ed639fa4b43ffd9bcfd70a1f6b44640791b7859f", + "repo_source_path": "k8s/awoooi-prod/02-network-policy.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:k8s_prod_network_policy", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 306, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "k8s_prod_network_policy", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "192.168.0.188", + "argocd metrics", + "192.168.0.0/24 UI" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "k8s_network_policy", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "argocd_namespace", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "ArgoCD metrics NetworkPolicy", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "41ccd0bb22410c48adc84eae74391106c3f28fe181786cfe4128a07f99d2942c", + "repo_source_path": "k8s/argocd/argocd-metrics-network-policy.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:argocd_metrics_network_policy", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 80, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "argocd_metrics_network_policy", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "nodePort 30882", + "nodePort 30883" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "k8s_nodeport_service", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "argocd_nodeport_30882_30883", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "ArgoCD metrics NodePort", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "7f4a8f09206ce0afc185fe11d5e55265bb553b671471724cdcd83c259ec7d266", + "repo_source_path": "k8s/argocd/argocd-metrics-nodeport.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:argocd_metrics_nodeport", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 47, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "argocd_metrics_nodeport", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "nodePort 30885", + "backup metrics" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "k8s_nodeport_service", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "velero_nodeport_30885", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Velero metrics NodePort", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "684959def32b792e2bca34b477afcdfe2b0c6dfd0cb90f4b681a514922d62b75", + "repo_source_path": "k8s/velero/velero-metrics-service.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:velero_metrics_nodeport", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 26, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "velero_metrics_nodeport", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "10.77.114.0/24", + "51820/udp", + "GCP-A", + "GCP-B" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "wireguard_runbook", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "110_111_120_121_gcp_a_gcp_b", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "GCP Ollama WireGuard mesh runbook", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "0af082698c727176ca82c79f95f3950f4c32ed6aabc91c88aff41831fbf0c044", + "repo_source_path": "docs/runbooks/GCP-OLLAMA-WIREGUARD-MESH.md", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:wireguard_mesh_runbook", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 280, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "wireguard_mesh_runbook", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": false + }, + { + "access_scope": [ + "ssh_diagnose", + "docker restart", + "systemctl restart", + "docker compose", + "docker prune" + ], + "action_buttons_allowed": false, + "active_scan_authorized": false, + "affected_scope": "pending_affected_scope", + "allowed_source_cidrs_ref": null, + "blocked_actions": [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open" + ], + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "config_kind": "alert_ssh_action_rules", + "control_tier": "C1", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "deploy_ssh_action_authorized": false, + "expected_scope": "ssh_mcp_action_catalog", + "firewall_change_authorized": false, + "followup_owner": "pending_followup_owner", + "host_keyscan_authorized": false, + "host_write_authorized": false, + "known_hosts_patch_authorized": false, + "label": "Alert rules SSH action surface", + "live_access_state_ref": null, + "live_evidence_received": false, + "maintenance_window": "pending_maintenance_window", + "maintenance_window_accepted": false, + "network_policy_apply_authorized": false, + "nodeport_change_authorized": false, + "not_approval": true, + "owner_response_accepted": false, + "owner_response_received": false, + "owner_role_or_team": "pending_owner_role_or_team", + "port_change_authorized": false, + "recipient_confirmed": false, + "redacted_evidence_refs": [], + "repo_sha256": "5786505aa05073bbb2069203a443a75c8337a289dc015630792d0c201c85cafb", + "repo_source_path": "apps/api/alert_rules.yaml", + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "request_id": "ssh_network_owner_request:alert_rules_ssh_actions", + "request_sent": false, + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "requires_live_evidence": true, + "rollback_owner": "pending_rollback_owner", + "rollback_owner_accepted": false, + "runtime_gate": false, + "secret_value_collection_allowed": false, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "source_line_count": 889, + "ssh_key_collection_allowed": false, + "ssh_read_authorized": false, + "ssh_write_authorized": false, + "status": "draft_not_dispatched", + "sudo_action_authorized": false, + "surface_id": "alert_rules_ssh_actions", + "validation_plan": "pending_validation_plan", + "validation_plan_accepted": false, + "wireguard_change_authorized": false, + "write_capable_surface": true + } + ], + "request_fields": [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval" + ], + "required_owner_fields": [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner" + ], + "schema_version": "ssh_network_owner_request_draft_v1", + "source_inventory_schema_version": "ssh_network_access_inventory_v1", + "source_inventory_status": "repo_only_inventory_ready", + "status": "owner_request_draft_ready_not_dispatched", + "summary": { + "action_button_count": 0, + "active_scan_authorized_count": 0, + "blocked_action_count": 16, + "deploy_ssh_action_authorized_count": 0, + "firewall_change_authorized_count": 0, + "host_keyscan_authorized_count": 0, + "host_write_authorized_count": 0, + "known_hosts_patch_authorized_count": 0, + "live_evidence_received_count": 0, + "live_evidence_required_request_count": 16, + "maintenance_window_accepted_count": 0, + "network_policy_apply_authorized_count": 0, + "nodeport_change_authorized_count": 0, + "owner_response_accepted_count": 0, + "owner_response_received_count": 0, + "port_change_authorized_count": 0, + "recipient_confirmed_count": 0, + "request_draft_count": 16, + "request_field_count": 23, + "request_sent_count": 0, + "required_owner_field_count": 13, + "rollback_owner_accepted_count": 0, + "runtime_gate_count": 0, + "secret_value_collection_allowed_count": 0, + "ssh_key_collection_allowed_count": 0, + "ssh_read_authorized_count": 0, + "ssh_write_authorized_count": 0, + "sudo_action_authorized_count": 0, + "validation_plan_accepted_count": 0, + "wireguard_change_authorized_count": 0, + "write_capable_request_draft_count": 6 + } +} diff --git a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md index db061a12..283b4be2 100644 --- a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md +++ b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md @@ -696,6 +696,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/security/k8s-argocd-manifest-inventory.snapshot.json` + `scripts/security/k8s-argocd-manifest-inventory.py` | K8s / ArgoCD manifest repo-only 清冊已本地完成;掃描 `k8s/awoooi-prod`、`k8s/argocd`、`k8s/velero`、`k8s/monitoring`,固定 4 個 scan groups、49 個 files、36 個 C0 files、45 個 YAML manifests、20 個 unique top-level kind markers、11 欄 required owner fields、8 個 evidence gaps 與 13 類 blocked action;owner response、rendered manifest diff、ArgoCD health readback、ArgoCD sync、kubectl action、live cluster read、runtime gate、action buttons 仍全部為 `0` | 這是 repo-only manifest inventory,不是 live cluster read、ArgoCD API read、ArgoCD sync、kubectl apply / patch / delete、Helm upgrade、secret collection、manual pod restart、scale workload、RBAC / NetworkPolicy change、restore backup、production write 或 runtime gate | | `docs/security/k8s-argocd-owner-request-draft.snapshot.json` + `scripts/security/k8s-argocd-owner-request-draft.py` | K8s / ArgoCD Owner Request Draft 已本地完成;承接 manifest repo-only 清冊,固定 4 份 request draft、3 份 C0、1 份 C1、20 欄 request fields、11 欄 required owner fields、8 個 evidence gaps 與 13 類 blocked action;request sent、recipient confirmed、owner response received / accepted、rendered manifest diff、ArgoCD health readback、ArgoCD sync、kubectl action、live cluster read、runtime gate、action buttons 仍全部為 `0` | 這是人工送件前 request draft,不是 request sent、owner response received / accepted、ArgoCD API read、ArgoCD sync、kubectl action、live cluster read、secret collection、production write 或 runtime gate | | `docs/security/host-service-owner-request-draft.snapshot.json` + `scripts/security/host-service-owner-request-draft.py` | Docker / systemd / Host Service Owner Request Draft 已本地完成;承接 host-service repo-only 清冊,固定 9 份 request draft、3 份 write-capable request draft、8 份需 live evidence request、22 欄 request fields、12 欄 required owner fields 與 14 類 blocked action;request sent、recipient confirmed、owner response received / accepted、live evidence、restart window、rollback owner、host write、runtime gate、action buttons 仍全部為 `0` | 這是人工送件前 request draft,不是 request sent、owner response received / accepted、live host read、Docker Compose action、systemctl action、repair-bot execution、Ansible apply、secret collection、host write、production write 或 runtime gate | +| `docs/security/ssh-network-owner-request-draft.snapshot.json` + `scripts/security/ssh-network-owner-request-draft.py` | SSH / Firewall / Network Access Owner Request Draft 已本地完成;承接 SSH / network repo-only 清冊,固定 16 份 request draft、6 份 write-capable request draft、16 份需 live evidence request、23 欄 request fields、13 欄 required owner fields 與 16 類 blocked action;request sent、recipient confirmed、owner response received / accepted、live access state、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、host write、runtime gate、action buttons 仍全部為 `0` | 這是人工送件前 request draft,不是 request sent、owner response received / accepted、live host read、host keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、secret collection、host write、production write 或 runtime gate | | `docs/evaluations/ai_agent_live_read_model_gate_2026-06-11.json` + `GET /api/v1/agents/agent-live-read-model-gate` | P2-403B AgentSession / Redis Streams live read model gate;定義 safe fields、Redis envelope、worker gate、rollback plan 與 no-write smoke,不連 DB、不讀寫 Redis、不啟動 worker | #### 3.2.1c 2026-06-11 AI Agent 主動營運委派與版本生命週期契約 @@ -843,6 +844,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 77. 建立 K8s / ArgoCD manifest repo-only 清冊。✅ 本地完成;新增 `k8s-argocd-manifest-inventory.py`、snapshot 與說明文件,將 `k8s/awoooi-prod`、`k8s/argocd`、`k8s/velero`、`k8s/monitoring` 轉成 `file_count=49`、`c0_file_count=36`、`yaml_manifest_file_count=45`、`unique_kind_count=20`、`top_level_kind_marker_count=56`、`blocked_action_count=13` 的 repo-only manifest inventory;owner response、rendered manifest diff、ArgoCD health readback、ArgoCD sync、kubectl action、live cluster read、secret collection、runtime gate、action buttons 仍全部為 `0`。這不是 live cluster read、ArgoCD API read、ArgoCD sync、kubectl action、Helm upgrade、secret collection、manual pod restart、scale workload、RBAC / NetworkPolicy change、restore backup、production write 或 runtime gate。 78. 建立 K8s / ArgoCD Owner Request Draft。✅ 本地完成;新增 `k8s-argocd-owner-request-draft.py`、snapshot 與說明文件,將四個 scan group 轉成 `request_draft_count=4`、`c0_request_draft_count=3`、`c1_request_draft_count=1`、`request_field_count=20`、`required_owner_field_count=11`、`evidence_gap_count=8`、`blocked_action_count=13` 的人工送件前草稿;request sent、recipient confirmed、owner response received / accepted、rendered manifest diff、ArgoCD health readback、ArgoCD sync、kubectl action、live cluster read、secret collection、runtime gate、action buttons 仍全部為 `0`。這不是 request sent、owner response received / accepted、ArgoCD API read、ArgoCD sync、kubectl action、live cluster read、secret collection、production write 或 runtime gate。 79. 建立 Docker / systemd / Host Service Owner Request Draft。✅ 本地完成;新增 `host-service-owner-request-draft.py`、snapshot 與說明文件,將 9 個 host service surface 轉成 `request_draft_count=9`、`write_capable_request_draft_count=3`、`live_evidence_required_request_count=8`、`request_field_count=22`、`required_owner_field_count=12`、`blocked_action_count=14` 的人工送件前草稿;request sent、recipient confirmed、owner response received / accepted、live evidence、restart window、rollback owner、post-check、host write、Docker Compose action、systemctl action、repair-bot execution、Ansible apply、secret collection、runtime gate、action buttons 仍全部為 `0`。這不是 request sent、owner response received / accepted、live host read、Docker Compose action、systemctl action、repair-bot execution、Ansible apply、secret collection、host write、production write 或 runtime gate。 +80. 建立 SSH / Firewall / Network Access Owner Request Draft。✅ 本地完成;新增 `ssh-network-owner-request-draft.py`、snapshot 與說明文件,將 16 個 SSH / network access surface 轉成 `request_draft_count=16`、`write_capable_request_draft_count=6`、`live_evidence_required_request_count=16`、`request_field_count=23`、`required_owner_field_count=13`、`blocked_action_count=16` 的人工送件前草稿;request sent、recipient confirmed、owner response received / accepted、live access state、host keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、secret collection、host write、runtime gate、action buttons 仍全部為 `0`。這不是 request sent、owner response received / accepted、live host read、host keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change、secret collection、host write、production write 或 runtime gate。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 diff --git a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md index e85544b6..adbed17e 100644 --- a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md +++ b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md @@ -23,6 +23,7 @@ | 最新 AwoooP Tenants 全域產品資產台帳 D1 基準 | code `fef94df8`、deploy marker `180a6543`、code-review `2967`、CD `2966`;正式 API 顯示產品 / 專案 `16`、網站 / 服務入口 `31`、source-control candidate repo `10`,已納入 `2026fifa.wooo.work`、WOOO Open Design、n8n、Grist、Vault、Ollama、Monitor 與 AWOOOI API / AIOps 服務入口;owner response / runtime gate / action button 仍 `0` | | 最新 P0 Public Gateway / DNS TLS / K8s 配置控管基準 | P0-15 `5068654d` live conf 匯出請求包、P0-16 `f856df1c` redacted export 收件預檢、P0-17 `762f73a6` rendered diff / nginx gate 草稿、P0-19 `551d8144` DNS / TLS / certbot owner confirmation request 草稿、P0-20 `e8876c45` K8s / ArgoCD manifest repo-only 清冊、P0-21 `e8de19d7` K8s / ArgoCD owner request draft;Public Gateway 三段固定 requests / candidates `3`、C0 `2`,DNS / TLS 固定 owner confirmation requests `4`、C0 `4`,K8s manifest 固定 files `49`、C0 `36`、YAML `45`、kinds `20`,K8s owner request 固定 drafts `4`、C0 `3`;request sent、owner response received / accepted、redacted export received / accepted、raw conf stored、rendered diff ready、`nginx -t`、reload、DNS query、TLS probe、certbot renew、ArgoCD sync、kubectl action、route smoke、runtime gate、action button 全部 `0` | | 最新 P1 Docker / systemd / Host Service 配置控管基準 | repo-only inventory 已固定 `surface=9`、`write_capable=3`、`live_evidence_required=8`、成熟度 `42% -> 50%`;本輪新增 owner request draft,固定 `drafts=9`、`owner_fields=12`、`blocked_actions=14`、request sent / received / accepted / live evidence / restart window / rollback owner / host write / runtime gate 全部 `0` | +| 最新 P1 SSH / Firewall / Network Access 配置控管基準 | repo-only inventory 已固定 `surface=16`、`write_capable=6`、`live_evidence_required=16`、成熟度 `48% -> 54%`;本輪新增 owner request draft,固定 `drafts=16`、`owner_fields=13`、`blocked_actions=16`、request sent / received / accepted / live access state / firewall / port change / NetworkPolicy / NodePort / WireGuard / host write / runtime gate 全部 `0` | | 最新 AI Agent automation P1-305 / P1-306 基準 | code `4f0787f8`、deploy marker `af3a9d48`、CD `2592`、code-review `2593`;任務批准邊界與進度彙總已正式驗證;backlog `70%`、done `16/23`、下一步 `P1-001` | | 最新 AI Agent automation P1-001 基準 | code `de3007b7`、stability fix `fd33591c`、deploy marker `8caba233`、LOGBOOK / marker 對齊 `37c0e171`;runtime surface `22`、Secret surface `4`、live gaps `6`、backlog `74%`、done `17/23`、下一步 `P1-002` | | 最新 AI Agent automation P1-002 基準 | code `943faaee`、stability fix `ff266926`、deploy marker `01b8712d`、LOGBOOK / marker 對齊 `70c01003`;Gitea workflows `9`、runner contracts `4`、需 runner attestation workflows `8`、backlog `78%`、done `18/23`、下一步 `P1-003` | @@ -226,6 +227,7 @@ S4.9 是目前 IwoooS 64% 能往前的第一優先 gate。驗收前所有 count |------|------|------|------| | P1 | GitHub primary readiness 只讀重盤 | repo visibility、refs、tags、workflow、secret name、runner、rollback ADR | 只讀 inventory;不建立 repo、不同步 refs | | P1 | Docker / systemd / Host Service owner request draft | 9 個 repo-only surface 已轉成人工送件前草稿,涵蓋 110 / 188 Docker Compose、repair-bot、Ansible role 與 host config backup capture | request sent、owner response、live hash、restart、repair-bot、Ansible apply、host write、runtime gate 全部維持 0 / false | +| P1 | SSH / Firewall / Network Access owner request draft | 16 個 repo-only surface 已轉成人工送件前草稿,涵蓋 SSH target、known_hosts、CI deploy SSH、sudoers、NetworkPolicy、NodePort、WireGuard 與 alert SSH action | request sent、owner response、live access state、keyscan、known_hosts patch、firewall / port change、NetworkPolicy apply、NodePort change、WireGuard change 全部維持 0 / false | | P1 | Kali 112 維護窗口草案 | 1994 pending updates、`networking.service` failed、服務硬化 0/4、rollback、post-check | P1-7 草案已完成;不 `apt upgrade`、不 restart | | P1 | 111 / 168 主機 scope 補強 | P1-8 已補 `DEV-HOSTS-111-168-SCOPE-HANDOFF.md`、snapshot 與 schema;scope、maintenance window、credential handling、rollback owner、validation 指標已可交接 | observe-only;不 credentialed scan、不改 route / CORS / firewall / service | | P1 | VibeWork 納入 IwoooS | P1-9 已補 `VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md`、snapshot 與 schema;repo、product、surface、owner、evidence refs、資料分級、部署邊界與獨立產品邊界已可交接 | 繁中 docs/specs;不合併產品責任、不部署、不改 refs | @@ -297,6 +299,7 @@ P1 只讀重盤階段整體完成度:`70%`。它代表 freshness / inventory / | P1-8 | 111 / 168 開發主機 scope | 已補 `DEV-HOSTS-111-168-SCOPE-HANDOFF.md`、`dev-hosts-111-168-scope-handoff.snapshot.json`、`dev_host_scope_handoff_v1.schema.json`;111 fallback truth / model inventory / service posture 與 168 dev origin / repo hygiene / CORS / local exposure 已拆成 handoff | scope handoff `100%`;主機執行 `0%`;不 credentialed scan、不讀未授權資料、不改 fallback route、不改 CORS / firewall / service | | P1-9 | VibeWork 納入 IwoooS | 已補 `VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md`、`vibework-iwooos-onboarding-handoff.snapshot.json`、`vibework_iwooos_onboarding_handoff_v1.schema.json`;repo / product / surface / owner / evidence refs / 資料分級 / 部署邊界 / 獨立產品邊界已拆成 handoff | onboarding handoff `100%`;runtime / deploy / repo mutation `0 / false`;docs/specs 繁中,產品責任不合併 | | P1-10 | Docker / systemd / Host Service owner request draft | 已新增 `HOST-SERVICE-OWNER-REQUEST-DRAFT.md`、`host-service-owner-request-draft.snapshot.json` 與產生器;9 個 surface、3 個 write-capable、8 個需 live evidence、12 個 owner 必填欄位、14 類 blocked action | 草稿 artifact `100%`;request sent / received / accepted、live evidence、restart window、rollback owner、host write、runtime gate 仍全部 `0 / false` | +| P1-11 | SSH / Firewall / Network Access owner request draft | 已新增 `SSH-NETWORK-OWNER-REQUEST-DRAFT.md`、`ssh-network-owner-request-draft.snapshot.json` 與產生器;16 個 surface、6 個 write-capable、16 個需 live evidence、13 個 owner 必填欄位、16 類 blocked action | 草稿 artifact `100%`;request sent / received / accepted、live access state、keyscan、known_hosts patch、firewall / port change、NetworkPolicy / NodePort / WireGuard 變更、host write、runtime gate 仍全部 `0 / false` | ## 7. 2026-06-04 本輪驗證紀錄 diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 0c758a3f..e1729b45 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -99,6 +99,9 @@ def validate(root: Path) -> None: security_dir / "host-service-owner-request-draft.snapshot.json" ) ssh_network_access_inventory = load_json(security_dir / "ssh-network-access-inventory.snapshot.json") + ssh_network_owner_request_draft = load_json( + security_dir / "ssh-network-owner-request-draft.snapshot.json" + ) backup_restore_escrow_inventory = load_json(security_dir / "backup-restore-escrow-inventory.snapshot.json") monitoring_alerting_observability_inventory = load_json( security_dir / "monitoring-alerting-observability-inventory.snapshot.json" @@ -2936,6 +2939,128 @@ def validate(root: Path) -> None: [item["surface_id"] for item in ssh_network_access_inventory["write_capable_surfaces"]], surface_id, ) + assert_equal( + "ssh_network_owner_request_draft.schema", + ssh_network_owner_request_draft["schema_version"], + "ssh_network_owner_request_draft_v1", + ) + assert_equal( + "ssh_network_owner_request_draft.status", + ssh_network_owner_request_draft["status"], + "owner_request_draft_ready_not_dispatched", + ) + assert_equal( + "ssh_network_owner_request_draft.source_inventory_schema_version", + ssh_network_owner_request_draft["source_inventory_schema_version"], + "ssh_network_access_inventory_v1", + ) + expected_ssh_network_owner_request_summary = { + "request_draft_count": 16, + "write_capable_request_draft_count": 6, + "live_evidence_required_request_count": 16, + "request_field_count": 23, + "required_owner_field_count": 13, + "blocked_action_count": 16, + "request_sent_count": 0, + "recipient_confirmed_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "live_evidence_received_count": 0, + "maintenance_window_accepted_count": 0, + "rollback_owner_accepted_count": 0, + "validation_plan_accepted_count": 0, + "host_write_authorized_count": 0, + "ssh_read_authorized_count": 0, + "ssh_write_authorized_count": 0, + "host_keyscan_authorized_count": 0, + "known_hosts_patch_authorized_count": 0, + "firewall_change_authorized_count": 0, + "port_change_authorized_count": 0, + "network_policy_apply_authorized_count": 0, + "nodeport_change_authorized_count": 0, + "wireguard_change_authorized_count": 0, + "sudo_action_authorized_count": 0, + "deploy_ssh_action_authorized_count": 0, + "secret_value_collection_allowed_count": 0, + "ssh_key_collection_allowed_count": 0, + "active_scan_authorized_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + } + for key, expected in expected_ssh_network_owner_request_summary.items(): + assert_equal( + f"ssh_network_owner_request_draft.summary.{key}", + ssh_network_owner_request_draft["summary"][key], + expected, + ) + for key, value in ssh_network_owner_request_draft["execution_boundaries"].items(): + if key == "not_authorization": + assert_true(f"ssh_network_owner_request_draft.execution_boundaries.{key}", value) + else: + assert_false(f"ssh_network_owner_request_draft.execution_boundaries.{key}", value) + assert_equal( + "ssh_network_owner_request_draft.request_drafts.count", + len(ssh_network_owner_request_draft["request_drafts"]), + 16, + ) + ssh_network_owner_request_ids = [ + item["request_id"] for item in ssh_network_owner_request_draft["request_drafts"] + ] + for request_id in [ + "ssh_network_owner_request:gitea_cd_known_hosts_secret", + "ssh_network_owner_request:deploy_alerts_ssh_path", + "ssh_network_owner_request:host_ops_sudoers_wrapper", + "ssh_network_owner_request:argocd_metrics_nodeport", + "ssh_network_owner_request:wireguard_mesh_runbook", + "ssh_network_owner_request:alert_rules_ssh_actions", + ]: + assert_contains( + "ssh_network_owner_request_draft.request_drafts", + ssh_network_owner_request_ids, + request_id, + ) + for draft in ssh_network_owner_request_draft["request_drafts"]: + assert_equal( + f"ssh_network_owner_request_draft.{draft['request_id']}.required_owner_fields", + len(draft["required_owner_fields"]), + 13, + ) + assert_equal( + f"ssh_network_owner_request_draft.{draft['request_id']}.blocked_actions", + len(draft["blocked_actions"]), + 16, + ) + for false_key in [ + "request_sent", + "recipient_confirmed", + "owner_response_received", + "owner_response_accepted", + "live_evidence_received", + "maintenance_window_accepted", + "rollback_owner_accepted", + "validation_plan_accepted", + "host_write_authorized", + "ssh_read_authorized", + "ssh_write_authorized", + "host_keyscan_authorized", + "known_hosts_patch_authorized", + "firewall_change_authorized", + "port_change_authorized", + "network_policy_apply_authorized", + "nodeport_change_authorized", + "wireguard_change_authorized", + "sudo_action_authorized", + "deploy_ssh_action_authorized", + "secret_value_collection_allowed", + "ssh_key_collection_allowed", + "active_scan_authorized", + "runtime_gate", + "action_buttons_allowed", + ]: + assert_false( + f"ssh_network_owner_request_draft.{draft['request_id']}.{false_key}", + draft[false_key], + ) assert_equal( "backup_restore_escrow_inventory.schema", backup_restore_escrow_inventory["schema_version"], diff --git a/scripts/security/ssh-network-owner-request-draft.py b/scripts/security/ssh-network-owner-request-draft.py new file mode 100644 index 00000000..23610d8b --- /dev/null +++ b/scripts/security/ssh-network-owner-request-draft.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +""" +IwoooS SSH / Firewall / Network Access owner request draft 產生器。 + +本工具讀取 ssh-network-access repo-only 清冊,將 16 個 surface 轉成人工 +送件前 request draft。它不 SSH、不 keyscan、不讀 live firewall、不 patch +known_hosts、不套用 NetworkPolicy、不改 NodePort、不啟動 WireGuard。 +""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Any + + +TAIPEI = timezone(timedelta(hours=8)) + +REQUEST_FIELDS = [ + "request_id", + "surface_id", + "label", + "expected_scope", + "config_kind", + "access_scope", + "control_tier", + "repo_source_path", + "repo_sha256", + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", + "not_approval", +] + +REQUIRED_OWNER_FIELDS = [ + "owner_role_or_team", + "decision", + "decision_reason", + "affected_scope", + "redacted_evidence_refs", + "live_access_state_ref", + "allowed_source_cidrs_ref", + "maintenance_window", + "rollback_owner", + "validation_plan", + "break_glass_owner", + "change_freeze_rule", + "followup_owner", +] + +BLOCKED_ACTIONS = [ + "ssh_read", + "ssh_write", + "host_keyscan", + "known_hosts_patch", + "firewall_change", + "port_close", + "port_open", + "network_policy_apply", + "nodeport_change", + "wireguard_change", + "sudo_action", + "deploy_ssh_action", + "secret_value_collection", + "ssh_key_collection", + "active_scan", + "runtime_gate_open", +] + +WRITE_CAPABLE_KINDS = { + "ci_deploy_ssh", + "monitoring_ssh_deploy_script", + "sudoers_policy", + "alert_ssh_action_rules", +} + + +def git_short_sha(root: Path) -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "--short", "HEAD"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + except Exception: + return "unknown" + + +def load_json(path: Path) -> dict[str, Any]: + return json.loads(path.read_text(encoding="utf-8")) + + +def request_for(surface: dict[str, Any]) -> dict[str, Any]: + surface_id = surface["surface_id"] + write_capable = surface["config_kind"] in WRITE_CAPABLE_KINDS + return { + "request_id": f"ssh_network_owner_request:{surface_id}", + "status": "draft_not_dispatched", + "surface_id": surface_id, + "label": surface["label"], + "expected_scope": surface["expected_scope"], + "config_kind": surface["config_kind"], + "access_scope": surface["access_scope"], + "control_tier": surface["control_tier"], + "repo_source_path": surface["source_path"], + "repo_sha256": surface["sha256"], + "source_line_count": surface["line_count"], + "requires_live_evidence": surface["requires_live_evidence"], + "write_capable_surface": write_capable, + "source_inventory_ref": "docs/security/ssh-network-access-inventory.snapshot.json", + "request_fields": REQUEST_FIELDS, + "required_owner_fields": REQUIRED_OWNER_FIELDS, + "blocked_actions": BLOCKED_ACTIONS, + "owner_role_or_team": "pending_owner_role_or_team", + "decision": "pending_owner_decision", + "decision_reason": "pending_decision_reason", + "affected_scope": "pending_affected_scope", + "redacted_evidence_refs": [], + "live_access_state_ref": None, + "allowed_source_cidrs_ref": None, + "maintenance_window": "pending_maintenance_window", + "rollback_owner": "pending_rollback_owner", + "validation_plan": "pending_validation_plan", + "break_glass_owner": "pending_break_glass_owner", + "change_freeze_rule": "pending_change_freeze_rule", + "followup_owner": "pending_followup_owner", + "not_approval": True, + "request_sent": False, + "recipient_confirmed": False, + "owner_response_received": False, + "owner_response_accepted": False, + "live_evidence_received": False, + "maintenance_window_accepted": False, + "rollback_owner_accepted": False, + "validation_plan_accepted": False, + "host_write_authorized": False, + "ssh_read_authorized": False, + "ssh_write_authorized": False, + "host_keyscan_authorized": False, + "known_hosts_patch_authorized": False, + "firewall_change_authorized": False, + "port_change_authorized": False, + "network_policy_apply_authorized": False, + "nodeport_change_authorized": False, + "wireguard_change_authorized": False, + "sudo_action_authorized": False, + "deploy_ssh_action_authorized": False, + "secret_value_collection_allowed": False, + "ssh_key_collection_allowed": False, + "active_scan_authorized": False, + "runtime_gate": False, + "action_buttons_allowed": False, + } + + +def build_report(root: Path, inventory: dict[str, Any], generated_at: str | None) -> dict[str, Any]: + report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds") + requests = [request_for(surface) for surface in inventory["access_surfaces"]] + write_capable_requests = [item for item in requests if item["write_capable_surface"]] + live_evidence_requests = [item for item in requests if item["requires_live_evidence"]] + + return { + "schema_version": "ssh_network_owner_request_draft_v1", + "generated_at": report_time, + "git_commit": git_short_sha(root), + "source_inventory_schema_version": inventory.get("schema_version"), + "source_inventory_status": inventory.get("status"), + "status": "owner_request_draft_ready_not_dispatched", + "summary": { + "request_draft_count": len(requests), + "write_capable_request_draft_count": len(write_capable_requests), + "live_evidence_required_request_count": len(live_evidence_requests), + "request_field_count": len(REQUEST_FIELDS), + "required_owner_field_count": len(REQUIRED_OWNER_FIELDS), + "blocked_action_count": len(BLOCKED_ACTIONS), + "request_sent_count": 0, + "recipient_confirmed_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "live_evidence_received_count": 0, + "maintenance_window_accepted_count": 0, + "rollback_owner_accepted_count": 0, + "validation_plan_accepted_count": 0, + "host_write_authorized_count": 0, + "ssh_read_authorized_count": 0, + "ssh_write_authorized_count": 0, + "host_keyscan_authorized_count": 0, + "known_hosts_patch_authorized_count": 0, + "firewall_change_authorized_count": 0, + "port_change_authorized_count": 0, + "network_policy_apply_authorized_count": 0, + "nodeport_change_authorized_count": 0, + "wireguard_change_authorized_count": 0, + "sudo_action_authorized_count": 0, + "deploy_ssh_action_authorized_count": 0, + "secret_value_collection_allowed_count": 0, + "ssh_key_collection_allowed_count": 0, + "active_scan_authorized_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + }, + "execution_boundaries": { + "request_sent": False, + "recipient_confirmed": False, + "owner_response_received": False, + "owner_response_accepted": False, + "live_host_read_authorized": False, + "live_evidence_received": False, + "host_write_authorized": False, + "ssh_read_authorized": False, + "ssh_write_authorized": False, + "host_keyscan_authorized": False, + "known_hosts_patch_authorized": False, + "firewall_change_authorized": False, + "port_change_authorized": False, + "network_policy_apply_authorized": False, + "nodeport_change_authorized": False, + "wireguard_change_authorized": False, + "sudo_action_authorized": False, + "deploy_ssh_action_authorized": False, + "secret_value_collection_allowed": False, + "ssh_key_collection_allowed": False, + "active_scan_authorized": False, + "runtime_execution_authorized": False, + "action_buttons_allowed": False, + "not_authorization": True, + }, + "request_fields": REQUEST_FIELDS, + "required_owner_fields": REQUIRED_OWNER_FIELDS, + "blocked_actions": BLOCKED_ACTIONS, + "request_drafts": requests, + "next_steps": [ + "人工送件前確認 network / firewall / deploy owner role 與回覆窗口。", + "owner 只能提供脫敏 live access state、allowed source CIDR metadata、maintenance window、rollback owner 與 validation plan。", + "收到回覆後先做欄位完整性、敏感 payload 隔離、port close/open 影響範圍與 rollback gate 檢查,不得直接改 firewall 或套用 NetworkPolicy。", + ], + } + + +def main() -> int: + parser = argparse.ArgumentParser(description="IwoooS SSH / network owner request draft 產生器") + parser.add_argument("--root", default=".", help="repo root") + parser.add_argument( + "--inventory-report", + default="docs/security/ssh-network-access-inventory.snapshot.json", + help="ssh-network-access-inventory.py 輸出的 JSON", + ) + parser.add_argument("--output", help="寫出 JSON 報告") + parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用") + args = parser.parse_args() + + root = Path(args.root).resolve() + inventory = load_json(root / args.inventory_report) + report = build_report(root, inventory, args.generated_at) + payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + + if args.output: + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(payload + "\n", encoding="utf-8") + else: + print(payload) + + summary = report["summary"] + print( + "SSH_NETWORK_OWNER_REQUEST_DRAFT_OK " + f"drafts={summary['request_draft_count']} " + f"write_capable={summary['write_capable_request_draft_count']} " + f"fields={summary['required_owner_field_count']} " + f"sent={summary['request_sent_count']} " + f"runtime_gate={summary['runtime_gate_count']}", + file=sys.stderr, + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main())