停電後 SSH 連不上,第一反應是機器沒起來。但 ping 有回應,服務也在跑——只是 IP 換了。
打開 overlay VPN 的 admin console,舊節點還在,旁邊多了一個新節點:同一台機器,但 VPN 把它當成初次登記的陌生人,分配了全新的 100.x 位址。IP 從 100.105.190.108 漂移到 100.88.32.121。所有寫死舊 IP 的 SSH config、agent 設定,全部同時失效。
這有點像用暱稱在旅館訂房——每次退房再入住,前台找不到原本的帳號,直接開一個新帳號給你。你以為名字只是稱呼,但後台系統把名字當成唯一識別碼。
技術環境與觸發條件
系統是 macOS Monterey,跑在 OCLP/OpenCore 的 iMac 上。overlay VPN 使用 Tailscale,透過 MagicDNS 管理節點名稱。macOS 的主機名有三層:ComputerName(顯示名稱)、LocalHostName(本地 mDNS 名稱)、HostName(系統 hostname)。觸發模式是 unclean shutdown——停電直接斷電,Tailscale 沒有機會乾淨地登出並保留節點狀態。下次開機時,VPN daemon 判斷節點狀態遺失,重新走完整個 registration 流程。
錯誤傳染鏈
Machine unclean shutdown (power cut)
│
▼
Tailscale node state lost
│
▼
Daemon re-registers on boot
│
▼
Hostname parse: ComputerName contains non-ASCII「的」
│ ← 炸點在這裡
▼
Sanitized name collides → appends -1, -2, -3 ...
│
▼
New node created → new 100.x IP assigned
│
▼
SSH / agent configs with hardcoded IP → all fail
問題不在 unclean shutdown 本身——正常重新登入不會換 IP,同一節點的 IP 是固定的。問題在:節點狀態遺失之後,重新協商身份時,含有中文字元的 ComputerName 被 Tailscale sanitize 成 skylinkdigitalimac,和前幾次殘留的死節點撞名,一撞就往後加 -1、-2、-3,每個新節點都拿到新 IP。
為什麼第一時間沒看出來
直覺上,ComputerName 像是「顯示名稱」——改了只影響 Finder 側欄怎麼寫,跟網路沒關係。這個直覺在正常情況下確實沒錯;問題藏在「非正常關機 + 節點重新協商」這個組合裡才會浮出來。
非 ASCII 字元在底層系統裡是埋伏的地雷,這件事台灣工程師其實有天然直覺——從小在注音符號和 ASCII 兩套符號系統之間切換,早就知道「看起來能顯示」不等於「底層能處理」。緬甸曾花了多年時間處理 Zawgyi 字型和 Unicode 之間的全國遷移,表層顯示正常、跨系統傳輸全是亂碼——是同一類問題的國家級版本。Tailscale 的 hostname sanitize 只是把這個老問題在 overlay 網路層重演了一次。
Code 對照:三層主機名修法
# 修法前(問題根源)
ComputerName = "skylinkdigital的iMac" # 含中文「的」,非 ASCII
LocalHostName = "skylinkdigitaldeiMac" # sanitize 結果,不穩定
HostName = (未設定)
# 修法後(乾淨 ASCII)
sudo scutil --set ComputerName sky # 顯示名稱
sudo scutil --set LocalHostName sky # mDNS 本地名稱
sudo scutil --set HostName sky # 系統 hostname
# 所有存取改用 MagicDNS hostname,不寫死 IP
ssh user@sky.tail1dc8e3.ts.net # IP 漂移也不斷
三層要一起改。只改 ComputerName 但留著 LocalHostName 裡的 sanitize 殘留,下次重連還是會撞名。
側效應隔離清單
這次問題觸發了一串連帶失效,值得逐類確認,避免排除主因後還有盲點殘留:
- SSH config 寫死 IP:凡是
Host條目直接寫100.x.x.x的,全部改成 MagicDNS hostname,否則下次 IP 漂移還是斷。 - Agent / 自動化腳本的目標位址:任何排程腳本、cron job 裡硬編 IP 連線的,需同步更新;IP 是環境假設,不是穩定參數。
- Admin console 死節點殘留:
skylinkdigitalimac、skylinkdigitalimac-1、-2、-3這類 sanitize 產出的死節點要手動刪除,否則下次重連依然會繼續往後加-N。 - Key expiry 設定:節點在 admin console 裡應關閉 key expiry(Disable key expiry),避免長時間未上線的機器因金鑰過期被強制重新 registration。
- 停電自動開機:
sudo systemsetup -setrestartpowerfailure on讓機器復電後自動起來,縮短下次停電的停機時間,但這只解決開機問題,不解決 hostname 問題。 - NVRAM boot entry:這台機器用 OCLP/OpenCore 開機,停電後 NVRAM 丟失 boot entry,需實體按 Alt 選「EFI Boot」才能正常進 macOS。這是獨立問題,和 hostname 無關,但同一次停電事件裡同時發生,容易被誤認為是同一個根因。
- TLS 憑證:用 Tailscale 憑證代理的 dashboard(如 Caddy 反向代理的 HTTPS 端點),憑證約 90 天到期,不會自動 renew,需獨立設定排程更新——這不是本次觸發的根因,但屬於同一個維護週期的盲點。
留給下次的一件事
新機器設好之後,ComputerName 看起來只是個顯示名稱,改起來一點阻力都沒有,所以幾乎不會有人主動去確認它的內容。但對 overlay VPN 來說,它是這台機器在整個網路裡的身份憑證——而且只有在 unclean shutdown 之後,重新協商身份的那個瞬間,問題才真正浮出水面。
下次新機器入網,值得在第一步就把三層主機名設成乾淨的 ASCII。不是因為這個 bug 難修,而是修它的時機往往是停電後手忙腳亂的那段時間。
— 邱柏宇
延伸閱讀
The Chinese Hostname That Forgot Itself After a Reboot
After the power cut, SSH refused to connect. The machine was up — ping responded, services were running — but the IP had changed.
Opening the overlay VPN admin console showed the old node still there, with a new ghost sitting beside it: same machine, but the VPN treated it as a first-time registration and handed it a fresh 100.x address. The IP drifted from 100.105.190.108 to 100.88.32.121. Every SSH config and agent setting with a hardcoded IP broke simultaneously.
It’s like booking a hotel room under a nickname — every time you check out and back in, the front desk can’t find your original account and opens a new one. The name feels like a label, but the backend uses it as the unique identifier.
Stack and Trigger Conditions
The system was macOS Monterey on an OCLP/OpenCore iMac, using Tailscale as the overlay VPN with MagicDNS for node naming. macOS exposes hostname through three layers: ComputerName (display name), LocalHostName (mDNS), and HostName (system hostname). The trigger was an unclean shutdown — a hard power cut with no graceful logout — so Tailscale had no chance to preserve node state. On the next boot, the daemon treated it as a new registration from scratch.
The Failure Chain
Machine unclean shutdown (power cut)
│
▼
Tailscale node state lost
│
▼
Daemon re-registers on boot
│
▼
Hostname parse: ComputerName contains non-ASCII「的」
│ ← failure point
▼
Sanitized name collides → appends -1, -2, -3 ...
│
▼
New node created → new 100.x IP assigned
│
▼
SSH / agent configs with hardcoded IP → all fail
The unclean shutdown alone wasn’t the cause — a normal reconnect keeps the same node IP. The problem was what happened during re-registration: the ComputerName containing the Chinese character「的」got sanitized to skylinkdigitalimac, collided with leftover dead nodes from previous registrations, and the system appended -1, -2, -3 — each new node receiving a new IP.
Why It Wasn’t Obvious at First
Intuition says ComputerName is a display label — it affects what shows up in Finder, nothing more. That intuition is correct under normal conditions. The bug lives in the combination of unclean shutdown plus node re-negotiation, which almost never happens during routine use.
Non-ASCII characters sitting inside low-level system identifiers are a familiar hazard — engineers who’ve grown up switching between phonetic input systems and ASCII infrastructure develop a feel for how “displays correctly” and “parsed correctly by the kernel” aren’t the same thing. Myanmar’s years-long forced migration from Zawgyi to Unicode — where text looked fine on screen but scrambled in transit — is the same problem at national scale. Tailscale’s hostname sanitize just replayed it at the overlay network layer.
The Fix: All Three Hostname Layers
# Before (the root cause)
ComputerName = "skylinkdigital的iMac" # contains non-ASCII「的」
LocalHostName = "skylinkdigitaldeiMac" # unstable sanitize output
HostName = (unset)
# After (clean ASCII)
sudo scutil --set ComputerName sky
sudo scutil --set LocalHostName sky
sudo scutil --set HostName sky
# All access via MagicDNS hostname — not hardcoded IP
ssh user@sky.tail1dc8e3.ts.net # survives IP drift
All three layers need to change together. Fixing only ComputerName while leaving a sanitize artifact in LocalHostName still causes name collisions on the next re-registration.
Side-Effect Isolation Checklist
One root cause, multiple downstream breakages. Each category needs a separate check:
- SSH configs with hardcoded IPs: Any
Hostentry pointing directly to a100.x.x.xaddress must be migrated to MagicDNS hostname — the next IP drift will break it again. - Agent / automation scripts: Any scheduled job or cron with a hardcoded IP target. IP is an environmental assumption, not a stable parameter.
- Dead node cleanup in admin console: Sanitize artifacts like
skylinkdigitalimac-1,-2,-3must be manually deleted, or the counter keeps incrementing on future re-registrations. - Key expiry settings: Disable key expiry on active nodes in the admin console; otherwise a machine that’s been offline too long gets forced into full re-registration again.
- Power-loss auto-restart:
sudo systemsetup -setrestartpowerfailure onreduces downtime after power failures, but it’s orthogonal to the hostname issue. - NVRAM boot entry (OCLP machines): This iMac required physically holding Alt to select “EFI Boot” after NVRAM loss. A separate failure triggered by the same power cut, not the same root cause — easy to conflate.
- TLS certificates: Tailscale-signed certificates used by reverse-proxy dashboards expire around 90 days with no auto-renewal. Not triggered this time, but lives in the same maintenance blind spot.
One Thing to Check on the Next New Machine
After setup, ComputerName looks like a cosmetic setting — zero friction to change, so almost nobody audits it. But to the overlay VPN, it’s the machine’s identity credential on the entire network. The catch is that the flaw only surfaces after an unclean shutdown, during the re-registration handshake, which is exactly when there’s already something else on fire.
Setting all three hostname layers to clean ASCII as the first step when onboarding a new machine costs nothing. The cost of not doing it shows up at the worst possible time.
— 邱柏宇
Related Posts
https://justfly.idv.tw/s/Tp7dMnu