qnap-nas-cli 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qnap_nas_cli-0.2.0/CHANGELOG.md +45 -0
- qnap_nas_cli-0.2.0/COMPATIBILITY.md +11 -0
- qnap_nas_cli-0.2.0/CONTRIBUTING.md +31 -0
- qnap_nas_cli-0.2.0/LICENSE +21 -0
- qnap_nas_cli-0.2.0/MANIFEST.in +7 -0
- qnap_nas_cli-0.2.0/PKG-INFO +259 -0
- qnap_nas_cli-0.2.0/README.md +244 -0
- qnap_nas_cli-0.2.0/SECURITY.md +21 -0
- qnap_nas_cli-0.2.0/docs/ARCHITECTURE.md +55 -0
- qnap_nas_cli-0.2.0/docs/DEVELOPMENT_DECISIONS.md +57 -0
- qnap_nas_cli-0.2.0/docs/INSTALL.md +88 -0
- qnap_nas_cli-0.2.0/docs/LIVE-DRY-RUN-TS431K.md +36 -0
- qnap_nas_cli-0.2.0/docs/OBSERVATION_CONTRACT.md +20 -0
- qnap_nas_cli-0.2.0/docs/PUBLISHING.md +21 -0
- qnap_nas_cli-0.2.0/docs/RESPONSE-WORKFLOWS.md +55 -0
- qnap_nas_cli-0.2.0/docs/SECRETS.md +38 -0
- qnap_nas_cli-0.2.0/pyproject.toml +24 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/__init__.py +3 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/__main__.py +3 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/cli.py +327 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/client.py +201 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/commands.py +229 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/config.py +69 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/errors.py +14 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/incident.py +302 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/mcp.py +126 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/parsers.py +191 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/redaction.py +48 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/remediation.py +186 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli/ssh_backend.py +141 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli.egg-info/PKG-INFO +259 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli.egg-info/SOURCES.txt +48 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli.egg-info/dependency_links.txt +1 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli.egg-info/entry_points.txt +2 -0
- qnap_nas_cli-0.2.0/qnap_nas_cli.egg-info/top_level.txt +1 -0
- qnap_nas_cli-0.2.0/schemas/observation-health.schema.json +37 -0
- qnap_nas_cli-0.2.0/schemas/observation-incident.schema.json +71 -0
- qnap_nas_cli-0.2.0/schemas/observation-raid.schema.json +29 -0
- qnap_nas_cli-0.2.0/schemas/observation-storage.schema.json +47 -0
- qnap_nas_cli-0.2.0/setup.cfg +4 -0
- qnap_nas_cli-0.2.0/tests/test_cli.py +66 -0
- qnap_nas_cli-0.2.0/tests/test_client.py +135 -0
- qnap_nas_cli-0.2.0/tests/test_commands.py +109 -0
- qnap_nas_cli-0.2.0/tests/test_config.py +36 -0
- qnap_nas_cli-0.2.0/tests/test_incident.py +132 -0
- qnap_nas_cli-0.2.0/tests/test_mcp.py +45 -0
- qnap_nas_cli-0.2.0/tests/test_observation_contract.py +91 -0
- qnap_nas_cli-0.2.0/tests/test_parsers.py +89 -0
- qnap_nas_cli-0.2.0/tests/test_remediation.py +140 -0
- qnap_nas_cli-0.2.0/tests/test_ssh_backend.py +121 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Add Traditional Chinese installation and credential injection guides; surface the support policy in README.
|
|
6
|
+
|
|
7
|
+
## 0.2.0 — 2026-11-06
|
|
8
|
+
|
|
9
|
+
- Prepare setuptools packaging and a gated PyPI trusted-publishing workflow; this release skeleton does not publish automatically without the configured `pypi` environment approval.
|
|
10
|
+
- Freeze read-only health, storage, RAID, and incident observation schemas with stdlib contract tests.
|
|
11
|
+
- Add contributor guidance, the TS-431K compatibility baseline, and the observation contract.
|
|
12
|
+
- Keep the mutation gate closed; no production mutation adapter is included.
|
|
13
|
+
|
|
14
|
+
## 0.1.3 — 2026-11-03
|
|
15
|
+
|
|
16
|
+
- Add shell-free read-only SSH probes for `/proc/mdstat`, physical disk topology, SMART attributes, and fixed Container Station/Tailscale package state.
|
|
17
|
+
- Parse degraded RAID member maps and recovery/resync progress.
|
|
18
|
+
- Add explicit SSH identity-file configuration without storing key material or passwords.
|
|
19
|
+
- Expose RAID and service status through CLI and read-only MCP tools.
|
|
20
|
+
- Formalize the recovery-gated progression from real fixtures to low-risk reversible mutations.
|
|
21
|
+
- Add reviewed TS-431K SSH fixtures; keep QNAP 128-wide RAID maps, empty SMART output, and QPKG runtime health explicitly unknown.
|
|
22
|
+
|
|
23
|
+
## 0.1.2 — 2026-11-03
|
|
24
|
+
|
|
25
|
+
- Add QTS 2FA TOTP authentication without persistent bypass tokens.
|
|
26
|
+
- Add a reviewed TS-431K/QTS 5.2.8 real-device fixture.
|
|
27
|
+
- Merge sysinfo bay observations with `qsmart.cgi` model, capacity, temperature, and Health data.
|
|
28
|
+
- Normalize QTS enclosure locators such as `0:2` while retaining the raw locator.
|
|
29
|
+
- Expand dump v2 redaction to netmasks and MAC addresses without corrupting IPv6 capability flags.
|
|
30
|
+
- Keep `vol_status=0`, fixed volume query scope, and top-level SMART limitations explicit.
|
|
31
|
+
- Add `healthy_claim` and user-visible Storage limitations when evidence cannot support a healthy declaration.
|
|
32
|
+
- Prompt for TOTP interactively after QTS requests 2FA; keep MCP and automation environment-only.
|
|
33
|
+
|
|
34
|
+
## 0.1.1 — 2026-11-03
|
|
35
|
+
|
|
36
|
+
- Separate healthy, unhealthy, and indeterminate health outcomes.
|
|
37
|
+
- Preserve unknown QTS volume status codes as evidence gaps rather than critical findings.
|
|
38
|
+
- Mark the fixed `extra_vol_index=1` storage query scope as unverified.
|
|
39
|
+
- Add versioned dump validation and `qts fixture validate`.
|
|
40
|
+
- Parse nested DNS response values and report unknown DNS formats explicitly.
|
|
41
|
+
- Make MCP JSON-RPC request IDs stateless and validate malformed requests and parameters.
|
|
42
|
+
|
|
43
|
+
## 0.1.0 — 2026-11-03
|
|
44
|
+
|
|
45
|
+
- Initial read-only QTS diagnostics CLI and MCP server.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# QTS 相容性基線
|
|
2
|
+
|
|
3
|
+
此表只記錄 repository 中的 fixture provenance、live dry-run 紀錄與 README 所載證據。支援命令表示有對應樣本或唯讀驗證,不代表完整涵蓋或健康保證;`unknown` 是明確的證據缺口。
|
|
4
|
+
|
|
5
|
+
凍結的五欄 health envelope 由 `qts health` 與 MCP `qts_health` 輸出;incident report 只在 `observations.health` 巢狀包含該 envelope。`qts storage` 是 storage observation,不是 health envelope。
|
|
6
|
+
|
|
7
|
+
| 機型 | 內部 model | QTS build | 覆蓋命令 | 已知 unknown |
|
|
8
|
+
|---|---|---|---|---|
|
|
9
|
+
| TS-431K | TS-X41 | QTS 5.2.8 build 20251216 | `status`、`health`、`storage`(sysinfo、volume、SMART 摘要 fixture);`raid`、`disk-topology`、`smart-details`、`services`(唯讀 SSH fixture/probe);`tailscale`(live read-only smoke test) | `vol_status=0` 語義 unknown;`md9`/`md13` QNAP 128-wide member map 語義 unknown;`get_hd_smartinfo` 1..4 回應為空,SMART attributes unknown;QPKG metadata 不證明 daemon/workload 健康;volume index/full coverage unknown;第二管理員帳號未驗證;`incident scan` 尚無此機型實機 fixture。 |
|
|
10
|
+
|
|
11
|
+
證據來源:[`docs/LIVE-DRY-RUN-TS431K.md`](docs/LIVE-DRY-RUN-TS431K.md)、[`PROVENANCE.md`](tests/fixtures/ts431k-qts-5.2.8-build-20251216/PROVENANCE.md)、[`ssh-provenance.md`](tests/fixtures/ts431k-qts-5.2.8-build-20251216/ssh-provenance.md)。這些記錄證明觀察到的輸出形狀與測試範圍,不代表當前或未來設備狀態。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 貢獻指南
|
|
2
|
+
|
|
3
|
+
歡迎提交問題回報與 pull request。請先閱讀 [SECURITY.md](SECURITY.md);安全問題請使用其中的私下回報方式,不要開公開 issue。
|
|
4
|
+
|
|
5
|
+
## 相容性資料
|
|
6
|
+
|
|
7
|
+
若要提供 QTS 回應樣本,請在受信任的管理電腦執行 `qts dump <directory>`,再檢查產生的檔案與 manifest:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
qts dump qts-dump
|
|
11
|
+
qts fixture validate qts-dump --json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`dump` 會遮蔽已知欄位,但提交前仍須逐檔人工檢查,並確認沒有秘密或可識別資料。**禁止上傳 NAS 密碼、SID/qtoken、TOTP、私鑰、未遮蔽回應、公開或內部個資、帳號資訊、hostname、序號、IP、MAC、DNS 資料或客戶資料。**不要把秘密放進 issue、pull request、fixture、log 或 commit。請只提交必要且已審查的最小樣本;若無法確認已清除個資,請勿提交。
|
|
15
|
+
|
|
16
|
+
## 開發與 pull request
|
|
17
|
+
|
|
18
|
+
- 使用 Python 3.8+;執行期間僅使用 Python 標準函式庫,不新增第三方 runtime dependency。
|
|
19
|
+
- 不新增通用 shell、任意 QTS API 或未經審查的寫入操作;unknown 與沒有掃描命中都必須保留現有語義。
|
|
20
|
+
- PR 說明變更原因、相容性影響,以及測試結果。涉及輸出欄位時同步更新 schema、契約文件、測試與 `CHANGELOG.md`。
|
|
21
|
+
- 使用小而聚焦的 diff,避免包含 dump、設定檔或本機秘密。
|
|
22
|
+
|
|
23
|
+
## 執行檢查
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python -m unittest discover -s tests -v
|
|
27
|
+
python -m compileall -q qnap_nas_cli
|
|
28
|
+
python -m qnap_nas_cli --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
套件建置亦可用 `python -m pip wheel . --no-deps -w dist/`。發布流程及 PyPI trusted publishing 設定見 [docs/PUBLISHING.md](docs/PUBLISHING.md)。
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 popuchoco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qnap-nas-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A safety-first, zero-dependency CLI and MCP server for QNAP QTS NAS diagnostics
|
|
5
|
+
Author: popuchoco
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: qnap,qts,nas,cli,mcp,diagnostics
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# qnap-nas-cli
|
|
17
|
+
|
|
18
|
+
[](https://github.com/popuchoco/qnap-nas-cli/actions/workflows/ci.yml)
|
|
19
|
+
[](LICENSE)
|
|
20
|
+
|
|
21
|
+
> **支援範圍:**唯讀 QTS 診斷 CLI 與 MCP;已審查基線為 TS-431K / QTS 5.2.8,其他機型/韌體可能回報 unknown。請見[相容性表](COMPATIBILITY.md)與[觀察資料契約](docs/OBSERVATION_CONTRACT.md)。
|
|
22
|
+
>
|
|
23
|
+
> **支援政策:**僅支援最新的 `main` 與最新 release。
|
|
24
|
+
>
|
|
25
|
+
> **Mutation 尚未啟用:**沒有生產用 mutation adapter;MCP 不提供通用 shell 或任意 QTS API 工具。`0.2.0` 套件建置已就緒,但尚未發布到 PyPI;目前可用 editable install,待正式發布後再從 PyPI 安裝。維護與發布流程見[貢獻指南](CONTRIBUTING.md)及[發布說明](docs/PUBLISHING.md)。
|
|
26
|
+
|
|
27
|
+
安全優先、零第三方 Python 相依的 QNAP QTS 診斷 CLI 與唯讀 MCP Server。目標是讓人與 AI agent 都能取得可稽核的 NAS 健康證據,而不是在缺乏實機驗證時直接修改 RAID、磁碟或網路服務。
|
|
28
|
+
|
|
29
|
+
目前版本為 `0.2.0`,主要相容目標為 QTS 5.x 與 TS-431K。QTS CGI 欄位會因機型、韌體及安裝套件而異;請使用 `qts dump` 提供已遮蔽的相容性樣本。
|
|
30
|
+
|
|
31
|
+
## 目前狀態
|
|
32
|
+
|
|
33
|
+
- 初始唯讀診斷版本已於 2026-11-03 發布到 `main`。
|
|
34
|
+
- CI 執行離線測試、Python bytecode compilation 與 CLI smoke test;repair 已完成 SSH 身分綁定的零操作實機 dry-run,但第二管理帳號與 QTS web 路徑尚未驗證,因此 mutation gate 仍關閉,詳見 [`docs/LIVE-DRY-RUN-TS431K.md`](docs/LIVE-DRY-RUN-TS431K.md)。
|
|
35
|
+
- GitHub Actions 在 Python 3.8、3.12、3.13 驗證:[CI workflow](https://github.com/popuchoco/qnap-nas-cli/actions/workflows/ci.yml)。
|
|
36
|
+
- CI 固定使用 Ubuntu 22.04,因為 GitHub 的 Ubuntu 26 runner 不再提供 Python 3.8;這是為了保留較舊 NAS/管理環境的相容性,不是測試降級。
|
|
37
|
+
- 已用 TS-431K、QTS 5.2.8 build 20251216 完成 2FA 與唯讀 SSH live smoke test,並納入人工審查的 system/volume/SMART、`mdstat` 與實體磁碟拓樸 fixture。QTS 在此機型回報的內部 model 是 `TS-X41`。
|
|
38
|
+
|
|
39
|
+
維護者:`popuchoco <p.choco@daum.net>`
|
|
40
|
+
|
|
41
|
+
## 功能
|
|
42
|
+
|
|
43
|
+
- `setup`:保存 HTTPS、帳號與選用的 SSH 主機;不保存密碼。
|
|
44
|
+
- `doctor`/`whoami`:檢查本機條件並驗證 QTS 登入。
|
|
45
|
+
- `status`:型號、韌體、CPU、記憶體、溫度與 uptime。
|
|
46
|
+
- `health`:唯讀彙整磁碟、Volume、已知異常 finding 與資料不足 unknown;`healthy`、`unhealthy`、`indeterminate` 不混為一談,回報使用中的通用溫度門檻。
|
|
47
|
+
- `storage`/`disks`/`volumes`:合併 Volume、sysinfo bay 與 `qsmart.cgi` SMART 摘要;可重複指定 `--volume-index N`,但尚無 endpoint 可證明全部 index 已查齊。
|
|
48
|
+
- `network`:介面、IP、速度與 DNS。
|
|
49
|
+
- `tailscale`:從已驗證的 QPKG 安裝路徑執行固定的 `tailscale status --json`。
|
|
50
|
+
- `raid`:直接讀取並解析 `/proc/mdstat`,標示 degraded、member state 與 recovery/resync 進度;QNAP 128-wide bitmap 保守標為未知語義。
|
|
51
|
+
- `disk-topology`:用 `qcli_storage -d` 讀取實體 bay、data/spare 角色、容量與型號。
|
|
52
|
+
- `smart-details DISK`:以已驗證的 bay 編號讀取 QNAP SMART attributes;韌體空回應會明確標為 unavailable。
|
|
53
|
+
- `services`:唯讀觀察 Container Station/Tailscale QPKG metadata,不把 `Status=complete` 誤稱為 daemon 正常運行。
|
|
54
|
+
- `dump`:匯出已遮蔽 hostname、序號、IP 與 DNS 的相容性樣本。
|
|
55
|
+
- `fixture validate`:不連 NAS,離線檢查 dump 能否解析及是否存在未知欄位/狀態碼。
|
|
56
|
+
- `incident scan`:有上限地遞迴掃描 File Station 可見共享資料夾的檔名,檢查 Qlocker/DeadBolt 已知指標,並彙整 health、RAID 與套件 observation;報告會標記 `truncated`、`depth_limited` 與截斷原因。
|
|
57
|
+
- `incident contain`:由 report 產生人工網路隔離/取證清單;不改 NAS 防火牆或停止程序。
|
|
58
|
+
- `incident evidence`:保存 report 到新的本機目錄並寫 SHA-256 manifest;不宣稱包含 NAS 原始日誌。
|
|
59
|
+
- `incident rescue`:輸出離線備份復原步驟;也可將 report 已列出的可疑檔案唯讀串流匯出到新的本機 case directory,產生 SHA-256,不會解密、執行或回寫 NAS。
|
|
60
|
+
- `repair plan`/`repair apply`:具備確認碼、短效 plan、裝置身分/前置條件/before-state/readback 驗證的 fail-closed 框架;目前沒有生產用 QTS mutation adapter,所有 CLI plan 都會明確拒絕執行,apply 不會改 NAS。
|
|
61
|
+
- `mcp`:提供九個唯讀 stdio MCP tools。
|
|
62
|
+
- 全部資料命令支援 `--json`。
|
|
63
|
+
|
|
64
|
+
本版不提供 RAID 重建、SMART 修復、Container Station 停止、Tailscale 重啟、惡意程序 suspend/kill、QTS 防火牆修改、惡意檔案刪除或 NAS 上直接還原。通用 plan/apply 執行引擎可由測試用 adapter 驗證,但正式 adapter 必須先有指定機型/韌體的實機契約測試、前置條件與第二管理路徑、before/after readback、失敗中止與回復程序。
|
|
65
|
+
|
|
66
|
+
## 安裝
|
|
67
|
+
|
|
68
|
+
需求為 Python 3.8 以上,執行時不需要第三方套件。`0.2.0` 尚未發布到 PyPI;pipx、venv/pip 與離線 wheel 的安裝方式見[安裝指南](docs/INSTALL.md)。
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/popuchoco/qnap-nas-cli.git
|
|
72
|
+
cd qnap-nas-cli
|
|
73
|
+
python -m pip install -e .
|
|
74
|
+
qts --help
|
|
75
|
+
qts --version
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
也可直接從 checkout 執行,不必安裝:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python -m qnap_nas_cli --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 快速開始
|
|
85
|
+
|
|
86
|
+
QTS 應使用有效 HTTPS 憑證。`--insecure` 只適合受信任區網內的暫時測試。
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
qts setup --origin https://192.0.2.10 --user ops \
|
|
90
|
+
--ssh-host 192.0.2.10 --ssh-user ops \
|
|
91
|
+
--ssh-identity-file ~/.ssh/qnap_ops
|
|
92
|
+
|
|
93
|
+
# 互動式輸入密碼;若帳號啟用 2FA,CLI 會接著提示 TOTP
|
|
94
|
+
qts status
|
|
95
|
+
qts health
|
|
96
|
+
qts storage --json
|
|
97
|
+
qts storage --volume-index 1 --volume-index 2 --json
|
|
98
|
+
qts network
|
|
99
|
+
qts raid --json
|
|
100
|
+
qts disk-topology --json
|
|
101
|
+
qts smart-details 2 --json
|
|
102
|
+
qts services --json
|
|
103
|
+
|
|
104
|
+
# 勒索事件:有限掃描,並建立本機 evidence bundle
|
|
105
|
+
qts incident scan --max-items 10000 --evidence-dir case-2026-01 --json
|
|
106
|
+
qts incident contain case-2026-01/incident.json --json
|
|
107
|
+
qts incident rescue case-2026-01/incident.json --json
|
|
108
|
+
# 只有在要匯出 report 已列出的 IOC 範例檔時才提供 candidate path。
|
|
109
|
+
# 匯出到全新的本機目錄,預設上限 100 MiB;不是 NAS restore。
|
|
110
|
+
qts incident rescue case-2026-01/incident.json --candidate-path '/Public/!!!READ_ME.txt' --directory case-sample
|
|
111
|
+
|
|
112
|
+
# 一般修復流程目前只會建立明確拒絕執行的計畫,不會修改 NAS
|
|
113
|
+
qts repair plan --action service-stop --output qts-repair-plan.json --json
|
|
114
|
+
qts repair apply qts-repair-plan.json --confirm '<plan_id>' --json
|
|
115
|
+
|
|
116
|
+
# 自動化或 MCP;不要把密碼寫進 shell history
|
|
117
|
+
QTS_PASSWORD='...' qts --json health
|
|
118
|
+
QTS_PASSWORD='...' qts mcp
|
|
119
|
+
|
|
120
|
+
# 啟用 2FA 的帳號:驗證碼只存在當次 process environment
|
|
121
|
+
QTS_PASSWORD='...' QTS_SECURITY_CODE='123456' qts --json health
|
|
122
|
+
|
|
123
|
+
# 產生已遮蔽的相容性資料
|
|
124
|
+
qts dump qts-dump
|
|
125
|
+
qts fixture validate qts-dump --json
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
若 stdin 不是終端,CLI 也能從 stdin 第一行讀取密碼。互動終端在 QTS 回報需要 2FA 時才提示 TOTP;非互動與 MCP 必須注入 `QTS_SECURITY_CODE`。密碼與驗證碼不接受命令列參數,也不會寫入設定檔。自動化注入與 shell history 安全說明見[憑證注入指南](docs/SECRETS.md)。
|
|
129
|
+
|
|
130
|
+
## MCP
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"qnap-nas": {
|
|
136
|
+
"command": "qts",
|
|
137
|
+
"args": ["mcp"],
|
|
138
|
+
"env": {"QTS_PASSWORD": "由安全的秘密管理工具注入"}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
提供的 tools:
|
|
145
|
+
|
|
146
|
+
- `qts_status`
|
|
147
|
+
- `qts_health`
|
|
148
|
+
- `qts_storage`
|
|
149
|
+
- `qts_network`
|
|
150
|
+
- `qts_tailscale`
|
|
151
|
+
- `qts_raid`
|
|
152
|
+
- `qts_disk_topology`
|
|
153
|
+
- `qts_services`
|
|
154
|
+
- `qts_incident`(有限名稱 IOC 掃描;assessment 不會回報 clean)
|
|
155
|
+
|
|
156
|
+
MCP 目前刻意只讀,不提供通用 shell 或任意 QTS API 呼叫。
|
|
157
|
+
|
|
158
|
+
## Health 語義
|
|
159
|
+
|
|
160
|
+
`health` 不把「沒有發現異常」直接等同「已證明健康」:
|
|
161
|
+
|
|
162
|
+
- `status: healthy`/`healthy: true`:必要狀態完整,而且觀察值沒有已知異常。
|
|
163
|
+
- `status: unhealthy`/`healthy: false`:存在已知異常,例如 degraded Volume、明確 SMART warning 或過熱。
|
|
164
|
+
- `status: indeterminate`/`healthy: null`:資料缺少、`vol_status=0`、未知狀態碼,或 parser 沒找到必要記錄。
|
|
165
|
+
|
|
166
|
+
當結果為 `indeterminate` 時也會輸出 `healthy_claim: not_permitted`;這不是「可能健康」的捷徑,而是明確表示目前證據不足、不能宣告 healthy。Storage 的 `limitations` 會列出未驗證的 volume coverage 與 SMART 深度限制。
|
|
167
|
+
|
|
168
|
+
已知異常放在 `findings`;資料不足放在 `unknowns`。兩者不互相替代。原始 `status_code` 仍保留,方便用真實 fixture 校準新韌體。
|
|
169
|
+
|
|
170
|
+
Volume 可用 `--volume-index` 重複指定,也可在 MCP `qts_storage`/`qts_health` 傳 `volume_indices`;預設仍為 index 1。沒有經實機核對的 index discovery API,因此即使查了多個 index,coverage 一樣是 `unverified`。系統 70°C、磁碟 55°C 是報告揭露的 generic default,不是經各機型校正的安全界線。
|
|
171
|
+
|
|
172
|
+
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`。
|
|
173
|
+
|
|
174
|
+
注意:`qsmart.cgi` 的頂層 `Health=OK/Normal` 不代表所有 SMART attribute、歷史 I/O 錯誤或 QTS 告警都正常。例如實機 HDD2 曾由 QTS UI 報告健康度不佳,但此次頂層欄位為 `Normal`;CLI 不會用它覆蓋或否定較具體的告警。
|
|
175
|
+
|
|
176
|
+
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`。
|
|
177
|
+
|
|
178
|
+
## 真實 dump 與 fixture 流程
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# 在受信任管理電腦上連線 NAS,輸出已遮蔽資料
|
|
182
|
+
qts dump qts-dump-ts431k
|
|
183
|
+
|
|
184
|
+
# 離線確認 XML 可解析,並列出未知狀態
|
|
185
|
+
qts fixture validate qts-dump-ts431k --json
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`dump` v2 會保存 `sysinfo.xml`、`storage.xml`、`smart.xml` 與版本化 manifest。提交 fixture 前仍應人工檢查內容;驗證通過只代表結構可解析,不代表 NAS 健康。已審查的實機資料位於 `tests/fixtures/ts431k-qts-5.2.8-build-20251216`,手寫 XML 只用於 edge cases。
|
|
189
|
+
|
|
190
|
+
## Incident / repair 工作流(目前為受限 v1)
|
|
191
|
+
|
|
192
|
+
`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 規格列於下方參考資料。
|
|
193
|
+
|
|
194
|
+
應變分工:
|
|
195
|
+
|
|
196
|
+
1. **scan**:保存有限掃描、health、可用的 RAID/QPKG observation 與 coverage gaps。
|
|
197
|
+
2. **contain**:只輸出人工步驟,建議從 router/switch 限制外部與不必要用戶端流量,同時保留受控管理/取證路徑;不自動更改 NAS firewall 或 stop process。
|
|
198
|
+
3. **evidence**:建立全新本機目錄、保存 JSON report 和 SHA-256 manifest。`rescue --candidate-path` 只可匯出 report 已列出的 IOC 範例,使用串流與大小上限,另算 SHA-256;不執行檔案、不傳回 NAS。
|
|
199
|
+
4. **rescue**:全量資料復原仍需先遏止、可信任重建與修補,再由離線/不可變備份恢復至新位置、驗證後切換;本 CLI 不會在疑似受感染 NAS 上覆寫還原。若疑似正在進行 Qlocker 活動,請依 QNAP 對應事件指引處理;家族與韌體不同時應以對應官方公告為準。
|
|
200
|
+
|
|
201
|
+
### QNAP 官方事件與 API 參考
|
|
202
|
+
|
|
203
|
+
- [Qlocker 說明與處置指引](https://www.qnap.com/static/landing/2021/qlocker/response/en/):Qlocker 事件回應資訊與官方建議。
|
|
204
|
+
- [DeadBolt 安全公告(QSA-22-02)](https://www.qnap.com/en-us/security-advisory/qsa-22-02):DeadBolt 事件與受影響產品/處置資訊。
|
|
205
|
+
- [QTS File Station HTTP API v5(PDF)](https://download.qnap.com/dev/QNAP_QTS_File_Station_API_v5.pdf):`incident scan` 使用之 File Station API 契約參考。
|
|
206
|
+
|
|
207
|
+
一般 `repair plan/apply` 的 code contract 已定義:計畫有 15 分鐘有效期與 hash `plan_id`;apply 要求逐字確認、裝置身分相同、前置條件已核對,每一步重新讀取 before state,操作後 readback 驗證,失敗即停止後續步驟。CLI 目前只輸出 `refused` plan,因為 QNAP 寫入 API/SSH 動作尚未通過實機測試;因此這不是可以改 NAS 的功能預告,而是安全框架先行。
|
|
208
|
+
|
|
209
|
+
## 內部架構
|
|
210
|
+
|
|
211
|
+
```text
|
|
212
|
+
CLI / JSON / MCP
|
|
213
|
+
|
|
|
214
|
+
+-- commands.py ------- 純讀診斷與 finding
|
|
215
|
+
| |
|
|
216
|
+
| +-- client.py -------- HTTPS + SID + QTS CGI
|
|
217
|
+
| +-- parsers.py ------- 韌體差異容錯解析
|
|
218
|
+
| +-- ssh_backend.py --- 固定 allowlist 命令,無任意 shell API
|
|
219
|
+
|
|
|
220
|
+
+-- config.py --------- 原子設定寫入,不保存密碼
|
|
221
|
+
+-- redaction.py ------ dump 與支援資料遮蔽
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Web 後端適合 QTS 通用資料;SSH 後端只用於 Web API 無法可靠提供、且命令已列入 allowlist 的深度診斷。未來 local backend 會在 NAS 本機執行相同 allowlist,而不是開放任意命令。
|
|
225
|
+
|
|
226
|
+
## 安全模型
|
|
227
|
+
|
|
228
|
+
- 遠端連線強制 HTTPS,只有 `localhost`/`127.0.0.1` 可使用 HTTP。
|
|
229
|
+
- 預設驗證 TLS;`--insecure` 不會寫成隱性預設值。
|
|
230
|
+
- 密碼不放 CLI argument、不保存於 JSON config、不寫入錯誤。
|
|
231
|
+
- SID 只存於 process memory,命令結束會嘗試 logout。
|
|
232
|
+
- SSH 使用 `BatchMode=yes`,只執行程式內固定 allowlist。
|
|
233
|
+
- SSH probe 直接組合 argv,不經 `/bin/sh -c`;SMART bay 僅接受 `1..64`,沒有任意 package 或命令參數。
|
|
234
|
+
- `dump` 預設遮蔽 hostname、序號、介面 IP、netmask、MAC 與 DNS。
|
|
235
|
+
- MCP 無通用 `api_call`、shell、刪除或修復工具。
|
|
236
|
+
|
|
237
|
+
詳見 [SECURITY.md](SECURITY.md)、[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) 與 [docs/RESPONSE-WORKFLOWS.md](docs/RESPONSE-WORKFLOWS.md)。
|
|
238
|
+
|
|
239
|
+
## 測試
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
python -m unittest discover -s tests -v
|
|
243
|
+
python -m compileall -q qnap_nas_cli
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
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。
|
|
247
|
+
|
|
248
|
+
## Roadmap
|
|
249
|
+
|
|
250
|
+
1. 已完成:以 TS-431K/QTS 5.2.8 真實 fixture 固化 system、disk、volume 與 SMART summary parsing。
|
|
251
|
+
2. 進行中:以 TS-431K 真實 SSH fixture 固化 SMART attributes、RAID `/proc/mdstat`、Container Station 與 Tailscale 狀態。
|
|
252
|
+
3. 完成唯讀面與依賴圖後,只挑 1~2 個低風險、可逆操作;每項強制 plan、dry-run、明確確認、post-verify 與 rescue steps。
|
|
253
|
+
4. 只有在確認第二管理帳號、SSH/本地復原路徑與失聯處置後,才評估更高風險操作。
|
|
254
|
+
5. RAID rebuild、SMART repair、磁碟寫入與大幅防火牆修改不列入早期版本。
|
|
255
|
+
6. Webhook 告警、排程健康報告與 Evidence Capsule。
|
|
256
|
+
|
|
257
|
+
## 授權與商標
|
|
258
|
+
|
|
259
|
+
本專案採 [MIT License](LICENSE),研究來源與授權記錄於 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。本專案不隸屬 QNAP Systems, Inc.;QNAP、QTS、QuTS hero 與其產品名稱為各自權利人的商標。
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# qnap-nas-cli
|
|
2
|
+
|
|
3
|
+
[](https://github.com/popuchoco/qnap-nas-cli/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
> **支援範圍:**唯讀 QTS 診斷 CLI 與 MCP;已審查基線為 TS-431K / QTS 5.2.8,其他機型/韌體可能回報 unknown。請見[相容性表](COMPATIBILITY.md)與[觀察資料契約](docs/OBSERVATION_CONTRACT.md)。
|
|
7
|
+
>
|
|
8
|
+
> **支援政策:**僅支援最新的 `main` 與最新 release。
|
|
9
|
+
>
|
|
10
|
+
> **Mutation 尚未啟用:**沒有生產用 mutation adapter;MCP 不提供通用 shell 或任意 QTS API 工具。`0.2.0` 套件建置已就緒,但尚未發布到 PyPI;目前可用 editable install,待正式發布後再從 PyPI 安裝。維護與發布流程見[貢獻指南](CONTRIBUTING.md)及[發布說明](docs/PUBLISHING.md)。
|
|
11
|
+
|
|
12
|
+
安全優先、零第三方 Python 相依的 QNAP QTS 診斷 CLI 與唯讀 MCP Server。目標是讓人與 AI agent 都能取得可稽核的 NAS 健康證據,而不是在缺乏實機驗證時直接修改 RAID、磁碟或網路服務。
|
|
13
|
+
|
|
14
|
+
目前版本為 `0.2.0`,主要相容目標為 QTS 5.x 與 TS-431K。QTS CGI 欄位會因機型、韌體及安裝套件而異;請使用 `qts dump` 提供已遮蔽的相容性樣本。
|
|
15
|
+
|
|
16
|
+
## 目前狀態
|
|
17
|
+
|
|
18
|
+
- 初始唯讀診斷版本已於 2026-11-03 發布到 `main`。
|
|
19
|
+
- CI 執行離線測試、Python bytecode compilation 與 CLI smoke test;repair 已完成 SSH 身分綁定的零操作實機 dry-run,但第二管理帳號與 QTS web 路徑尚未驗證,因此 mutation gate 仍關閉,詳見 [`docs/LIVE-DRY-RUN-TS431K.md`](docs/LIVE-DRY-RUN-TS431K.md)。
|
|
20
|
+
- GitHub Actions 在 Python 3.8、3.12、3.13 驗證:[CI workflow](https://github.com/popuchoco/qnap-nas-cli/actions/workflows/ci.yml)。
|
|
21
|
+
- CI 固定使用 Ubuntu 22.04,因為 GitHub 的 Ubuntu 26 runner 不再提供 Python 3.8;這是為了保留較舊 NAS/管理環境的相容性,不是測試降級。
|
|
22
|
+
- 已用 TS-431K、QTS 5.2.8 build 20251216 完成 2FA 與唯讀 SSH live smoke test,並納入人工審查的 system/volume/SMART、`mdstat` 與實體磁碟拓樸 fixture。QTS 在此機型回報的內部 model 是 `TS-X41`。
|
|
23
|
+
|
|
24
|
+
維護者:`popuchoco <p.choco@daum.net>`
|
|
25
|
+
|
|
26
|
+
## 功能
|
|
27
|
+
|
|
28
|
+
- `setup`:保存 HTTPS、帳號與選用的 SSH 主機;不保存密碼。
|
|
29
|
+
- `doctor`/`whoami`:檢查本機條件並驗證 QTS 登入。
|
|
30
|
+
- `status`:型號、韌體、CPU、記憶體、溫度與 uptime。
|
|
31
|
+
- `health`:唯讀彙整磁碟、Volume、已知異常 finding 與資料不足 unknown;`healthy`、`unhealthy`、`indeterminate` 不混為一談,回報使用中的通用溫度門檻。
|
|
32
|
+
- `storage`/`disks`/`volumes`:合併 Volume、sysinfo bay 與 `qsmart.cgi` SMART 摘要;可重複指定 `--volume-index N`,但尚無 endpoint 可證明全部 index 已查齊。
|
|
33
|
+
- `network`:介面、IP、速度與 DNS。
|
|
34
|
+
- `tailscale`:從已驗證的 QPKG 安裝路徑執行固定的 `tailscale status --json`。
|
|
35
|
+
- `raid`:直接讀取並解析 `/proc/mdstat`,標示 degraded、member state 與 recovery/resync 進度;QNAP 128-wide bitmap 保守標為未知語義。
|
|
36
|
+
- `disk-topology`:用 `qcli_storage -d` 讀取實體 bay、data/spare 角色、容量與型號。
|
|
37
|
+
- `smart-details DISK`:以已驗證的 bay 編號讀取 QNAP SMART attributes;韌體空回應會明確標為 unavailable。
|
|
38
|
+
- `services`:唯讀觀察 Container Station/Tailscale QPKG metadata,不把 `Status=complete` 誤稱為 daemon 正常運行。
|
|
39
|
+
- `dump`:匯出已遮蔽 hostname、序號、IP 與 DNS 的相容性樣本。
|
|
40
|
+
- `fixture validate`:不連 NAS,離線檢查 dump 能否解析及是否存在未知欄位/狀態碼。
|
|
41
|
+
- `incident scan`:有上限地遞迴掃描 File Station 可見共享資料夾的檔名,檢查 Qlocker/DeadBolt 已知指標,並彙整 health、RAID 與套件 observation;報告會標記 `truncated`、`depth_limited` 與截斷原因。
|
|
42
|
+
- `incident contain`:由 report 產生人工網路隔離/取證清單;不改 NAS 防火牆或停止程序。
|
|
43
|
+
- `incident evidence`:保存 report 到新的本機目錄並寫 SHA-256 manifest;不宣稱包含 NAS 原始日誌。
|
|
44
|
+
- `incident rescue`:輸出離線備份復原步驟;也可將 report 已列出的可疑檔案唯讀串流匯出到新的本機 case directory,產生 SHA-256,不會解密、執行或回寫 NAS。
|
|
45
|
+
- `repair plan`/`repair apply`:具備確認碼、短效 plan、裝置身分/前置條件/before-state/readback 驗證的 fail-closed 框架;目前沒有生產用 QTS mutation adapter,所有 CLI plan 都會明確拒絕執行,apply 不會改 NAS。
|
|
46
|
+
- `mcp`:提供九個唯讀 stdio MCP tools。
|
|
47
|
+
- 全部資料命令支援 `--json`。
|
|
48
|
+
|
|
49
|
+
本版不提供 RAID 重建、SMART 修復、Container Station 停止、Tailscale 重啟、惡意程序 suspend/kill、QTS 防火牆修改、惡意檔案刪除或 NAS 上直接還原。通用 plan/apply 執行引擎可由測試用 adapter 驗證,但正式 adapter 必須先有指定機型/韌體的實機契約測試、前置條件與第二管理路徑、before/after readback、失敗中止與回復程序。
|
|
50
|
+
|
|
51
|
+
## 安裝
|
|
52
|
+
|
|
53
|
+
需求為 Python 3.8 以上,執行時不需要第三方套件。`0.2.0` 尚未發布到 PyPI;pipx、venv/pip 與離線 wheel 的安裝方式見[安裝指南](docs/INSTALL.md)。
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/popuchoco/qnap-nas-cli.git
|
|
57
|
+
cd qnap-nas-cli
|
|
58
|
+
python -m pip install -e .
|
|
59
|
+
qts --help
|
|
60
|
+
qts --version
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
也可直接從 checkout 執行,不必安裝:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python -m qnap_nas_cli --help
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 快速開始
|
|
70
|
+
|
|
71
|
+
QTS 應使用有效 HTTPS 憑證。`--insecure` 只適合受信任區網內的暫時測試。
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
qts setup --origin https://192.0.2.10 --user ops \
|
|
75
|
+
--ssh-host 192.0.2.10 --ssh-user ops \
|
|
76
|
+
--ssh-identity-file ~/.ssh/qnap_ops
|
|
77
|
+
|
|
78
|
+
# 互動式輸入密碼;若帳號啟用 2FA,CLI 會接著提示 TOTP
|
|
79
|
+
qts status
|
|
80
|
+
qts health
|
|
81
|
+
qts storage --json
|
|
82
|
+
qts storage --volume-index 1 --volume-index 2 --json
|
|
83
|
+
qts network
|
|
84
|
+
qts raid --json
|
|
85
|
+
qts disk-topology --json
|
|
86
|
+
qts smart-details 2 --json
|
|
87
|
+
qts services --json
|
|
88
|
+
|
|
89
|
+
# 勒索事件:有限掃描,並建立本機 evidence bundle
|
|
90
|
+
qts incident scan --max-items 10000 --evidence-dir case-2026-01 --json
|
|
91
|
+
qts incident contain case-2026-01/incident.json --json
|
|
92
|
+
qts incident rescue case-2026-01/incident.json --json
|
|
93
|
+
# 只有在要匯出 report 已列出的 IOC 範例檔時才提供 candidate path。
|
|
94
|
+
# 匯出到全新的本機目錄,預設上限 100 MiB;不是 NAS restore。
|
|
95
|
+
qts incident rescue case-2026-01/incident.json --candidate-path '/Public/!!!READ_ME.txt' --directory case-sample
|
|
96
|
+
|
|
97
|
+
# 一般修復流程目前只會建立明確拒絕執行的計畫,不會修改 NAS
|
|
98
|
+
qts repair plan --action service-stop --output qts-repair-plan.json --json
|
|
99
|
+
qts repair apply qts-repair-plan.json --confirm '<plan_id>' --json
|
|
100
|
+
|
|
101
|
+
# 自動化或 MCP;不要把密碼寫進 shell history
|
|
102
|
+
QTS_PASSWORD='...' qts --json health
|
|
103
|
+
QTS_PASSWORD='...' qts mcp
|
|
104
|
+
|
|
105
|
+
# 啟用 2FA 的帳號:驗證碼只存在當次 process environment
|
|
106
|
+
QTS_PASSWORD='...' QTS_SECURITY_CODE='123456' qts --json health
|
|
107
|
+
|
|
108
|
+
# 產生已遮蔽的相容性資料
|
|
109
|
+
qts dump qts-dump
|
|
110
|
+
qts fixture validate qts-dump --json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
若 stdin 不是終端,CLI 也能從 stdin 第一行讀取密碼。互動終端在 QTS 回報需要 2FA 時才提示 TOTP;非互動與 MCP 必須注入 `QTS_SECURITY_CODE`。密碼與驗證碼不接受命令列參數,也不會寫入設定檔。自動化注入與 shell history 安全說明見[憑證注入指南](docs/SECRETS.md)。
|
|
114
|
+
|
|
115
|
+
## MCP
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"qnap-nas": {
|
|
121
|
+
"command": "qts",
|
|
122
|
+
"args": ["mcp"],
|
|
123
|
+
"env": {"QTS_PASSWORD": "由安全的秘密管理工具注入"}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
提供的 tools:
|
|
130
|
+
|
|
131
|
+
- `qts_status`
|
|
132
|
+
- `qts_health`
|
|
133
|
+
- `qts_storage`
|
|
134
|
+
- `qts_network`
|
|
135
|
+
- `qts_tailscale`
|
|
136
|
+
- `qts_raid`
|
|
137
|
+
- `qts_disk_topology`
|
|
138
|
+
- `qts_services`
|
|
139
|
+
- `qts_incident`(有限名稱 IOC 掃描;assessment 不會回報 clean)
|
|
140
|
+
|
|
141
|
+
MCP 目前刻意只讀,不提供通用 shell 或任意 QTS API 呼叫。
|
|
142
|
+
|
|
143
|
+
## Health 語義
|
|
144
|
+
|
|
145
|
+
`health` 不把「沒有發現異常」直接等同「已證明健康」:
|
|
146
|
+
|
|
147
|
+
- `status: healthy`/`healthy: true`:必要狀態完整,而且觀察值沒有已知異常。
|
|
148
|
+
- `status: unhealthy`/`healthy: false`:存在已知異常,例如 degraded Volume、明確 SMART warning 或過熱。
|
|
149
|
+
- `status: indeterminate`/`healthy: null`:資料缺少、`vol_status=0`、未知狀態碼,或 parser 沒找到必要記錄。
|
|
150
|
+
|
|
151
|
+
當結果為 `indeterminate` 時也會輸出 `healthy_claim: not_permitted`;這不是「可能健康」的捷徑,而是明確表示目前證據不足、不能宣告 healthy。Storage 的 `limitations` 會列出未驗證的 volume coverage 與 SMART 深度限制。
|
|
152
|
+
|
|
153
|
+
已知異常放在 `findings`;資料不足放在 `unknowns`。兩者不互相替代。原始 `status_code` 仍保留,方便用真實 fixture 校準新韌體。
|
|
154
|
+
|
|
155
|
+
Volume 可用 `--volume-index` 重複指定,也可在 MCP `qts_storage`/`qts_health` 傳 `volume_indices`;預設仍為 index 1。沒有經實機核對的 index discovery API,因此即使查了多個 index,coverage 一樣是 `unverified`。系統 70°C、磁碟 55°C 是報告揭露的 generic default,不是經各機型校正的安全界線。
|
|
156
|
+
|
|
157
|
+
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`。
|
|
158
|
+
|
|
159
|
+
注意:`qsmart.cgi` 的頂層 `Health=OK/Normal` 不代表所有 SMART attribute、歷史 I/O 錯誤或 QTS 告警都正常。例如實機 HDD2 曾由 QTS UI 報告健康度不佳,但此次頂層欄位為 `Normal`;CLI 不會用它覆蓋或否定較具體的告警。
|
|
160
|
+
|
|
161
|
+
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`。
|
|
162
|
+
|
|
163
|
+
## 真實 dump 與 fixture 流程
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# 在受信任管理電腦上連線 NAS,輸出已遮蔽資料
|
|
167
|
+
qts dump qts-dump-ts431k
|
|
168
|
+
|
|
169
|
+
# 離線確認 XML 可解析,並列出未知狀態
|
|
170
|
+
qts fixture validate qts-dump-ts431k --json
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`dump` v2 會保存 `sysinfo.xml`、`storage.xml`、`smart.xml` 與版本化 manifest。提交 fixture 前仍應人工檢查內容;驗證通過只代表結構可解析,不代表 NAS 健康。已審查的實機資料位於 `tests/fixtures/ts431k-qts-5.2.8-build-20251216`,手寫 XML 只用於 edge cases。
|
|
174
|
+
|
|
175
|
+
## Incident / repair 工作流(目前為受限 v1)
|
|
176
|
+
|
|
177
|
+
`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 規格列於下方參考資料。
|
|
178
|
+
|
|
179
|
+
應變分工:
|
|
180
|
+
|
|
181
|
+
1. **scan**:保存有限掃描、health、可用的 RAID/QPKG observation 與 coverage gaps。
|
|
182
|
+
2. **contain**:只輸出人工步驟,建議從 router/switch 限制外部與不必要用戶端流量,同時保留受控管理/取證路徑;不自動更改 NAS firewall 或 stop process。
|
|
183
|
+
3. **evidence**:建立全新本機目錄、保存 JSON report 和 SHA-256 manifest。`rescue --candidate-path` 只可匯出 report 已列出的 IOC 範例,使用串流與大小上限,另算 SHA-256;不執行檔案、不傳回 NAS。
|
|
184
|
+
4. **rescue**:全量資料復原仍需先遏止、可信任重建與修補,再由離線/不可變備份恢復至新位置、驗證後切換;本 CLI 不會在疑似受感染 NAS 上覆寫還原。若疑似正在進行 Qlocker 活動,請依 QNAP 對應事件指引處理;家族與韌體不同時應以對應官方公告為準。
|
|
185
|
+
|
|
186
|
+
### QNAP 官方事件與 API 參考
|
|
187
|
+
|
|
188
|
+
- [Qlocker 說明與處置指引](https://www.qnap.com/static/landing/2021/qlocker/response/en/):Qlocker 事件回應資訊與官方建議。
|
|
189
|
+
- [DeadBolt 安全公告(QSA-22-02)](https://www.qnap.com/en-us/security-advisory/qsa-22-02):DeadBolt 事件與受影響產品/處置資訊。
|
|
190
|
+
- [QTS File Station HTTP API v5(PDF)](https://download.qnap.com/dev/QNAP_QTS_File_Station_API_v5.pdf):`incident scan` 使用之 File Station API 契約參考。
|
|
191
|
+
|
|
192
|
+
一般 `repair plan/apply` 的 code contract 已定義:計畫有 15 分鐘有效期與 hash `plan_id`;apply 要求逐字確認、裝置身分相同、前置條件已核對,每一步重新讀取 before state,操作後 readback 驗證,失敗即停止後續步驟。CLI 目前只輸出 `refused` plan,因為 QNAP 寫入 API/SSH 動作尚未通過實機測試;因此這不是可以改 NAS 的功能預告,而是安全框架先行。
|
|
193
|
+
|
|
194
|
+
## 內部架構
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
CLI / JSON / MCP
|
|
198
|
+
|
|
|
199
|
+
+-- commands.py ------- 純讀診斷與 finding
|
|
200
|
+
| |
|
|
201
|
+
| +-- client.py -------- HTTPS + SID + QTS CGI
|
|
202
|
+
| +-- parsers.py ------- 韌體差異容錯解析
|
|
203
|
+
| +-- ssh_backend.py --- 固定 allowlist 命令,無任意 shell API
|
|
204
|
+
|
|
|
205
|
+
+-- config.py --------- 原子設定寫入,不保存密碼
|
|
206
|
+
+-- redaction.py ------ dump 與支援資料遮蔽
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Web 後端適合 QTS 通用資料;SSH 後端只用於 Web API 無法可靠提供、且命令已列入 allowlist 的深度診斷。未來 local backend 會在 NAS 本機執行相同 allowlist,而不是開放任意命令。
|
|
210
|
+
|
|
211
|
+
## 安全模型
|
|
212
|
+
|
|
213
|
+
- 遠端連線強制 HTTPS,只有 `localhost`/`127.0.0.1` 可使用 HTTP。
|
|
214
|
+
- 預設驗證 TLS;`--insecure` 不會寫成隱性預設值。
|
|
215
|
+
- 密碼不放 CLI argument、不保存於 JSON config、不寫入錯誤。
|
|
216
|
+
- SID 只存於 process memory,命令結束會嘗試 logout。
|
|
217
|
+
- SSH 使用 `BatchMode=yes`,只執行程式內固定 allowlist。
|
|
218
|
+
- SSH probe 直接組合 argv,不經 `/bin/sh -c`;SMART bay 僅接受 `1..64`,沒有任意 package 或命令參數。
|
|
219
|
+
- `dump` 預設遮蔽 hostname、序號、介面 IP、netmask、MAC 與 DNS。
|
|
220
|
+
- MCP 無通用 `api_call`、shell、刪除或修復工具。
|
|
221
|
+
|
|
222
|
+
詳見 [SECURITY.md](SECURITY.md)、[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) 與 [docs/RESPONSE-WORKFLOWS.md](docs/RESPONSE-WORKFLOWS.md)。
|
|
223
|
+
|
|
224
|
+
## 測試
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
python -m unittest discover -s tests -v
|
|
228
|
+
python -m compileall -q qnap_nas_cli
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
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。
|
|
232
|
+
|
|
233
|
+
## Roadmap
|
|
234
|
+
|
|
235
|
+
1. 已完成:以 TS-431K/QTS 5.2.8 真實 fixture 固化 system、disk、volume 與 SMART summary parsing。
|
|
236
|
+
2. 進行中:以 TS-431K 真實 SSH fixture 固化 SMART attributes、RAID `/proc/mdstat`、Container Station 與 Tailscale 狀態。
|
|
237
|
+
3. 完成唯讀面與依賴圖後,只挑 1~2 個低風險、可逆操作;每項強制 plan、dry-run、明確確認、post-verify 與 rescue steps。
|
|
238
|
+
4. 只有在確認第二管理帳號、SSH/本地復原路徑與失聯處置後,才評估更高風險操作。
|
|
239
|
+
5. RAID rebuild、SMART repair、磁碟寫入與大幅防火牆修改不列入早期版本。
|
|
240
|
+
6. Webhook 告警、排程健康報告與 Evidence Capsule。
|
|
241
|
+
|
|
242
|
+
## 授權與商標
|
|
243
|
+
|
|
244
|
+
本專案採 [MIT License](LICENSE),研究來源與授權記錄於 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。本專案不隸屬 QNAP Systems, Inc.;QNAP、QTS、QuTS hero 與其產品名稱為各自權利人的商標。
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
請勿在 issue、fixture 或 log 中提交 NAS 密碼、SID、qtoken、私鑰、公開 IP、序號或未遮蔽的完整 QTS 回應。
|
|
4
|
+
|
|
5
|
+
## Supported versions
|
|
6
|
+
|
|
7
|
+
目前只支援最新的 `main` 與最新 release。`0.x` 期間的 QTS compatibility 仍可能變動。
|
|
8
|
+
|
|
9
|
+
## Reporting
|
|
10
|
+
|
|
11
|
+
請使用 GitHub Security Advisory 私下回報安全問題。不要先建立公開 issue。
|
|
12
|
+
|
|
13
|
+
## Operational safety
|
|
14
|
+
|
|
15
|
+
- 優先建立權限受限的 QTS 管理帳號,不使用內建 `admin` 作為日常 automation 帳號。
|
|
16
|
+
- 使用可信 HTTPS 憑證;`--insecure` 只供隔離測試。
|
|
17
|
+
- 密碼與 2FA 驗證碼只在執行期間注入;詳見[憑證注入說明](docs/SECRETS.md)。
|
|
18
|
+
- SSH 使用金鑰與 `BatchMode`,不要在腳本中保存 SSH 密碼。
|
|
19
|
+
- SSH probe 不經 shell,且只允許程式內固定 argv;不要新增通用 command/package 參數。
|
|
20
|
+
- 執行任何未來的修復命令前,確認已有可登入的第二管理帳號與本地復原路徑。
|
|
21
|
+
- RAID rebuild、SMART repair、磁碟寫入與大幅防火牆修改不屬於早期受控修復範圍。
|