Skip to main content

qnap-nas-cli

CI License: MIT

**支援範圍:**唯讀 QTS 診斷 CLI 與 MCP;已審查基線為 TS-431K / QTS 5.2.8,其他機型/韌體可能回報 unknown。請見相容性表與觀察資料契約。

**支援政策:**僅支援最新的 main 與最新 release。

**Mutation 尚未啟用:**沒有生產用 mutation adapter;MCP 不提供通用 shell 或任意 QTS API 工具。0.4.0 可用 pipx 或 pip 安裝;安裝方式見安裝指南。維護與發布流程見貢獻指南及發布說明。

安全優先、零第三方 Python 相依的 QNAP QTS 診斷 CLI 與唯讀 MCP Server。目標是讓人與 AI agent 都能取得可稽核的 NAS 健康證據,而不是在缺乏實機驗證時直接修改 RAID、磁碟或網路服務。

目前版本為 0.4.0,主要相容目標為 QTS 5.x 與 TS-431K。QTS CGI 欄位會因機型、韌體及安裝套件而異;請使用 qts dump 提供已遮蔽的相容性樣本。

目前狀態

  • 初始唯讀診斷版本已於 2026-11-03 發布到 main。
  • CI 執行離線測試、Python bytecode compilation 與 CLI smoke test。TS-431K 的 File Station reviewed fixture,以及 QTS web status/health 與 repair plan/apply 回傳 refused,已於 2026-11-06 完成驗證;第二管理帳號仍未驗證,因此 mutation gate 仍關閉,詳見 docs/LIVE-DRY-RUN-TS431K.md。
  • TS-431K 也已於 2026-11-06 完成排程健康報告、HTTPS Webhook 成功/失敗隔離,以及 Evidence Capsule v2 的實機驗證:manifest hash/size 均吻合、重複目錄 fail closed、Webhook 只含 allowlisted codes,且本次 10,000-item 掃描因 coverage partial/truncated、沒有 IOC examples 而未匯出樣本。
  • GitHub Actions 在 Python 3.8、3.12、3.13 驗證:CI workflow。
  • CI 固定使用 Ubuntu 22.04,因為 GitHub 的 Ubuntu 26 runner 不再提供 Python 3.8;這是為了保留較舊 NAS/管理環境的相容性,不是測試降級。
  • 已用 TS-431K、QTS 5.2.8 build 20251216 完成 2FA 與唯讀 SSH live smoke test,並納入人工審查的 system/volume/SMART、mdstat 與實體磁碟拓樸 fixture。QTS 在此機型回報的內部 model 是 TS-X41。
  • 0.4.0 包含 opt-in HTTPS webhook、qts report health(外部 cron/systemd timer,無 daemon)與 atomic Evidence Capsule,並已完成 TS-431K 實機驗證與 Windows atomic 相容修正。mutation gate 仍關閉;第二管理帳號與第二機型仍未驗證。

維護者:popuchoco <p.choco@daum.net>

功能

  • setup:保存 HTTPS、帳號與選用的 SSH 主機;不保存密碼。
  • doctor/whoami:檢查本機條件並驗證 QTS 登入。
  • status:型號、韌體、CPU、記憶體、溫度與 uptime。
  • health:唯讀彙整磁碟、Volume、已知異常 finding 與資料不足 unknown;healthy、unhealthy、indeterminate 不混為一談,回報使用中的通用溫度門檻。
  • report health:將同一份唯讀 health observation 遮蔽後寫入新/空的本機目錄,包含時間戳記 JSON、只列 codes 的 Markdown 摘要與 SHA-256 manifest;可由外部 cron 或 systemd timer 呼叫。
  • storage/disks/volumes:合併 Volume、sysinfo bay 與 qsmart.cgi SMART 摘要;可重複指定 --volume-index N,但尚無 endpoint 可證明全部 index 已查齊。
  • network:介面、IP、速度與 DNS。
  • tailscale:從已驗證的 QPKG 安裝路徑執行固定的 tailscale status --json。
  • raid:直接讀取並解析 /proc/mdstat,標示 degraded、member state 與 recovery/resync 進度;QNAP 128-wide bitmap 保守標為未知語義。
  • disk-topology:用 qcli_storage -d 讀取實體 bay、data/spare 角色、容量與型號。
  • smart-details DISK:以已驗證的 bay 編號讀取 QNAP SMART attributes;韌體空回應會明確標為 unavailable。
  • services:唯讀觀察 Container Station/Tailscale QPKG metadata,不把 Status=complete 誤稱為 daemon 正常運行。
  • dump:匯出已遮蔽 hostname、序號、IP 與 DNS 的相容性樣本。
  • fixture validate:不連 NAS,離線檢查 dump 能否解析及是否存在未知欄位/狀態碼。
  • incident scan:有上限地遞迴掃描 File Station 可見共享資料夾的檔名,檢查 Qlocker/DeadBolt 已知指標,並彙整 health、RAID 與套件 observation;報告會標記 truncated、depth_limited 與截斷原因。
  • Webhook 通知:可選擇以 HTTPS 將 health 異常、選用的 indeterminate 結果與疑似 incident 通知至 operator-configured endpoint;預設停用、失敗不影響觀察結果,payload 只含遮蔽後的 allowlisted 欄位。
  • incident contain:由 report 產生人工網路隔離/取證清單;不改 NAS 防火牆或停止程序。
  • incident evidence:以 private staging 和 atomic directory publish 保存遞迴遮蔽後的 report、codes-only summary.md 與 format v2 SHA-256 manifest;可重複指定最多 100 個 --attach-candidate,但每個路徑都必須精確出現在 report 的 indicator examples,並受 --max-bytes 合計上限限制。
  • incident rescue:輸出離線備份復原步驟;也可將 report 已列出的可疑檔案唯讀串流匯出到新的本機 case directory,產生 SHA-256,不會解密、執行或回寫 NAS。
  • repair plan/repair apply:具備確認碼、短效 plan、裝置身分/前置條件/before-state/readback 驗證的 fail-closed 框架;目前沒有生產用 QTS mutation adapter,所有 CLI plan 都會明確拒絕執行,apply 不會改 NAS。
  • mcp:提供十個唯讀 stdio MCP tools。
  • 全部資料命令支援 --json。

Evidence capsule 僅是本機有限摘要,不是完整取證;不保證涵蓋 NAS 原始日誌,也不包含 disk image。附件是唯讀樣本匯出,不會寫回、執行或解密 NAS 檔案;rescue/--attach-candidate 匯出的樣本一律存成安全檔名 samples/sample-NNN.bin(非原始 NAS 路徑),原始 File Station 路徑保留在 manifest/receipt 的 source 欄位。capsule 輸出目錄必須是新的或空目錄,拒絕 symlink、非空目錄、root、目前工作目錄與 home 目錄。

本版不提供 RAID 重建、SMART 修復、Container Station 停止、Tailscale 重啟、惡意程序 suspend/kill、QTS 防火牆修改、惡意檔案刪除或 NAS 上直接還原。通用 plan/apply 執行引擎可由測試用 adapter 驗證,但正式 adapter 必須先有指定機型/韌體的實機契約測試、前置條件與第二管理路徑、before/after readback、失敗中止與回復程序。

安裝

需求為 Python 3.8 以上,執行時不需要第三方套件。套件已發布到 PyPI;pipx、venv/pip、Git checkout 與離線 wheel 的安裝方式見安裝指南。

pipx install qnap-nas-cli
qts --help
qts --version

也可在虛擬環境中用 pip 安裝,或直接從 checkout 執行。完整步驟見安裝指南:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install qnap-nas-cli

# 從 checkout 開發安裝
git clone https://github.com/popuchoco/qnap-nas-cli.git
cd qnap-nas-cli
python -m pip install -e .

# 或不安裝,直接執行
python -m qnap_nas_cli --help

快速開始

QTS 應使用有效 HTTPS 憑證。--insecure 只適合受信任區網內的暫時測試。

qts setup --origin https://192.0.2.10 --user ops \
  --ssh-host 192.0.2.10 --ssh-user ops \
  --ssh-identity-file ~/.ssh/qnap_ops

# 互動式輸入密碼;若帳號啟用 2FA,CLI 會接著提示 TOTP
qts status
qts health
qts report health --directory qts-health-report
qts storage --json
qts storage --volume-index 1 --volume-index 2 --json
qts network
qts raid --json
qts disk-topology --json
qts smart-details 2 --json
qts services --json

# 勒索事件:有限掃描,建立 atomic 本機 evidence capsule
qts incident scan --max-items 10000 --evidence-dir case-2026-01 --json
qts incident contain case-2026-01/incident.json --json
qts incident rescue case-2026-01/incident.json --json
# 後續可將 report 中精確列出的 IOC 範例附加到同一 capsule;合計上限預設 100 MiB。
qts incident evidence case-2026-01/incident.json case-with-sample \
  --attach-candidate '/Public/!!!READ_ME.txt' --max-bytes 104857600
# Rescue 同樣只接受 report 已列出的 IOC 範例,並存成 samples/sample-NNN.bin;不是 NAS restore。
qts incident rescue case-2026-01/incident.json --candidate-path '/Public/!!!READ_ME.txt' --directory case-sample

# 一般修復流程目前只會建立明確拒絕執行的計畫,不會修改 NAS
qts repair plan --action service-stop --output qts-repair-plan.json --json
qts repair apply qts-repair-plan.json --confirm '<plan_id>' --json

# 自動化或 MCP;不要把密碼寫進 shell history
QTS_PASSWORD='...' qts --json health
QTS_PASSWORD='...' qts mcp

# 啟用 2FA 的帳號:驗證碼只存在當次 process environment
QTS_PASSWORD='...' QTS_SECURITY_CODE='…' qts --json health

# 產生已遮蔽的相容性資料
qts dump qts-dump
# 可選:額外擷取已遮蔽 File Station;提交前仍須逐次人工審閱
qts dump --filestation qts-dump
qts fixture validate qts-dump --json

若 stdin 不是終端,CLI 也能從 stdin 第一行讀取密碼。互動終端在 QTS 回報需要 2FA 時才提示 TOTP;非互動與 MCP 必須注入 QTS_SECURITY_CODE。密碼與驗證碼不接受命令列參數,也不會寫入設定檔。自動化注入與 shell history 安全說明見憑證注入指南。

排程請由外部 cron 或 systemd timer 呼叫 qts report health,並為每次執行提供新的輸出目錄。範例與 webhook 排程政策見排程健康報告。

MCP

{
  "mcpServers": {
    "qnap-nas": {
      "command": "qts",
      "args": ["mcp"],
      "env": {"QTS_PASSWORD": "由安全的秘密管理工具注入"}
    }
  }
}

提供的 tools:

  • qts_status
  • qts_health
  • qts_storage
  • qts_network
  • qts_tailscale
  • qts_raid
  • qts_smart_details
  • qts_disk_topology
  • qts_services
  • qts_incident(有限名稱 IOC 掃描;assessment 不會回報 clean)

MCP 目前刻意只讀,不提供通用 shell 或任意 QTS API 呼叫。

Health 語義

health 不把「沒有發現異常」直接等同「已證明健康」:

  • status: healthy/healthy: true:必要狀態完整,而且觀察值沒有已知異常。
  • status: unhealthy/healthy: false:存在已知異常,例如 degraded Volume、明確 SMART warning 或過熱。
  • status: indeterminate/healthy: null:資料缺少、vol_status=0、未知狀態碼,或 parser 沒找到必要記錄。

當結果為 indeterminate 時也會輸出 healthy_claim: not_permitted;這不是「可能健康」的捷徑,而是明確表示目前證據不足、不能宣告 healthy。Storage 的 limitations 會列出未驗證的 volume coverage 與 SMART 深度限制。

已知異常放在 findings;資料不足放在 unknowns。兩者不互相替代。原始 status_code 仍保留,方便用真實 fixture 校準新韌體。

Volume 可用 --volume-index 重複指定,也可在 MCP qts_storage/qts_health 傳 volume_indices;預設仍為 index 1。沒有經實機核對的 index discovery API,因此即使查了多個 index,coverage 一樣是 unverified。系統 70°C、磁碟 55°C 是報告揭露的 generic default,不是經各機型校正的安全界線。

Volume 查詢目前固定使用 func=extra_get&extra_vol_index=1,回應仍標記 source.coverage: unverified。磁碟則合併 manaRequest.cgi 的 bay/溫度與 qsmart.cgi?func=all_hd_data 的型號、容量、序號遮蔽值與頂層 Health。TS-431K 實機的 vol_status=0 尚未有足夠證據映射成 healthy,因此整體仍為 indeterminate。

注意:qsmart.cgi 的頂層 Health=OK/Normal 不代表所有 SMART attribute、歷史 I/O 錯誤或 QTS 告警都正常。例如實機 HDD2 曾由 QTS UI 報告健康度不佳,但此次頂層欄位為 Normal;CLI 不會用它覆蓋或否定較具體的告警。

Webhook 通知

Webhook 只在設定 QTS_WEBHOOK_URL 時啟用;URL 是 operator-local secret,僅接受 HTTPS,並使用預設 TLS 憑證驗證。health 的 unhealthy 及 incident scan 的 suspected 會通知;indeterminate 預設略過,可用 QTS_WEBHOOK_ON_INDETERMINATE=true 選擇啟用。可用 --webhook/--no-webhook 覆寫本次 CLI 呼叫。通知只會送出型號、韌體、build、有限 finding/unknown 摘要;findings 只含 code 與選用的 severity,unknowns 只含 code,不會送出自由文字訊息。Payload 不含憑證、hostname、序號、IP、share 名稱或 evidence 路徑。Delivery 使用短 timeout、不跟隨 redirect、不重試;錯誤不改變成功 observation 的 exit code。環境變數與秘密管理方式見憑證注入指南。Mutation gate 仍關閉。

TS-431K 的 /sbin/get_hd_smartinfo -d 1..4 實測皆為空,而且系統未安裝 smartctl;因此 smart-details 回傳 data_status: unavailable_on_firmware,不會假造 attributes。md9/md13 的 [128/3] QNAP wide bitmap 也不套用一般 Linux md 的底線判讀,degraded 會是 null。

真實 dump 與 fixture 流程

# 在受信任管理電腦上連線 NAS,輸出已遮蔽資料
qts dump qts-dump-ts431k

# 離線確認 XML 可解析,並列出未知狀態
qts fixture validate qts-dump-ts431k --json

dump v2 會保存 sysinfo.xml、storage.xml、smart.xml 與版本化 manifest。提交 fixture 前仍應人工檢查內容;驗證通過只代表結構可解析,不代表 NAS 健康。已審查的實機資料位於 tests/fixtures/ts431k-qts-5.2.8-build-20251216,手寫 XML 只用於 edge cases。

Incident / repair 工作流(目前為受限 v1)

qts incident scan 使用 QNAP File Station API 的 get_tree/get_list 做 bounded read-only inventory:最多掃描 100,000 個項目、深度 12;預設 10,000。僅掃描登入帳號可見的共享資料夾名稱,不讀內容、不掃程序/行為/系統日誌,也不代表完整 filesystem coverage。報告的 coverage.truncated、coverage.depth_limited 與 coverage.truncation_reasons 會明確標示項目數或深度上限造成的覆蓋缺口;partial/截斷掃描的無命中尤其不能解讀為乾淨。已知規則包含 .deadbolt、Qlocker 的 !!!READ_ME.txt,以及大量 .7z 的低信心群聚;.7z 群聚可能是正常備份,單獨不會升級成 suspected,需搭配勒索字條、DeadBolt 指標、QTS 告警或其他獨立異常證據。無命中永遠回 indeterminate,不回 clean;命中代表疑似而非確診。官方事件說明、公告與 API 規格列於下方參考資料。

應變分工:

  1. scan:保存有限掃描、health、可用的 RAID/QPKG observation 與 coverage gaps。
  2. contain:只輸出人工步驟,建議從 router/switch 限制外部與不必要用戶端流量,同時保留受控管理/取證路徑;不自動更改 NAS firewall 或 stop process。
  3. evidence:建立全新本機目錄、保存 JSON report、codes-only summary.md 和 SHA-256 manifest。rescue --candidate-path/--attach-candidate 只可匯出 report 已列出的 IOC 範例,使用串流與大小上限,另算 SHA-256,並寫入 samples/sample-NNN.bin(source 留在 receipt);不執行檔案、不傳回 NAS。
  4. rescue:全量資料復原仍需先遏止、可信任重建與修補,再由離線/不可變備份恢復至新位置、驗證後切換;本 CLI 不會在疑似受感染 NAS 上覆寫還原。若疑似正在進行 Qlocker 活動,請依 QNAP 對應事件指引處理;家族與韌體不同時應以對應官方公告為準。

QNAP 官方事件與 API 參考

一般 repair plan/apply 的 code contract 已定義:計畫有 15 分鐘有效期與 hash plan_id;apply 要求逐字確認、裝置身分相同、前置條件已核對,每一步重新讀取 before state,操作後 readback 驗證,失敗即停止後續步驟。CLI 目前只輸出 refused plan,因為 QNAP 寫入 API/SSH 動作尚未通過實機測試;因此這不是可以改 NAS 的功能預告,而是安全框架先行。

內部架構

CLI / JSON / MCP
       |
       +-- commands.py ------- 純讀診斷與 finding
       |       |
       |       +-- client.py -------- HTTPS + SID + QTS CGI
       |       +-- parsers.py ------- 韌體差異容錯解析
       |       +-- ssh_backend.py --- 固定 allowlist 命令,無任意 shell API
       |
       +-- config.py --------- 原子設定寫入,不保存密碼
       +-- redaction.py ------ dump 與支援資料遮蔽

Web 後端適合 QTS 通用資料;SSH 後端只用於 Web API 無法可靠提供、且命令已列入 allowlist 的深度診斷。未來 local backend 會在 NAS 本機執行相同 allowlist,而不是開放任意命令。

安全模型

  • 遠端連線強制 HTTPS,只有 localhost/127.0.0.1 可使用 HTTP。
  • 預設驗證 TLS;--insecure 不會寫成隱性預設值。
  • 密碼不放 CLI argument、不保存於 JSON config、不寫入錯誤。
  • SID 只存於 process memory,命令結束會嘗試 logout。
  • SSH 使用 BatchMode=yes,只執行程式內固定 allowlist。
  • SSH probe 直接組合 argv,不經 /bin/sh -c;SMART bay 僅接受 1..64,沒有任意 package 或命令參數。
  • dump 預設遮蔽 hostname、序號、介面 IP、netmask、MAC 與 DNS。
  • MCP 無通用 api_call、shell、刪除或修復工具。

詳見 SECURITY.md、docs/ARCHITECTURE.md 與 docs/RESPONSE-WORKFLOWS.md。

測試

python -m unittest discover -s tests -v
python -m compileall -q qnap_nas_cli

CI 在 Ubuntu 22.04 上以 Python 3.8、3.12 與 3.13 執行離線測試與 wheel 建置。固定 runner 的理由是 Ubuntu 26 已無 Python 3.8 toolcache。Live smoke test 不會在公開 CI 使用真實 NAS credential。

Roadmap

  1. 已完成:以 TS-431K/QTS 5.2.8 真實 fixture 固化 system、disk、volume 與 SMART summary parsing。
  2. 已完成/有已知 unknown:以 TS-431K 真實 SSH fixture 與 live smoke 固化 RAID /proc/mdstat、實體磁碟拓樸、Container Station/Tailscale QPKG metadata;smart-details 在此韌體仍為 unavailable_on_firmware(空 get_hd_smartinfo、無 smartctl),不宣稱 SMART attributes 已齊;md9/md13 QNAP 128-wide bitmap 語義仍 unknown。
  3. 完成唯讀面與依賴圖後,只挑 1~2 個低風險、可逆操作;每項強制 plan、dry-run、明確確認、post-verify 與 rescue steps。
  4. 只有在確認第二管理帳號、SSH/本地復原路徑與失聯處置後,才評估更高風險操作。
  5. RAID rebuild、SMART repair、磁碟寫入與大幅防火牆修改不列入早期版本。
  6. 已完成:Webhook 告警、排程健康報告與 Evidence Capsule(納入 0.4.0)。

授權與商標

本專案採 MIT License,研究來源與授權記錄於 THIRD_PARTY_NOTICES.md。本專案不隸屬 QNAP Systems, Inc.;QNAP、QTS、QuTS hero 與其產品名稱為各自權利人的商標。

Metadata

Release files for qnap-nas-cli 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qnap-nas-cli 0.4.0
File Size Uploaded Provenance Actions
qnap_nas_cli-0.4.0.tar.gz 110.2 kB With provenance Details for qnap_nas_cli-0.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for qnap-nas-cli 0.4.0
File Interpreter ABI Platform Actions
qnap_nas_cli-0.4.0-py3-none-any.whl Python 3 none any Details for qnap_nas_cli-0.4.0-py3-none-any.whl

Total release size: 173.7 kB

Release files / qnap_nas_cli-0.4.0.tar.gz

Download URL qnap_nas_cli-0.4.0.tar.gz
Size 110.2 kB
Tags Source
SHA-256 checksum
How to use checksums
bb4a66afbd420a40556d13f48eaa171893781c1a4f718842ed1c730e8ac10e56
BLAKE2b-256 checksum
How to use checksums
422c48cdf847095f885f7db4d0dc663ba97d36d7bc068b7f3661d3ebff98c297
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / qnap_nas_cli-0.4.0-py3-none-any.whl

Download URL qnap_nas_cli-0.4.0-py3-none-any.whl
Size 63.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f3aa3c1efbc18cb4b9cbd3c1fe637adb756ba55c160b58bcc1365fae76784a96
BLAKE2b-256 checksum
How to use checksums
0db567774e175f5657d4950df30174ffcd3217dcf48c93fe5e8628986b399c7f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for qnap-nas-cli 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for qnap_nas_cli-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page