---
title: C22 agent → agent-seaweedfs
eyebrow: Guidant AI 資安檢視總報告 · 連線清單
h1: C22 代理程式 → 證據暫存（agent-seaweedfs，S3 HTTP :8333）
lede: 代理程式把掃描報告先放在自己旁邊的那條線——同一台機器上的兩個容器。它不出客戶機房，掃描六頁也沒有任何一條落在它上面；但它存的是**稽核證據的原始本體**，而產品曾對它有「防篡改（WORM）」的想像。這一頁回答：這條線**怎麼連**、**哪些功能走它**、依特性**可能有什麼威脅**、我們**要怎麼防、目前做到哪**。
chips:
  - { text: "裝了 agent 才有", kind: plain }
  - { text: "同機・明文・S3 金鑰", kind: warn }
  - { text: "可能手法 3 種", kind: accent }
  - { text: "掃描命中 0 條", kind: plain }
---

> **這一頁怎麼來的**：[DFD Level 0](../DFD/dfd-level0.html#c22) 把產品運作時的連線編成 C01～C25；[STRIDE 六頁](../STRIDE/S-spoofing.html)每一條問題都標了發生在哪幾條線。**這條線零命中**——不是因為安全，是因為掃描範圍是主系統程式碼，agent 安裝包的 compose 與儲存設定不在裡面。第四段寫的是依這條線的特性可能的手法，沒有掃描實例。

## 一、連線圖

```{.mermaid cap="C22 — agent 到同機的 SeaweedFS。兩個容器在同一個 docker 網路，S3 埠預設只綁 127.0.0.1。agent 持有讀寫金鑰；雲端經 C21 從 agent 取檔，不直連 SeaweedFS。紅色是「持有金鑰者可以做」而目前沒擋的事。"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB'}}}%%
flowchart LR
    subgraph HOST["🏢 客戶機房・同一台主機（docker compose）"]
        direction LR
        AG["evidence-agent<br/>持 S3 讀寫金鑰"]
        subgraph SW["guidant-agent-seaweedfs"]
            direction TB
            S3["S3 gateway :8333<br/>（綁 127.0.0.1）"]
            BKT[("bucket guidant-evidence<br/>無版本控制・無 Object Lock")]
            S3 --> BKT
        end
        ROOT(["🧑‍💻 主機擁有者／root<br/>讀得到 .env 的金鑰<br/>碰得到 volume"])
        AG == "C22 · S3 HTTP :8333 · 金鑰簽章<br/>上傳報告・下載給雲端" ==> S3
        ROOT -. "同一組金鑰<br/>或直接改 volume" .-> BKT
    end
    API["guidant-api"]
    API -- "C21 · 取檔" --> AG
    classDef ext fill:#FBFCFC,stroke:#9AA8AA,stroke-dasharray:3 2
    classDef open fill:#FDECEC,stroke:#C0392B
    class ROOT ext
    class BKT open
```

## 二、這條線怎麼連

| 項目 | 現況 |
|---|---|
| **誰 → 誰** | `guidant-ai-agent` 容器 → 同一份 compose 裡的 `guidant-agent-seaweedfs` 容器。走 docker 內部網路，位址 `guidant-agent-seaweedfs:8333` |
| **協定／埠** | S3 相容 HTTP :8333（`SEAWEEDFS_SECURE` 預設 `false`，**明文**）。同機容器間，不經實體網路 |
| **對外暴露** | compose 把 8333（S3）、8888（filer）、9333（master）三個埠映射到主機，**預設綁 `127.0.0.1`**（`SEAWEEDFS_BIND_ADDR`），只有主機本機能連。改成 `0.0.0.0` 就全網可達 |
| **傳什麼** | 掃描報告（PDF／HTML／XML）、agent 自己的上傳檔。雲端要取時經 [C21](C21-api-to-agent-dataplane.html) 向 agent 要，agent 再從這裡讀——**雲端不直連 SeaweedFS** |
| **怎麼驗身分** | S3 存取金鑰（`SEAWEEDFS_ACCESS_KEY`／`SECRET_KEY`）。compose 設為**必填**（`:?` 語法），沒設容器起不來——刻意的，因為 SeaweedFS 沒給金鑰就是匿名全開。金鑰由 `install.sh` 產生寫進 `.env`，agent 與 SeaweedFS 兩個容器讀同一組 |
| **也能換成** | `STORAGE_TYPE=minio` 指向客戶自備的 S3 端點（MinIO 等），欄位同一套；或 `local` 落容器內磁碟（無網路）。預設 `seaweedfs` |
| **防篡改（WORM）** | **沒開**。`jedi-file-upload` 建桶只呼叫 `make_bucket()`，沒帶 `object_lock`、沒設 retention 或 legal hold、沒開版本控制。SeaweedFS 3.99 實測這些能力都有（物件層的鎖是真的），但**目前一項都沒用上**；且實測 `delete_bucket` 連同鎖住的物件一起消失——桶層不設防 |
| **何時存在** | 客戶有裝代理程式、且用預設儲存 |

依據：agent repo `deploy/docker-compose.yml`（兩容器定義、`:?` 必填、`127.0.0.1` 綁定）、`config/config.py`（`STORAGE_TYPE` 三選一）、`docs/fr066-6-t62-object-storage-and-worm.md`（WORM 實測與「目前根本沒開」結論）、`deploy/README.md` §5（「本產品不宣稱 WORM」）。

## 三、哪些功能會走這條線

| 群 | 功能 | 對威脅的意義 |
|---|---|---|
| **① 寫入** | 掃描完成，connector 產出的報告經 `_upload_report()` 寫進 bucket；雲端派工單的結果回報只帶檔案編號 | 寫進去的那一刻起，這份檔就是**稽核證據的原始本體**，雲端那份是從這裡複製過去的 |
| **② 讀出** | 雲端經 C21 `/blob/<uid>` 取檔、`/blob/<uid>/sha256` 對帳 | 雲端拿到後會算 sha256 當防竄改錨點（`upload_files.sha256`）——**錨點是取回那一刻算的**，取回前被換掉就錨到假的 |
| **③ 清理** | 目前沒有自動清理；第一期無容量上限 | 越掃越多，磁碟滿是可用性問題；但也意味著所有歷史證據都在這裡，一次拿走全部 |

## 四、依這條線的特性可能的攻擊手法（無掃描實例）

這條線零命中。但它的特性很清楚：**同一台機器、明文 S3、一組金鑰兩邊共用、持有金鑰者對桶內一切有完全控制**。依這些特性，可能的手法有三種。風險欄寫「—（無掃描實例）」。

| # | 風險 | 威脅 | 駭客怎麼打 | 得手什麼 | STRIDE | 實例 |
|---|---|---|---|---|---|---|
| T1 | —（無掃描實例） | **主機擁有者或拿到主機 root 的人，改掉或刪掉證據，不留痕** | ① 在 agent 主機上拿到 root（客戶自己的維運人員、或入侵了那台機器的人）；② 讀 `.env` 拿到 S3 金鑰，或直接進 `seaweed-data` volume；③ 覆寫某份掃描報告（例如把「3 個高風險」改成「0 個」）或刪掉它；④ 沒有版本控制、沒有 Object Lock，覆寫不留舊版、刪除不留紀錄；⑤ 雲端若還沒取回，取回時算的 sha256 錨的就是假檔 | 竄改或銷毀稽核證據，且事後查不出來 | [T 竄改資料](../STRIDE/T-tampering.html)、[R 事後無法追查](../STRIDE/R-repudiation.html) | — |
| T2 | —（無掃描實例） | **金鑰或埠外露，同網段的人直接讀整桶** | ① 客戶為了維運方便把 `SEAWEEDFS_BIND_ADDR` 改成 `0.0.0.0`，或 `.env` 被備份到共用位置；② 同內網的人用金鑰連 :8333，列出 bucket 全部物件；③ 下載所有歷史掃描報告——裡面是客戶每台主機的弱點清單、開放埠、組態缺失 | 客戶全部主機的弱點地圖（對攻擊者而言是現成的作戰計畫） | [I 資料外洩](../STRIDE/I-information-disclosure.html) | — |
| T3 | —（無掃描實例） | **塞爆磁碟讓 agent 停擺** | ① 拿到金鑰的人（或 T1 的 root）往 bucket 狂寫；② 或單純等——沒有容量上限也沒有清理，掃得夠多磁碟自己滿；③ agent 寫不進報告，掃描全部失敗，但雲端只看到「上傳失敗」 | 讓該客戶的所有檢測停擺 | [D 讓服務停擺](../STRIDE/D-denial-of-service.html) | — |

**三種手法的共同點**：全部以**拿到那台主機**為前提。這條線的本質是「資料存在客戶自己的機器上、用客戶自己機器上的金鑰保護」——**機器擁有者永遠能改底層資料**，這一點任何 S3 設定都擋不住（agent 部署手冊已明寫「本產品不宣稱 WORM」）。所以這條線的防線目標不是「讓機器擁有者改不了」，而是「改了會被發現」——那要靠雲端那頭盡早取回、算指紋、留副本。

## 五、我們要怎麼防、目前做到哪

零命中的線沒有「直接對應掃描手法」的防線，**五條全部是依連線特性補的標準防線，全標 ◇**。「目前」欄寫程式與部署裡實際有的機制；⚠️ 表示沒有機制保證。

| # | 防線 | 擋哪種威脅 | 目前做到哪 |
|---|---|---|---|
| D1 ◇ | **S3 埠只綁本機、金鑰必填、金鑰不入版控** | T2 | ✅ compose 預設 `127.0.0.1` 綁定；✅ 金鑰 `:?` 必填，缺了容器不起；✅ `install.sh` 產生隨機金鑰寫 `.env`（`.env.example` 寫明「勿沿用範例值」）。⚠️ 綁定位址可被客戶改成 `0.0.0.0`，沒有任何警告 |
| D2 ◇ | **證據寫進去就不可覆寫、不可刪**：建桶開 Object Lock（COMPLIANCE 模式）＋版本控制，寫入時設 retention；桶本身的刪除也要擋 | T1 | ⚠️ **沒有**。建桶只 `make_bucket()`，零 retention／versioning／legal hold。SeaweedFS 3.99 實測物件層鎖有效、**但 `delete_bucket` 會連鎖住的物件一起刪**——即使開了，持有金鑰者仍能整桶帶走。所以 D2 擋得住「改一份」，擋不住「全刪」；後者要靠 D3 |
| D3 ◇ | **雲端盡早取回、算指紋、留副本**：掃描完成的回報一到，雲端立刻經 C21 取檔、算 sha256 落庫；之後 agent 側的檔只是備份，被改也不影響雲端那份 | T1（根本解法） | ✅ 回報結果後雲端會取檔並記 `upload_files.sha256`（FR-039 防竄改錨點）。⚠️ 取回與回報之間有時間窗；取回失敗時（雲端連不到 8443）證據只存在 agent 側，窗口無限長。沒有「取回失敗要告警」的機制 |
| D4 ◇ | **容量上限與清理**：bucket 設配額或 agent 定期清已取回的檔；磁碟低於門檻告警 | T3 | ⚠️ 第一期**無容量上限、無清理**（設計文件明寫，清理指引在部署手冊，LRU 列 follow-up）。agent 上傳源碼包前有磁碟檢查（FR-058.7 D28），但那是「不夠就不掃」，不是「滿了會通知」 |
| D5 ◇ | **文件誠實**：對客戶與稽核單位不宣稱 WORM／不可刪，說清楚「證據的可信副本在雲端、agent 側是暫存」 | 期望落差（不是技術威脅，但稽核場景裡最傷） | ✅ 部署手冊 §5 明寫「機器擁有者始終能改動底層資料，本產品不宣稱 WORM」。⚠️ DFD Level 0 的 C22 列「傳什麼」欄寫「證據暫存、WORM」——與程式現況不符，已回寫母卡 |

**最該補的一步**：D3 的告警。D2（Object Lock）開了也擋不住整桶刪；真正讓證據可信的是雲端那份副本。所以「回報了但取不回」要響——這比在 agent 側疊加鎖便宜，也才是這條線的正確信任模型。

## 六、依這些防線，掃描還沒看過的地方

- **整個 agent 安裝包不在掃描範圍**：compose、`install.sh`、`.env` 的產生與權限、volume 的檔案權限——六頁 STRIDE 一條都沒有。這條線零命中是「沒掃」不是「沒事」。
- **取回失敗的處置**（D3）：雲端取檔失敗時任務狀態怎麼走、有沒有重試、有沒有告警，沒看。
- **`SEAWEEDFS_BIND_ADDR` 改動的後果**（D1）：文件有沒有警告客戶「改成 0.0.0.0 等於把弱點報告開給全網」，沒看。
- **MinIO 模式的 TLS**：客戶自備 S3 端點時 `MINIO_SECURE` 預設 `false`，跨機明文——與主系統 C07 同病（總表 #124 裁定接受），但 agent 側沒人裁過。

---

*依據：agent repo `deploy/docker-compose.yml`、`deploy/.env.example`、`deploy/README.md` §5、`config/config.py`、`docs/fr066-6-t62-object-storage-and-worm.md`（SeaweedFS 3.99 Object Lock 實測）、`core/task_executor.py`（`_upload_report`）；`jedi-file-upload` 全套件 grep `retention|ObjectLock|legal_hold|versioning` 零命中；DFD Level 0。*
