feat(security): 新增 DNS TLS 只讀清冊
All checks were successful
CD Pipeline / tests (push) Successful in 1m26s
Code Review / ai-code-review (push) Successful in 14s
CD Pipeline / build-and-deploy (push) Successful in 4m23s
CD Pipeline / post-deploy-checks (push) Successful in 1m54s

This commit is contained in:
Your Name
2026-06-11 18:40:18 +08:00
parent e8d5eafb9f
commit 32b553ee8f
11 changed files with 1921 additions and 0 deletions

View File

@@ -1,3 +1,39 @@
## 2026-06-11IwoooS DNS / TLS / certbot 只讀清冊
**背景**:統帥要求所有重要配置都要納入資安控管,尤其 Nginx 常被手動變動;前一階段已建立高價值配置分類 Gate 與 Nginx repo-only drift detector本階段接著把公開入口的 domain、TLS certificate path、ACME challenge、admin route、WebSocket 與 upstream 關係轉成可重跑清冊。
**本輪完成**
- 新增 `scripts/security/domain-tls-certbot-inventory.py`,從 `nginx-config-drift-repo.snapshot.json` 只讀推導 DNS / TLS / certbot 清冊;工具不做 DNS 查詢、不連線 TLS、不執行 certbot、不 SSH、不 reload Nginx。
- 新增 `docs/security/domain-tls-certbot-inventory.snapshot.json``docs/schemas/domain_tls_certbot_inventory_v1.schema.json`:盤點 3 份 Nginx source config、14 個 managed domain、10 條 certificate path、7 個 ACME challenge domain、1 個 admin route domain、6 個 WebSocket route domain。
- 新增 `docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md`,列出 4 個需要 owner 確認的 certificate path 關係:`gitea.wooo.work``langfuse.wooo.work``signoz.wooo.work` 使用 `sentry.wooo.work` path`tsenyang.com` 使用 `www.tsenyang.com` path這只是 owner confirmation gap不是 live TLS 失效結論。
- 更新 `iwooos-posture-projection.snapshot.json` / schema加入 DNS / TLS 清冊 summary 與 0 / false 邊界。
- 更新 `/zh-TW/iwooos`,新增 DNS / TLS / certbot 清冊只讀卡,顯示 domain、憑證路徑、owner 確認缺口、ACME 與禁止操作邊界;不提供 renew、reload、probe、批准或主機操作按鈕。
- 更新 `security-mirror-progress-guard.py`,固定驗證 snapshot、projection、前端 testid、邊界 marker 與 `zh-TW` / `en` 繁中鏡像。
**本地驗證**
- JSON parse 通過DNS / TLS snapshot、schema、IwoooS projection、projection schema、`zh-TW.json``en.json`
- `python3 -m py_compile scripts/security/domain-tls-certbot-inventory.py scripts/security/security-mirror-progress-guard.py` 通過。
- `python3 scripts/security/domain-tls-certbot-inventory.py --root .` 通過,輸出 `domains=14``cert_paths=10``owner_confirm=4``runtime_gate=0`
- `python3 scripts/security/security-mirror-progress-guard.py --root .` 通過。
- `python3 scripts/security/source-control-owner-response-guard.py --root .` 通過。
- `pnpm --filter @awoooi/web exec tsc --noEmit --tsBuildInfoFile /tmp/awoooi-web-domain-tls-iwooos-after-merge.tsbuildinfo` 通過。
- `git diff --check` 通過;`jsonschema` 套件本機仍未安裝,因此 schema 深驗證未執行。
- 本地 desktop `1280x720``http://127.0.0.1:3048/zh-TW/iwooos?_v=domain-tls-local-desktop`DNS / TLS 卡可見、邊界可展開、11 個 marker 全部命中、水平溢出 `false`、卡片內互動控制 `0`、內部協作內容外露 `false`
- 本地 mobile `390x844``http://127.0.0.1:3048/zh-TW/iwooos?_v=domain-tls-local-mobile`DNS / TLS 卡可見、明確捲動後邊界可展開、11 個 marker 全部命中、水平溢出 `false`、卡片內互動控制 `0`、內部協作內容外露 `false`
**完成度與邊界**
- DNS / TLS / certbot repo-only 清冊:`100%`
- owner confirmation queue`100%`
- IwoooS 前台只讀呈現:本地 `100%`,正式站 `待部署後補`
- live DNS / TLS validation`0%`;未批准且未執行。
- certbot renew / Nginx reload / host write`0%`;未批准且未執行。
- IwoooS 整體仍維持 `64%`active runtime gate 仍為 `0`owner response request / received / accepted 仍為 `0 / 0 / 0`
- 本階段未 SSH、未 active scan、未 DNS 修改、未 TLS probe、未 certbot renew、未 Nginx reload、未讀 secret value、未改 workflow、未建立 repo、未同步 refs、未 force push。
- Code commit、Gitea runs、deploy marker 與 production desktop / mobile verification 待推版後補。
## 2026-06-11IwoooS S4.9 Owner Response Metadata Intake 封套
**背景**S4.9 已有 handoff queue但 owner response 真正可進 gate 前仍缺 owner role / team、decision、decision reason、affected scope、redacted evidence refs、followup owner 的只讀收件欄位封套。本階段把 6 個欄位做成 source-bound metadata intake envelope讓 IwoooS 前台可見下一步要補哪些欄位,同時避免把欄位可見誤解為已收件、已接受或 runtime gate。

View File

@@ -0,0 +1,198 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:awoooi:domain-tls-certbot-inventory-v1",
"title": "IwoooS DNS / TLS / certbot 只讀清冊 v1",
"description": "定義從 Nginx source-of-truth 推導出的 domain、TLS certificate path、ACME challenge 與 owner 確認缺口。本契約不授權 DNS 修改、TLS renew、Nginx reload、SSH、host write 或 runtime execution。",
"type": "object",
"required": [
"schema_version",
"generated_at",
"mode",
"git_commit",
"source_nginx_report",
"execution_boundaries",
"summary",
"certificate_paths",
"managed_domains",
"owner_confirmation_required_domains",
"required_owner_fields",
"next_steps"
],
"properties": {
"schema_version": {
"const": "domain_tls_certbot_inventory_v1"
},
"generated_at": {
"type": "string"
},
"mode": {
"const": "repo_only_from_nginx_source_of_truth"
},
"git_commit": {
"type": "string"
},
"source_nginx_report": {
"const": "docs/security/nginx-config-drift-repo.snapshot.json"
},
"execution_boundaries": {
"type": "object",
"required": [
"dns_query_executed",
"live_tls_probe_executed",
"certbot_renew_executed",
"nginx_reload_executed",
"host_write_executed",
"runtime_gate_opened",
"secret_value_collected",
"action_buttons_allowed"
],
"properties": {
"dns_query_executed": { "const": false },
"live_tls_probe_executed": { "const": false },
"certbot_renew_executed": { "const": false },
"nginx_reload_executed": { "const": false },
"host_write_executed": { "const": false },
"runtime_gate_opened": { "const": false },
"secret_value_collected": { "const": false },
"action_buttons_allowed": { "const": false }
},
"additionalProperties": false
},
"summary": {
"type": "object",
"required": [
"source_config_count",
"managed_domain_count",
"unique_certificate_path_count",
"acme_challenge_domain_count",
"certificate_owner_confirmation_required_count",
"admin_route_domain_count",
"websocket_route_domain_count",
"owner_response_request_sent_count",
"owner_response_received_count",
"owner_response_accepted_count",
"runtime_gate_count",
"live_tls_probe_executed",
"dns_change_executed",
"certbot_renew_executed",
"nginx_reload_executed",
"action_buttons_allowed"
],
"properties": {
"source_config_count": { "type": "integer", "const": 3 },
"managed_domain_count": { "type": "integer", "minimum": 1 },
"unique_certificate_path_count": { "type": "integer", "minimum": 1 },
"acme_challenge_domain_count": { "type": "integer", "minimum": 0 },
"certificate_owner_confirmation_required_count": { "type": "integer", "minimum": 0 },
"admin_route_domain_count": { "type": "integer", "minimum": 0 },
"websocket_route_domain_count": { "type": "integer", "minimum": 0 },
"owner_response_request_sent_count": { "const": 0 },
"owner_response_received_count": { "const": 0 },
"owner_response_accepted_count": { "const": 0 },
"runtime_gate_count": { "const": 0 },
"live_tls_probe_executed": { "const": false },
"dns_change_executed": { "const": false },
"certbot_renew_executed": { "const": false },
"nginx_reload_executed": { "const": false },
"action_buttons_allowed": { "const": false }
},
"additionalProperties": false
},
"certificate_paths": {
"type": "array",
"items": { "type": "string" }
},
"managed_domains": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"domain",
"hosts",
"config_ids",
"source_paths",
"live_paths",
"control_tier",
"server_block_refs",
"listens",
"tls_certificate_path_present",
"certificate_paths",
"certificate_key_paths",
"certificate_path_domains",
"certificate_owner_confirmation_required",
"acme_challenge_present",
"acme_challenge_roots",
"upstreams",
"admin_route_count",
"websocket_route_count",
"live_tls_probe_status",
"dns_resolution_status",
"certbot_renewal_status",
"owner_review_status"
],
"properties": {
"domain": { "type": "string" },
"hosts": { "type": "array", "items": { "type": "string" } },
"config_ids": { "type": "array", "items": { "type": "string" } },
"source_paths": { "type": "array", "items": { "type": "string" } },
"live_paths": { "type": "array", "items": { "type": "string" } },
"control_tier": { "enum": ["C0", "C1", "C2", "C3"] },
"server_block_refs": { "type": "array", "items": { "type": "string" } },
"listens": { "type": "array", "items": { "type": "string" } },
"tls_certificate_path_present": { "type": "boolean" },
"certificate_paths": { "type": "array", "items": { "type": "string" } },
"certificate_key_paths": { "type": "array", "items": { "type": "string" } },
"certificate_path_domains": { "type": "array", "items": { "type": "string" } },
"certificate_owner_confirmation_required": { "type": "boolean" },
"acme_challenge_present": { "type": "boolean" },
"acme_challenge_roots": { "type": "array", "items": { "type": "string" } },
"upstreams": { "type": "array", "items": { "type": "string" } },
"admin_route_count": { "type": "integer", "minimum": 0 },
"websocket_route_count": { "type": "integer", "minimum": 0 },
"live_tls_probe_status": { "const": "not_executed" },
"dns_resolution_status": { "const": "not_executed" },
"certbot_renewal_status": { "const": "not_executed" },
"owner_review_status": {
"enum": [
"repo_only_ready_for_owner_review",
"repo_only_owner_confirmation_required",
"repo_only_tls_path_missing"
]
}
},
"additionalProperties": false
}
},
"owner_confirmation_required_domains": {
"type": "array",
"items": {
"type": "object",
"required": [
"domain",
"certificate_path_domains",
"tls_certificate_path_present",
"owner_review_status"
],
"properties": {
"domain": { "type": "string" },
"certificate_path_domains": { "type": "array", "items": { "type": "string" } },
"tls_certificate_path_present": { "type": "boolean" },
"owner_review_status": { "type": "string" }
},
"additionalProperties": false
}
},
"required_owner_fields": {
"type": "array",
"minItems": 9,
"items": { "type": "string" }
},
"next_steps": {
"type": "array",
"minItems": 1,
"items": { "type": "string" }
}
},
"additionalProperties": false
}

View File

@@ -172,6 +172,25 @@
"high_value_config_owner_packet_received_response_count",
"high_value_config_owner_packet_accepted_response_count",
"high_value_config_owner_packet_runtime_gate_count",
"domain_tls_certbot_inventory_first_layer",
"domain_tls_certbot_inventory_summary_count",
"domain_tls_certbot_inventory_item_count",
"domain_tls_certbot_inventory_source_config_count",
"domain_tls_certbot_inventory_managed_domain_count",
"domain_tls_certbot_inventory_unique_certificate_path_count",
"domain_tls_certbot_inventory_acme_challenge_domain_count",
"domain_tls_certbot_inventory_owner_confirmation_required_count",
"domain_tls_certbot_inventory_admin_route_domain_count",
"domain_tls_certbot_inventory_websocket_route_domain_count",
"domain_tls_certbot_inventory_request_sent_count",
"domain_tls_certbot_inventory_received_response_count",
"domain_tls_certbot_inventory_accepted_response_count",
"domain_tls_certbot_inventory_runtime_gate_count",
"domain_tls_certbot_inventory_live_tls_probe_executed",
"domain_tls_certbot_inventory_dns_change_executed",
"domain_tls_certbot_inventory_certbot_renew_executed",
"domain_tls_certbot_inventory_nginx_reload_executed",
"domain_tls_certbot_inventory_action_buttons_allowed",
"awooop_high_value_config_owner_packet_first_layer",
"awooop_high_value_config_owner_packet_summary_count",
"awooop_high_value_config_owner_packet_ref_count",
@@ -475,6 +494,82 @@
"type": "integer",
"const": 0
},
"domain_tls_certbot_inventory_first_layer": {
"type": "boolean",
"const": true
},
"domain_tls_certbot_inventory_summary_count": {
"type": "integer",
"const": 4
},
"domain_tls_certbot_inventory_item_count": {
"type": "integer",
"const": 5
},
"domain_tls_certbot_inventory_source_config_count": {
"type": "integer",
"const": 3
},
"domain_tls_certbot_inventory_managed_domain_count": {
"type": "integer",
"const": 14
},
"domain_tls_certbot_inventory_unique_certificate_path_count": {
"type": "integer",
"const": 10
},
"domain_tls_certbot_inventory_acme_challenge_domain_count": {
"type": "integer",
"const": 7
},
"domain_tls_certbot_inventory_owner_confirmation_required_count": {
"type": "integer",
"const": 4
},
"domain_tls_certbot_inventory_admin_route_domain_count": {
"type": "integer",
"const": 1
},
"domain_tls_certbot_inventory_websocket_route_domain_count": {
"type": "integer",
"const": 6
},
"domain_tls_certbot_inventory_request_sent_count": {
"type": "integer",
"const": 0
},
"domain_tls_certbot_inventory_received_response_count": {
"type": "integer",
"const": 0
},
"domain_tls_certbot_inventory_accepted_response_count": {
"type": "integer",
"const": 0
},
"domain_tls_certbot_inventory_runtime_gate_count": {
"type": "integer",
"const": 0
},
"domain_tls_certbot_inventory_live_tls_probe_executed": {
"type": "boolean",
"const": false
},
"domain_tls_certbot_inventory_dns_change_executed": {
"type": "boolean",
"const": false
},
"domain_tls_certbot_inventory_certbot_renew_executed": {
"type": "boolean",
"const": false
},
"domain_tls_certbot_inventory_nginx_reload_executed": {
"type": "boolean",
"const": false
},
"domain_tls_certbot_inventory_action_buttons_allowed": {
"type": "boolean",
"const": false
},
"awooop_high_value_config_owner_packet_first_layer": {
"type": "boolean",
"const": true

View File

@@ -0,0 +1,91 @@
# IwoooS DNS / TLS / certbot 只讀清冊
| 項目 | 內容 |
|------|------|
| 日期 | 2026-06-11 |
| 狀態 | `repo_only_inventory_ready` |
| 工具 | `scripts/security/domain-tls-certbot-inventory.py` |
| Snapshot | `docs/security/domain-tls-certbot-inventory.snapshot.json` |
| Schema | `docs/schemas/domain_tls_certbot_inventory_v1.schema.json` |
| runtime gate | `0` |
## 1. 目的
公開入口的風險不只在 Nginx conf 本身,也包含 domain、TLS certificate path、ACME challenge path、admin route、WebSocket 與 upstream 的對應關係。本清冊把這些關係從已提交的 Nginx source-of-truth 轉成可重跑的只讀證據,讓 owner review 有一致欄位。
本階段不做 DNS 查詢、不連線 TLS、不執行 certbot、不 SSH、不讀 TLS private key 內容、不 reload Nginx、不改主機。
## 2. 目前 repo-only 摘要
| 指標 | 值 |
|------|----|
| Nginx source config | `3` |
| managed domain | `14` |
| TLS certificate path | `10` |
| ACME challenge domain | `7` |
| 需要 owner 確認的 certificate path 關係 | `4` |
| admin route domain | `1` |
| WebSocket route domain | `6` |
| owner request / received / accepted | `0 / 0 / 0` |
| runtime gate | `0` |
## 3. 需要 owner 確認的項目
以下不是判定錯誤,也不是 live TLS 失效結論;它只表示 repo template 的 `server_name` 與 certificate path 目錄名稱不同,需 owner 確認是否由 SAN、wildcard 或共用憑證合法覆蓋。
| domain | certificate path domain | 狀態 |
|--------|--------------------------|------|
| `gitea.wooo.work` | `sentry.wooo.work` | 需 owner 確認 |
| `langfuse.wooo.work` | `sentry.wooo.work` | 需 owner 確認 |
| `signoz.wooo.work` | `sentry.wooo.work` | 需 owner 確認 |
| `tsenyang.com` | `www.tsenyang.com` | 需 owner 確認 |
## 4. owner response 欄位
任何 domain / TLS / certbot 變更或確認,都至少要具備:
1. `owner_role_or_team`
2. `decision`
3. `decision_reason`
4. `affected_scope`
5. `redacted_evidence_refs`
6. `followup_owner`
7. `rollback_owner`
8. `maintenance_window`
9. `validation_plan`
## 5. 指令
更新 snapshot
```bash
python3 scripts/security/domain-tls-certbot-inventory.py \
--root . \
--generated-at 2026-06-11T18:40:00+08:00 \
--output docs/security/domain-tls-certbot-inventory.snapshot.json
```
只輸出目前清冊:
```bash
python3 scripts/security/domain-tls-certbot-inventory.py --root .
```
## 6. 邊界
1. 本清冊不執行 DNS query。
2. 本清冊不做 live TLS probe。
3. 本清冊不執行 certbot renew。
4. 本清冊不讀 TLS private key 內容,只記錄 certificate / key path metadata。
5. 本清冊不 SSH、不 reload / restart Nginx、不修改主機、不改 DNS。
6. IwoooS UI 可顯示 domain / TLS 清冊,但不得把清冊可見視為 owner 已確認或 runtime 已授權。
## 7. 完成度
| 工作 | 完成度 | 說明 |
|------|--------|------|
| repo-only domain / TLS 清冊 | `100%` | 已從 Nginx source-of-truth 產出 domain、cert path、ACME、admin、WebSocket 與 upstream 摘要 |
| owner confirmation queue | `100%` | 已列出 4 個需確認的 certificate path 關係 |
| IwoooS 前台只讀呈現 | `待本輪部署驗證` | 只顯示摘要與邊界,不提供操作按鈕 |
| live DNS / TLS validation | `0%` | 尚未批准;不得用本清冊替代 live probe |
| certbot renew / Nginx reload | `0%` | 未授權,未執行 |

View File

@@ -0,0 +1,768 @@
{
"certificate_paths": [
"/etc/letsencrypt/live/aiops.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/bitan.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/gitlab.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/harbor.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/mo.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/registry.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/sentry.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/stock.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/vtuber.wooo.work/fullchain.pem",
"/etc/letsencrypt/live/www.tsenyang.com/fullchain.pem"
],
"execution_boundaries": {
"action_buttons_allowed": false,
"certbot_renew_executed": false,
"dns_query_executed": false,
"host_write_executed": false,
"live_tls_probe_executed": false,
"nginx_reload_executed": false,
"runtime_gate_opened": false,
"secret_value_collected": false
},
"generated_at": "2026-06-11T18:40:00+08:00",
"git_commit": "99efc627",
"managed_domains": [
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/aiops.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"aiops.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/aiops.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "aiops.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#1",
"host188_all_sites#2"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.125:32334/api/",
"http://192.168.0.125:32334/api/v1/ws",
"http://192.168.0.125:32335"
],
"websocket_route_count": 1
},
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/bitan.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"bitan.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/bitan.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "bitan.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#12",
"host188_all_sites#13"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:3003"
],
"websocket_route_count": 1
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/certbot"
],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": true,
"certificate_path_domains": [
"sentry.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "gitea.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_owner_confirmation_required",
"server_block_refs": [
"host188_internal_tools_https#1",
"host188_internal_tools_https#5"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:3001"
],
"websocket_route_count": 1
},
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/gitlab.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"gitlab.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/gitlab.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "gitlab.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#3",
"host188_all_sites#4"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:8929"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/certbot"
],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/harbor.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"harbor.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/harbor.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "harbor.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_internal_tools_https#1",
"host188_internal_tools_https#7"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:5000"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/certbot"
],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": true,
"certificate_path_domains": [
"sentry.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "langfuse.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_owner_confirmation_required",
"server_block_refs": [
"host188_internal_tools_https#1",
"host188_internal_tools_https#6"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:3100"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/mo.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"mo.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/mo.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "mo.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#10",
"host188_all_sites#11"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://127.0.0.1:5003"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/certbot"
],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/registry.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"registry.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/registry.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "registry.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_internal_tools_https#1",
"host188_internal_tools_https#8"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:5000"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/certbot"
],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"sentry.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "sentry.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_internal_tools_https#1",
"host188_internal_tools_https#4"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:9000"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": true,
"certificate_path_domains": [
"sentry.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/sentry.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites",
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "signoz.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf",
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_owner_confirmation_required",
"server_block_refs": [
"host188_all_sites#5",
"host188_internal_tools_https#2"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2",
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://127.0.0.1:3301"
],
"websocket_route_count": 1
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/certbot",
"/var/www/html"
],
"admin_route_count": 2,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/stock.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"stock.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/stock.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites",
"host188_internal_tools_https"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "stock.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf",
"owner_confirmation_required"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#8",
"host188_all_sites#9",
"host188_internal_tools_https#1",
"host188_internal_tools_https#3"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2",
"infra/ansible/roles/nginx/templates/188-internal-tools-https.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://192.168.0.110:31235"
],
"websocket_route_count": 2
},
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/www.tsenyang.com/privkey.pem"
],
"certificate_owner_confirmation_required": true,
"certificate_path_domains": [
"www.tsenyang.com"
],
"certificate_paths": [
"/etc/letsencrypt/live/www.tsenyang.com/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "tsenyang.com",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_owner_confirmation_required",
"server_block_refs": [
"host188_all_sites#6",
"host188_all_sites#7"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://127.0.0.1:3000"
],
"websocket_route_count": 0
},
{
"acme_challenge_present": true,
"acme_challenge_roots": [
"/var/www/html"
],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/vtuber.wooo.work/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"vtuber.wooo.work"
],
"certificate_paths": [
"/etc/letsencrypt/live/vtuber.wooo.work/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "vtuber.wooo.work",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#14",
"host188_all_sites#15"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"https://192.168.0.110"
],
"websocket_route_count": 1
},
{
"acme_challenge_present": false,
"acme_challenge_roots": [],
"admin_route_count": 0,
"certbot_renewal_status": "not_executed",
"certificate_key_paths": [
"/etc/letsencrypt/live/www.tsenyang.com/privkey.pem"
],
"certificate_owner_confirmation_required": false,
"certificate_path_domains": [
"www.tsenyang.com"
],
"certificate_paths": [
"/etc/letsencrypt/live/www.tsenyang.com/fullchain.pem"
],
"config_ids": [
"host188_all_sites"
],
"control_tier": "C0",
"dns_resolution_status": "not_executed",
"domain": "www.tsenyang.com",
"hosts": [
"192.168.0.188"
],
"listens": [
"443 ssl http2",
"80"
],
"live_paths": [
"/etc/nginx/sites-enabled/all-sites.conf"
],
"live_tls_probe_status": "not_executed",
"owner_review_status": "repo_only_ready_for_owner_review",
"server_block_refs": [
"host188_all_sites#6",
"host188_all_sites#7"
],
"source_paths": [
"infra/ansible/roles/nginx/templates/188-all-sites.conf.j2"
],
"tls_certificate_path_present": true,
"upstreams": [
"http://127.0.0.1:3000"
],
"websocket_route_count": 0
}
],
"mode": "repo_only_from_nginx_source_of_truth",
"next_steps": [
"請 owner 確認 certificate path 是否由 SAN 或 wildcard 合法覆蓋;未確認前不得 renew 或 reload。",
"未來若要做 live TLS / DNS probe需另行 scope approval本清冊只保留 repo-only 證據。",
"任何 certbot renew、Nginx reload 或 DNS 變更都必須另開維護窗口、rollback owner 與 post-check。"
],
"owner_confirmation_required_domains": [
{
"certificate_path_domains": [
"sentry.wooo.work"
],
"domain": "gitea.wooo.work",
"owner_review_status": "repo_only_owner_confirmation_required",
"tls_certificate_path_present": true
},
{
"certificate_path_domains": [
"sentry.wooo.work"
],
"domain": "langfuse.wooo.work",
"owner_review_status": "repo_only_owner_confirmation_required",
"tls_certificate_path_present": true
},
{
"certificate_path_domains": [
"sentry.wooo.work"
],
"domain": "signoz.wooo.work",
"owner_review_status": "repo_only_owner_confirmation_required",
"tls_certificate_path_present": true
},
{
"certificate_path_domains": [
"www.tsenyang.com"
],
"domain": "tsenyang.com",
"owner_review_status": "repo_only_owner_confirmation_required",
"tls_certificate_path_present": true
}
],
"required_owner_fields": [
"owner_role_or_team",
"decision",
"decision_reason",
"affected_scope",
"redacted_evidence_refs",
"followup_owner",
"rollback_owner",
"maintenance_window",
"validation_plan"
],
"schema_version": "domain_tls_certbot_inventory_v1",
"source_nginx_report": "docs/security/nginx-config-drift-repo.snapshot.json",
"summary": {
"acme_challenge_domain_count": 7,
"action_buttons_allowed": false,
"admin_route_domain_count": 1,
"certbot_renew_executed": false,
"certificate_owner_confirmation_required_count": 4,
"dns_change_executed": false,
"live_tls_probe_executed": false,
"managed_domain_count": 14,
"nginx_reload_executed": false,
"owner_response_accepted_count": 0,
"owner_response_received_count": 0,
"owner_response_request_sent_count": 0,
"runtime_gate_count": 0,
"source_config_count": 3,
"unique_certificate_path_count": 10,
"websocket_route_domain_count": 6
}
}

View File

@@ -22,6 +22,9 @@
"docs/security/HIGH-VALUE-CONFIG-OWNER-PACKET.md",
"docs/security/high-value-config-change-gate.snapshot.json",
"docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md",
"docs/security/domain-tls-certbot-inventory.snapshot.json",
"docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md",
"docs/schemas/domain_tls_certbot_inventory_v1.schema.json",
"docs/LOGBOOK.md",
"docs/workplans/2026-06-04-iwooos-security-governance-p0.md",
"apps/web/src/app/[locale]/iwooos/page.tsx",
@@ -234,6 +237,25 @@
"high_value_config_owner_packet_received_response_count": 0,
"high_value_config_owner_packet_accepted_response_count": 0,
"high_value_config_owner_packet_runtime_gate_count": 0,
"domain_tls_certbot_inventory_first_layer": true,
"domain_tls_certbot_inventory_summary_count": 4,
"domain_tls_certbot_inventory_item_count": 5,
"domain_tls_certbot_inventory_source_config_count": 3,
"domain_tls_certbot_inventory_managed_domain_count": 14,
"domain_tls_certbot_inventory_unique_certificate_path_count": 10,
"domain_tls_certbot_inventory_acme_challenge_domain_count": 7,
"domain_tls_certbot_inventory_owner_confirmation_required_count": 4,
"domain_tls_certbot_inventory_admin_route_domain_count": 1,
"domain_tls_certbot_inventory_websocket_route_domain_count": 6,
"domain_tls_certbot_inventory_request_sent_count": 0,
"domain_tls_certbot_inventory_received_response_count": 0,
"domain_tls_certbot_inventory_accepted_response_count": 0,
"domain_tls_certbot_inventory_runtime_gate_count": 0,
"domain_tls_certbot_inventory_live_tls_probe_executed": false,
"domain_tls_certbot_inventory_dns_change_executed": false,
"domain_tls_certbot_inventory_certbot_renew_executed": false,
"domain_tls_certbot_inventory_nginx_reload_executed": false,
"domain_tls_certbot_inventory_action_buttons_allowed": false,
"awooop_high_value_config_owner_packet_first_layer": true,
"awooop_high_value_config_owner_packet_summary_count": 4,
"awooop_high_value_config_owner_packet_ref_count": 4,