痛點拆解:為什麼「偶發連不上」比「永遠連不上」更難排
痛點一:把間歇逾時當成認證問題。雙堆疊環境下,客戶端可能對同一主機名先嘗試 IPv6、失敗後退回 IPv4,或相反;若其中一條路徑被中間盒捨棄或遇到 MTU 黑洞,表現就是偶發卡在 TCP 交握,與 authorized_keys 無關。
痛點二:DNS 與企業分割地平線。內網解析給出一套 A/AAAA,外網解析給出另一套;同仁在 VPN 內外切換時,「同一網域名稱」實際指向不同監聽面,遠端 Mac 上的 sshd 日誌會出現來源位址族切換,與 known_hosts 中入庫指紋預期不一致。
痛點三:防火牆只配了半套。許多範本預設寫 22/tcp 但不區分 inet/inet6;在 macOS 內建 pf 或上游雲端安全群組只放行 IPv4 時,IPv6 工作階段會被靜默捨棄,開發者卻看到「有時能傳有時不能」。
痛點四:CGNAT 與上傳佇列疊加。家寬或部分電信業者出口在 IPv4 側處於 CGNAT,IPv6 反而更直連;若 CI 與人工共用入口而不做 並行與 keepalive 審查,會把路徑抖動放大成「傳輸慢等於金鑰問題」。
威脅模型與觀測指標:把網路層從工作階段層剝離
建議把觀測拆成四欄:DNS 的 A/AAAA、TCP 交握 RTT、SSH 協商耗時,以及 SFTP 子系統首封包延遲。遠端 Mac 上統一日誌裡 sshd 的篩選條件,應與防火牆計數器指向同一個工作階段。若已部署 Tailscale/Headscale,優先把威脅模型收斂成「僅私網 IP 可達」,再調整 ListenAddress。
可引用資料:用具體數字結束「感覺網路不穩」
跨洲鏈路裡 v4/v6 的 RTT 差 20 到 80 毫秒很常見;Happy Eyeballs 的平行嘗試可能疊加數百毫秒到數秒的失敗等待。把 ConnectTimeout 與 ServerAliveInterval 寫進基線後,異常應先對照 DNS 變更與路由公告。CI 側若 IPv4 NAT 出口漂移,先把 TCP 可達與解析穩定,再聯動 MaxAuthTries 討論認證重試。
決策矩陣:僅 IPv4、僅 IPv6 與雙堆疊如何選
| 情境 | 優先策略 | 主要效益 | 主要風險 |
|---|---|---|---|
| 企業僅發 AAAA | 客戶端 AddressFamily inet6,伺服器完整 v6 監聽與 v6 防火牆 | 路徑單一、排障清楚 | 舊客戶端/函式庫無 v6 時需跳板 |
| 雙堆疊但 v6 品質差 | 客戶端強制 inet 或為該主機拆分別名(謹慎) | 穩定走成熟路徑 | 與「未來只發 AAAA」衝突,要文件化 |
| 遠端 Mac 在雲上同時有公網與私網 | 生產入口走私網或 mesh,公網僅維運 | 攻擊面小、延遲穩 | 需要額外路由與 DNS 分層 |
| CI 無頭與人工並行 | 拆分帳號與連接埠,或拆分位址族策略 | 避免人類偵錯踩到 CI 限流 | 需要更多監控面板 |
實作步驟:從解析到監聽再到防火牆(How-to)
# 1) 先看 DNS:是否同時回傳 A 與 AAAA(範例網域名請替換)
# dig +short A your.remote.mac.example
# dig +short AAAA your.remote.mac.example
# 2) 客戶端:為特定 Host 強制位址族(範例:僅 IPv4)
# Host rm-prod
# HostName your.remote.mac.example
# AddressFamily inet
# ServerAliveInterval 30
# ServerAliveCountMax 6
# 3) 客戶端:需要僅 IPv6 時
# Host rm-prod-v6
# HostName your.remote.mac.example
# AddressFamily inet6
# 4) 伺服器(遠端 Mac/Linux):檢視 sshd 實際監聽
# sudo sshd -T | egrep 'listenaddress|addressfamily|port'
# 5) 快速驗證連接埠在雙堆疊是否可達(分別在能存取 v4/v6 的機器上執行)
# nc -vz <host> 22
# nc -6vz <host-v6> 22
步驟一:在變更窗口凍結 DNS,記錄 A/AAAA 的 TTL 與發佈者,避免客戶端快取舊 AAAA。
步驟二:為工程師與 CI 分別建立 Host 別名,CI 側明確寫 UserKnownHostsFile 與指紋策略,複用 known_hosts 固定 文的可執行範本。
步驟三:在遠端 Mac 上核對 ListenAddress:若只監聽 0.0.0.0 而不監聽 ::,IPv6 客戶端必然失敗;反之亦然。
步驟四:把防火牆規則拆成 v4/v6 兩張表計數;出現「其中一側命中為零」時優先修網路,而不是先改 SSH 設定。
步驟五:將 並行 SFTP 與 MaxSessions、ClientAliveInterval 一併壓測,觀察長傳是否與位址族切換落在同一時間窗。
延伸閱讀、FAQ 與為什麼考慮 SFTPMAC 託管遠端 Mac
閱讀順序:本文 → known_hosts → 並行 SFTP → 限次與寬限 → ProxyJump → 首頁。
我應該在公司網路停用 AAAA 嗎?
不建議作為長期策略;更穩妥的是為關鍵主機名拆分 Host 記錄、在客戶端分段 AddressFamily,並在文件中寫明「此入口僅 v4」或「僅 v6」,避免團隊各自以本機 hosts 硬改造成隱性分叉。
macOS 客戶端與 Linux CI 行為不一致怎麼辦?
先統一 OpenSSH 版本與設定檔載入順序(系統層與使用者層),再在 CI 映像裡固化 ssh -G 輸出做回歸基線;差異多半來自解析函式庫與預設位址族,而不是金鑰本身。
雙堆疊問題會影響 rsync 嗎?
會:rsync 走 -e ssh 時與 SFTP 同源。
總結:把遠端 Mac 當成交付入口時,「能 ping」與「能穩定傳產物」之間隔著 DNS、位址族、監聽面與防火牆;先把雙堆疊議題從認證層剝離,排障較短。
限制:電信業者與企業 DNS 任一側悄悄變更 AAAA,都會讓團隊回到間歇性泥潭。
對比與收束:SFTPMAC 託管遠端 Mac 將穩定上線與傳輸治理產品化,減少跨部門追查 AAAA 與半套防火牆;長期對外提供 SFTP/rsync 時,租用專用節點通常更容易取得可重複的服務水準敘述。
把位址族、監聽面與 known_hosts 固化到同一份 Runbook,託管入口更容易一致回歸。
