本卡屬 FR-114 資安修正(母卡 CM-2019)連帶卡 X-3。首腦第 4 棒裁 CM-2052 信任鏈時,獨立分析 agent 順手查到的既有坑;決策者 2026-09-23 裁「檢查並警告、不強制」。分析:docs/analysis/2026-09-23-agent-control-plane-identity-token.md。與 CM-2052 同一個 agent repo 工作區,等 CM-2052 驗過再派,避免兩棒同時改 enroll.py。
問題是什麼(白話)
雲端要上傳或下載代理程式(agent,裝在客戶機房的小程式)手上的證據檔時,會先發一張「60 秒內有效」的票給它。agent 檢查票有沒有過期用的是客戶那台機器自己的時鐘。機器時鐘走偏超過 60 秒(快或慢都算),每一張票都會被判「已過期」或「還沒生效」而拒收。症狀很難查:心跳正常、管理頁顯示在線、掃描任務跑得完,唯獨證據檔傳不上來,錯誤只有一行「Signature has expired」。2026-08-23 決策者在客戶 Windows 網域控制站(DC)上實際撞到,那台時鐘差了 16 小時(DC 預設不對外找時間伺服器)。
票裡的時間是 UTC 秒數,與時區設定無關,只有機器實際時刻不準才會出事。本卡不改票的效期、不動客戶機器的時間設定(那是客戶的事),只做「讓人一眼看到是時鐘問題」。
首腦核對:
- evidence-agent/core/agent_auth.py:99-105 — pyjwt.decode() 無 leeway,零容忍
- jedi-remote-agent/jedi_remote_agent/common/agent_auth/jwt_util.py:22-56 — 資料面票 exp = now + 60s,雲端簽
- docs/features/FR-066-2608-agent-native-installer/t71-windows-probe.md §8A(366-395 行)— 真機案例與根因,§8A.1 建議過安裝時檢查,查不到已落地
- CM-2052 裁定的控制面長效通行證是雲端簽雲端驗、agent 時鐘不參與,不受本坑影響;本卡只補資料面的可觀測性
工作區
- 套件側:/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/.claude/worktrees/jedi-wt-fix-security/jedi-remote-agent(branch fix/security-b1),只 git add 該子目錄下的檔
- agent 側:/Users/chouraymond/Projects/Billows/Audit-Manager/evidence-agent/.claude/worktrees/wt-fix-security(branch fix/security-b1)。CM-2052 也在這個工作區改 enroll.py,本卡等它驗過再開工,開工前 git log 確認 CM-2052 的 commit 已在
在哪裡
jedi-remote-agent/jedi_remote_agent/app/service/agent_enrollment_service.py:305-311 _register 回傳 dict,加 server_time_utc(UTC 秒數,int)
jedi-remote-agent/jedi_remote_agent/app/service/agent_enrollment_service.py:395 _heartbeat 回傳 dict,加 server_time_utc
evidence-agent/core/enroll.py:164-185 register 回應解析,算偏差、超標警告
evidence-agent/core/enroll.py:230-258 心跳回應,算偏差、超標警告(有節流)
evidence-agent/core/agent_auth.py:99-105 資料面 JWT 驗票失敗時,錯誤 log 補一句「若本機時鐘偏差請對時」(只改 log 字串,不加 leeway)
evidence-agent/scripts/install/install.sh enroll 成功後若偏差超標,用既有 warn() 印醒目警告,安裝照常完成
怎麼修
- ① 雲端側:_register 與 _heartbeat 兩個回傳 dict 各加一個欄位 server_time_utc = int(utc_now().timestamp())。用套件既有的 utc_now()(_heartbeat 已在用),不另寫時間 helper。舊 agent 收到多一個欄位不影響(它只取自己認得的 key)。
- ② agent 側 enroll.py:register 與心跳回應若含 server_time_utc,算 skew = 本機 UTC 秒數 − server_time_utc。|skew| > 60 → logger.warning 一行固定格式「本機時鐘與雲端偏差 N 秒(本機快/慢),證據檔上傳下載會失敗;請開啟系統自動對時(Windows: w32tm /resync;Linux: timedatectl set-ntp true)」。心跳每次都算但警告節流:偏差狀態沒變就每 10 分鐘印一次,不要每 30 秒刷一行;偏差回到 60 秒內印一行 info「時鐘偏差已恢復」。雲端沒回這個欄位(舊版雲端)→ 跳過,不報錯。
- ③ agent 側 agent_auth.py:99-105:資料面票驗失敗的既有 warning 尾巴加「(若本機時鐘偏差超過 60 秒會出現此錯,請對時)」。不加 leeway——加了等於拉長票的有效窗,那是設計決策不在本卡。
- ④ install.sh:enroll 成功後讀 agent 落地的 meta 或 log 取 skew(enroll.py 把 skew 一併寫進 agent.json meta 最省事),超標用既有 warn() 印同一句警告,exit code 不變、安裝照常完成。
- ⑤ 先查再寫:utc_now() 在套件 common 已有;agent 端取 UTC 用 time.time()(本來就是 UTC 秒數);不引入 ntplib 之類新依賴。