All instances shared the image-baked machine-id (a67bf09f...), so Tencent
saw every WechatOnCloud account worldwide as one "device" — a textbook
device-farm signal triggering risk control and the forced-logout loop
reported across old and new versions.
- docker/woc-identity.sh: new /custom-cont-init.d/00-woc-identity hook —
generates a unique machine-id on first start, persists it in the data
volume (survives restart/upgrade/recreate), writes /etc/machine-id +
/var/lib/dbus/machine-id, removes /.dockerenv. Existing instances get a
fresh unique id on first upgraded start (volume lacks the file).
- regenInstanceMachineId + POST /api/admin/instances/:id/regen-machine-id:
roll a brand-new device id and restart, for accounts re-flagged by risk
control. Gated on the hook being present (old image → instructs upgrade).
- Admin 实例卡片「安全」弹窗新增「重置设备 ID 并重启」。
Verified: two fresh containers get distinct machine-ids; id persists across
restart; regen (rm persisted file + restart) yields a new persistent id.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- .env.example: surface multi-domain syntax (PANEL_ALLOWED_HOSTS=a,b,c),
IPv6 literal example, and reverse-proxy troubleshooting tip.
- index.ts: 400 response includes the rejected `host` and a hint pointing
at PANEL_ALLOWED_HOSTS — drops the diagnostic floor when reverse-proxy
Host-passthrough is misconfigured.
Follow-up to #13 (DNS-rebinding allowlist).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without Host validation, a malicious page the operator visits can use DNS
rebinding to point a hostname at the panel's loopback / LAN IP and drive
every authenticated API from the operator's own browser — including the
docker.sock-backed admin endpoints. The README's "intranet-only" guidance
does not cover this: the browser is the trust-boundary crossing.
Add an onRequest hook (plus a Host check on raw WebSocket upgrades) that
allows loopback + RFC1918 LAN by default and accepts public hostnames via
PANEL_ALLOWED_HOSTS (documented in .env.example and threaded through
docker-compose.yml). 35 inject()-driven assertions; tsc --noEmit clean.
Detected by Aeon + manual review (DNS-rebinding-gate axis).
Severity: high
CWE-346 (Origin Validation Error)