<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ssh on Tarragon</title><link>https://tarrragon.github.io/blog/tags/ssh/</link><description>Recent content in Ssh on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 02 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/ssh/index.xml" rel="self" type="application/rss+xml"/><item><title>遠端連線與同步工具選型：連得穩、斷得起、檔案一致</title><link>https://tarrragon.github.io/blog/linux/tools/remote/connection-and-sync-tools/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/tools/remote/connection-and-sync-tools/</guid><description>&lt;p>遠端工作有三塊彼此獨立的能力：&lt;strong>保住 session&lt;/strong>（多工器讓遠端的工作不隨連線消失）、&lt;strong>連線層&lt;/strong>（決定你怎麼接上遠端、斷了怎麼辦）、&lt;strong>同步層&lt;/strong>（決定本地與遠端的檔案怎麼保持一致）。多工器是地基、已在&lt;a href="../">遠端工具總覽&lt;/a>談過；這篇補另外兩塊——連線用什麼、檔案怎麼同步。這三塊要分開看，因為它們解的是不同問題，混在一起挑會挑錯：session 掉了是多工器的事，打字延遲高是連線層的事，本地改了遠端沒更新是同步層的事。&lt;/p>
&lt;h2 id="連線層從-ssh-出發按弱點往上補">連線層：從 SSH 出發，按弱點往上補&lt;/h2>
&lt;p>連線層的基準是 SSH——它是遠端登入的通用標準，加密、認證、port forwarding 都靠它，多數情況直接用 SSH 就夠。往上補工具的時機，是 SSH 在特定弱點上讓你難受的時候，而不是「有更潮的工具就換」。SSH 的兩個典型弱點是「網路一換就斷」（筆電休眠、Wi-Fi 換點、行動網路切換）和「連線中斷後要手動重連」，mosh 與 autossh 各補一個。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>工具&lt;/th>
 &lt;th>解的問題&lt;/th>
 &lt;th>代價&lt;/th>
 &lt;th>何時值得換&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>SSH&lt;/td>
 &lt;td>通用遠端登入基準&lt;/td>
 &lt;td>網路一換 IP 就斷、休眠喚醒常要重連&lt;/td>
 &lt;td>預設就用它&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>mosh&lt;/td>
 &lt;td>漫遊不斷線、高延遲下打字順&lt;/td>
 &lt;td>走 UDP 要開額外 port、不支援 port forwarding&lt;/td>
 &lt;td>行動網路 / Wi-Fi 換點 / 高延遲&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>autossh&lt;/td>
 &lt;td>SSH 斷線自動重連&lt;/td>
 &lt;td>只是重連、session 內容還是靠多工器保住&lt;/td>
 &lt;td>需要一條長期自動維持的隧道&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="mosh換網路不掉線高延遲下還能打字">mosh：換網路不掉線、高延遲下還能打字&lt;/h3>
&lt;p>mosh（mobile shell）解的是「連線的存活與手感」：它在 SSH 之上用 UDP 維持一個跟客戶端 IP 無關的 session，所以你的筆電從家裡 Wi-Fi 換到行動網路、或休眠喚醒換了 IP，連線不會斷。它還做本地回顯預測，高延遲鏈路上打字不會有一個字一個字等回應的黏滯感。從咖啡廳、通勤、跨國高延遲連遠端時，mosh 的體驗明顯優於裸 SSH。&lt;/p>
&lt;p>它的代價是走 UDP，要在遠端開一段 UDP port 範圍（防火牆/雲端 security group 要放行），且不做 SSH 的 port forwarding——需要轉發本地端口時還是得另開一條 SSH。所以 mosh 通常跟多工器搭配用：mosh 保住連線手感、多工器保住 session 內容，兩者互補。&lt;/p>
&lt;h3 id="autossh維持一條會自己重連的隧道">autossh：維持一條會自己重連的隧道&lt;/h3>
&lt;p>autossh 解的是「隧道的自動存活」：它監控一條 SSH 連線，斷了就自動重建，適合需要長期維持的場景——例如把遠端某個服務 port forward 回本地、或維持一條反向隧道讓 NAT 後的機器可被連入。它本身只負責「重連」這個動作，重連後你原本的工作是否還在，取決於你有沒有用多工器把 session 保住。&lt;/p>
&lt;p>判讀：autossh 是「基礎設施型」工具，用在你要一條無人值守、掉了要自己回來的隧道；日常互動式登入用 mosh 的漫遊能力更順。兩者不衝突。&lt;/p>
&lt;h2 id="網路層機器根本連不到時先解可達性">網路層：機器根本連不到時，先解可達性&lt;/h2>
&lt;p>前面的連線工具都假設「遠端機器的 IP 你連得到」。當遠端機器在 NAT 或防火牆後面、沒有公開 IP 時，連不到是可達性問題，要在網路層解，而不是換 SSH 客戶端。WireGuard 是現代的輕量 VPN 協定，讓兩台機器像在同一個私網裡直接互連；Tailscale 建在 WireGuard 之上，把「交換金鑰、打洞穿透 NAT、管理裝置清單」這些麻煩事自動化，通常裝好登入就能讓你的所有裝置互相 SSH，不必自己配 VPN。&lt;/p>
&lt;p>判讀：家裡的機器、公司內網的開發機、雲端私網裡的主機，想從外面連進去又不想開公網 port 暴露 SSH，用 Tailscale（要省事）或自建 WireGuard（要完全自主、不依賴第三方協調伺服器）在網路層打通，之後 SSH/mosh 照常用。這一層跟連線層是疊加關係：先有可達性，上面才談連線手感。（機器連不到的診斷——是網路層、服務層還是機器沒起——見&lt;a href="../../../debug/machine-unreachable/">機器連不到或起不來&lt;/a>。）&lt;/p>
&lt;h2 id="同步層三種語義依-workflow-選">同步層：三種語義，依 workflow 選&lt;/h2>
&lt;p>檔案同步不是一個問題，是三種不同語義的問題，挑錯工具會很痛。核心差異在「同步是單向還是雙向、是一次性快照還是持續即時、檔案存在本地還是遠端」。rsync、sshfs、mutagen 各自代表一種語義：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>工具&lt;/th>
 &lt;th>語義&lt;/th>
 &lt;th>檔案實際在哪&lt;/th>
 &lt;th>適合的 workflow&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>rsync&lt;/td>
 &lt;td>單向、一次性快照、增量傳輸&lt;/td>
 &lt;td>兩邊各一份&lt;/td>
 &lt;td>部署、備份、把成果拉回來&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>sshfs&lt;/td>
 &lt;td>把遠端目錄掛載成本地路徑&lt;/td>
 &lt;td>只在遠端&lt;/td>
 &lt;td>偶爾存取遠端檔案、當本地資料夾用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>mutagen&lt;/td>
 &lt;td>雙向、持續、即時同步&lt;/td>
 &lt;td>兩邊各一份即時&lt;/td>
 &lt;td>本地編輯、遠端執行的開發迴圈&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="rsync單向增量部署與拉回成果的正典">rsync：單向增量，部署與拉回成果的正典&lt;/h3>
&lt;p>rsync 解的是「把一批檔案有效率地從 A 複製到 B」：它只傳有變動的部分（增量），可保留權限與時間戳，是單向的一次性動作——你下指令、它同步一次、結束。它適合明確的「推上去」或「拉回來」：把本地建好的東西部署到遠端、把遠端跑完的產出拉回本地、定期備份。它不做「持續盯著兩邊、誰改了就同步」，所以拿它當即時開發同步用會很累（每次改都要手動跑）。&lt;/p>
&lt;p>因為單向且明確，rsync 也是三者裡最可預測、最不會意外覆蓋的——你清楚知道哪邊是來源、哪邊被更新。無人值守的成果回收（遠端跑完長任務、把結果 rsync 回本地）用它最穩。&lt;/p>
&lt;h3 id="sshfs把遠端目錄當本地資料夾掛載">sshfs：把遠端目錄當本地資料夾掛載&lt;/h3>
&lt;p>sshfs 解的是「我想用本地的工具存取遠端的檔案、但不想先複製下來」：它透過 SSH 把遠端目錄掛載成本地的一個路徑，你用本地的編輯器、檔案管理員直接開，實際檔案仍只在遠端。適合偶爾存取、檔案不宜落地到本地的場景。&lt;/p>
&lt;p>它的代價是脆與慢：每次存取都走網路，延遲高時開大目錄、跑 &lt;code>git status&lt;/code> 這種大量小檔操作會很卡；連線一斷，掛載點就進入壞狀態要重掛。所以 sshfs 適合「輕度、偶爾」的遠端存取，不適合當重度開發的主力——重度開發本地要有一份真的檔案，那是 mutagen 的場景。&lt;/p>
&lt;h3 id="mutagen雙向即時本地編輯遠端執行的開發迴圈">mutagen：雙向即時，本地編輯遠端執行的開發迴圈&lt;/h3>
&lt;p>mutagen 解的是現代遠端開發最常見的迴圈：「在本地用順手的編輯器改、在遠端（有算力、有環境、有相依）執行」，它在兩邊各保留一份實體檔案並持續雙向即時同步——你本地存檔，遠端幾乎同時更新；遠端產生的檔案也同步回本地。因為兩邊都是本地檔案，&lt;code>git status&lt;/code>、搜尋、建置都快，沒有 sshfs 那種每次存取走網路的黏滯。&lt;/p>
&lt;p>它的代價是多一個常駐同步 daemon 與初次設定，且雙向同步要處理衝突（兩邊同時改同一檔）。適合「本地機器弱/環境不對、但要在強遠端上跑」的長期開發關係。如果你的需求只是「偶爾拉個檔」，mutagen 是殺雞用牛刀，rsync 或 sshfs 更省。&lt;/p>
&lt;h2 id="ide-remote把編輯器的執行環境整個搬到遠端">IDE remote：把編輯器的執行環境整個搬到遠端&lt;/h2>
&lt;p>VS Code Remote（SSH/Containers/WSL）與 JetBrains Gateway 是另一條路線：它們不同步檔案，而是把編輯器的後端（語言伺服器、終端機、除錯器）整個跑在遠端，本地只留 UI。你在本地視窗編輯，但索引、建置、執行全發生在遠端那台機器上，檔案也只在遠端。這解掉了同步的衝突問題（沒有兩份檔案要對齊），代價是綁定該編輯器、且需要一條夠穩的連線維持 UI 與後端的通訊。&lt;/p>
&lt;p>判讀：如果你本來就用 VS Code / JetBrains，remote 模式通常比自己接 mutagen + SSH 更省事、體驗更整合；如果你用終端機編輯器（Vim/Neovim/Emacs）或要編輯器無關的方案，走 mutagen（雙向同步）或直接在遠端多工器裡編輯（檔案只在遠端、靠多工器保住 session）。&lt;/p></description><content:encoded><![CDATA[<p>遠端工作有三塊彼此獨立的能力：<strong>保住 session</strong>（多工器讓遠端的工作不隨連線消失）、<strong>連線層</strong>（決定你怎麼接上遠端、斷了怎麼辦）、<strong>同步層</strong>（決定本地與遠端的檔案怎麼保持一致）。多工器是地基、已在<a href="../">遠端工具總覽</a>談過；這篇補另外兩塊——連線用什麼、檔案怎麼同步。這三塊要分開看，因為它們解的是不同問題，混在一起挑會挑錯：session 掉了是多工器的事，打字延遲高是連線層的事，本地改了遠端沒更新是同步層的事。</p>
<h2 id="連線層從-ssh-出發按弱點往上補">連線層：從 SSH 出發，按弱點往上補</h2>
<p>連線層的基準是 SSH——它是遠端登入的通用標準，加密、認證、port forwarding 都靠它，多數情況直接用 SSH 就夠。往上補工具的時機，是 SSH 在特定弱點上讓你難受的時候，而不是「有更潮的工具就換」。SSH 的兩個典型弱點是「網路一換就斷」（筆電休眠、Wi-Fi 換點、行動網路切換）和「連線中斷後要手動重連」，mosh 與 autossh 各補一個。</p>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>解的問題</th>
          <th>代價</th>
          <th>何時值得換</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SSH</td>
          <td>通用遠端登入基準</td>
          <td>網路一換 IP 就斷、休眠喚醒常要重連</td>
          <td>預設就用它</td>
      </tr>
      <tr>
          <td>mosh</td>
          <td>漫遊不斷線、高延遲下打字順</td>
          <td>走 UDP 要開額外 port、不支援 port forwarding</td>
          <td>行動網路 / Wi-Fi 換點 / 高延遲</td>
      </tr>
      <tr>
          <td>autossh</td>
          <td>SSH 斷線自動重連</td>
          <td>只是重連、session 內容還是靠多工器保住</td>
          <td>需要一條長期自動維持的隧道</td>
      </tr>
  </tbody>
</table>
<h3 id="mosh換網路不掉線高延遲下還能打字">mosh：換網路不掉線、高延遲下還能打字</h3>
<p>mosh（mobile shell）解的是「連線的存活與手感」：它在 SSH 之上用 UDP 維持一個跟客戶端 IP 無關的 session，所以你的筆電從家裡 Wi-Fi 換到行動網路、或休眠喚醒換了 IP，連線不會斷。它還做本地回顯預測，高延遲鏈路上打字不會有一個字一個字等回應的黏滯感。從咖啡廳、通勤、跨國高延遲連遠端時，mosh 的體驗明顯優於裸 SSH。</p>
<p>它的代價是走 UDP，要在遠端開一段 UDP port 範圍（防火牆/雲端 security group 要放行），且不做 SSH 的 port forwarding——需要轉發本地端口時還是得另開一條 SSH。所以 mosh 通常跟多工器搭配用：mosh 保住連線手感、多工器保住 session 內容，兩者互補。</p>
<h3 id="autossh維持一條會自己重連的隧道">autossh：維持一條會自己重連的隧道</h3>
<p>autossh 解的是「隧道的自動存活」：它監控一條 SSH 連線，斷了就自動重建，適合需要長期維持的場景——例如把遠端某個服務 port forward 回本地、或維持一條反向隧道讓 NAT 後的機器可被連入。它本身只負責「重連」這個動作，重連後你原本的工作是否還在，取決於你有沒有用多工器把 session 保住。</p>
<p>判讀：autossh 是「基礎設施型」工具，用在你要一條無人值守、掉了要自己回來的隧道；日常互動式登入用 mosh 的漫遊能力更順。兩者不衝突。</p>
<h2 id="網路層機器根本連不到時先解可達性">網路層：機器根本連不到時，先解可達性</h2>
<p>前面的連線工具都假設「遠端機器的 IP 你連得到」。當遠端機器在 NAT 或防火牆後面、沒有公開 IP 時，連不到是可達性問題，要在網路層解，而不是換 SSH 客戶端。WireGuard 是現代的輕量 VPN 協定，讓兩台機器像在同一個私網裡直接互連；Tailscale 建在 WireGuard 之上，把「交換金鑰、打洞穿透 NAT、管理裝置清單」這些麻煩事自動化，通常裝好登入就能讓你的所有裝置互相 SSH，不必自己配 VPN。</p>
<p>判讀：家裡的機器、公司內網的開發機、雲端私網裡的主機，想從外面連進去又不想開公網 port 暴露 SSH，用 Tailscale（要省事）或自建 WireGuard（要完全自主、不依賴第三方協調伺服器）在網路層打通，之後 SSH/mosh 照常用。這一層跟連線層是疊加關係：先有可達性，上面才談連線手感。（機器連不到的診斷——是網路層、服務層還是機器沒起——見<a href="../../../debug/machine-unreachable/">機器連不到或起不來</a>。）</p>
<h2 id="同步層三種語義依-workflow-選">同步層：三種語義，依 workflow 選</h2>
<p>檔案同步不是一個問題，是三種不同語義的問題，挑錯工具會很痛。核心差異在「同步是單向還是雙向、是一次性快照還是持續即時、檔案存在本地還是遠端」。rsync、sshfs、mutagen 各自代表一種語義：</p>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>語義</th>
          <th>檔案實際在哪</th>
          <th>適合的 workflow</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>rsync</td>
          <td>單向、一次性快照、增量傳輸</td>
          <td>兩邊各一份</td>
          <td>部署、備份、把成果拉回來</td>
      </tr>
      <tr>
          <td>sshfs</td>
          <td>把遠端目錄掛載成本地路徑</td>
          <td>只在遠端</td>
          <td>偶爾存取遠端檔案、當本地資料夾用</td>
      </tr>
      <tr>
          <td>mutagen</td>
          <td>雙向、持續、即時同步</td>
          <td>兩邊各一份即時</td>
          <td>本地編輯、遠端執行的開發迴圈</td>
      </tr>
  </tbody>
</table>
<h3 id="rsync單向增量部署與拉回成果的正典">rsync：單向增量，部署與拉回成果的正典</h3>
<p>rsync 解的是「把一批檔案有效率地從 A 複製到 B」：它只傳有變動的部分（增量），可保留權限與時間戳，是單向的一次性動作——你下指令、它同步一次、結束。它適合明確的「推上去」或「拉回來」：把本地建好的東西部署到遠端、把遠端跑完的產出拉回本地、定期備份。它不做「持續盯著兩邊、誰改了就同步」，所以拿它當即時開發同步用會很累（每次改都要手動跑）。</p>
<p>因為單向且明確，rsync 也是三者裡最可預測、最不會意外覆蓋的——你清楚知道哪邊是來源、哪邊被更新。無人值守的成果回收（遠端跑完長任務、把結果 rsync 回本地）用它最穩。</p>
<h3 id="sshfs把遠端目錄當本地資料夾掛載">sshfs：把遠端目錄當本地資料夾掛載</h3>
<p>sshfs 解的是「我想用本地的工具存取遠端的檔案、但不想先複製下來」：它透過 SSH 把遠端目錄掛載成本地的一個路徑，你用本地的編輯器、檔案管理員直接開，實際檔案仍只在遠端。適合偶爾存取、檔案不宜落地到本地的場景。</p>
<p>它的代價是脆與慢：每次存取都走網路，延遲高時開大目錄、跑 <code>git status</code> 這種大量小檔操作會很卡；連線一斷，掛載點就進入壞狀態要重掛。所以 sshfs 適合「輕度、偶爾」的遠端存取，不適合當重度開發的主力——重度開發本地要有一份真的檔案，那是 mutagen 的場景。</p>
<h3 id="mutagen雙向即時本地編輯遠端執行的開發迴圈">mutagen：雙向即時，本地編輯遠端執行的開發迴圈</h3>
<p>mutagen 解的是現代遠端開發最常見的迴圈：「在本地用順手的編輯器改、在遠端（有算力、有環境、有相依）執行」，它在兩邊各保留一份實體檔案並持續雙向即時同步——你本地存檔，遠端幾乎同時更新；遠端產生的檔案也同步回本地。因為兩邊都是本地檔案，<code>git status</code>、搜尋、建置都快，沒有 sshfs 那種每次存取走網路的黏滯。</p>
<p>它的代價是多一個常駐同步 daemon 與初次設定，且雙向同步要處理衝突（兩邊同時改同一檔）。適合「本地機器弱/環境不對、但要在強遠端上跑」的長期開發關係。如果你的需求只是「偶爾拉個檔」，mutagen 是殺雞用牛刀，rsync 或 sshfs 更省。</p>
<h2 id="ide-remote把編輯器的執行環境整個搬到遠端">IDE remote：把編輯器的執行環境整個搬到遠端</h2>
<p>VS Code Remote（SSH/Containers/WSL）與 JetBrains Gateway 是另一條路線：它們不同步檔案，而是把編輯器的後端（語言伺服器、終端機、除錯器）整個跑在遠端，本地只留 UI。你在本地視窗編輯，但索引、建置、執行全發生在遠端那台機器上，檔案也只在遠端。這解掉了同步的衝突問題（沒有兩份檔案要對齊），代價是綁定該編輯器、且需要一條夠穩的連線維持 UI 與後端的通訊。</p>
<p>判讀：如果你本來就用 VS Code / JetBrains，remote 模式通常比自己接 mutagen + SSH 更省事、體驗更整合；如果你用終端機編輯器（Vim/Neovim/Emacs）或要編輯器無關的方案，走 mutagen（雙向同步）或直接在遠端多工器裡編輯（檔案只在遠端、靠多工器保住 session）。</p>
<h2 id="在-arch-上的安裝與依賴實測-aarch64">在 Arch 上的安裝與依賴（實測 aarch64）</h2>
<p>這些工具多數在官方 repo，但有幾個安裝陷阱與部署前提是實機才看得出來的，選好工具後要一起確認：</p>
<ul>
<li><strong>mosh</strong>：官方 repo（<code>pacman -S mosh</code>）。同一套件同時含 client（<code>mosh</code>）與 server（<code>mosh-server</code>），所以<strong>遠端機器也要裝 mosh</strong>——遠端是別人的伺服器時這是前提；另外 UDP 埠範圍（預設 60000–61000）要在防火牆 / security group 放行。</li>
<li><strong>autossh</strong> / <strong>rsync</strong> / <strong>sshfs</strong>：都在官方 repo（<code>pacman -S autossh rsync sshfs</code>）。<code>sshfs</code> 會自動拉 <code>fuse3</code> 相依，不用手動裝 FUSE；掛載需要 <code>/dev/fuse</code> 可存取（一般環境已就緒）。</li>
<li><strong>tailscale</strong>：官方 repo（<code>pacman -S tailscale</code>），但<strong>裝完 daemon 預設沒起</strong>——要 <code>sudo systemctl enable --now tailscaled</code> 之後才能 <code>tailscale up</code>。少了這步，<code>tailscale</code> 指令會因 daemon 未運行而失敗。</li>
<li><strong>wireguard</strong>：裝 <code>wireguard-tools</code>（<code>wg</code> / <code>wg-quick</code>）。核心模組在多數發行版是 loadable module，<code>wg-quick</code> 會在需要時自動 <code>modprobe wireguard</code>，日常不用手動處理。</li>
<li><strong>mutagen</strong>：<strong>不在官方 repo</strong>，且 <code>pacman -S mutagen</code> 會裝到完全無關的 <code>python-mutagen</code>（音訊 metadata 函式庫）。正確安裝是 AUR 的 <code>mutagen.io-bin</code>（<code>paru -S mutagen.io-bin</code>），提供 <code>mutagen</code> 執行檔，aarch64 有官方 binary。這是「以為一句 pacman 就有、實際會裝錯東西」的典型坑。</li>
<li><strong>VS Code Remote / JetBrains Gateway</strong>：綁各自的 IDE，不透過套件管理器單獨裝，隨 IDE 的 remote 擴充啟用。</li>
</ul>
<h2 id="依情境選">依情境選</h2>
<p>把上面的工具對回你的實際處境：</p>
<ul>
<li><strong>日常從筆電連遠端、常換網路</strong>：mosh（漫遊）+ 多工器（保 session）。</li>
<li><strong>要一條長期自動維持的隧道 / 反向連接</strong>：autossh + 多工器。</li>
<li><strong>遠端機器在 NAT 後、根本連不到</strong>：先用 Tailscale 或 WireGuard 在網路層打通，再照常 SSH/mosh。</li>
<li><strong>部署上去、或把遠端跑完的成果拉回來</strong>：rsync（單向、可預測）。</li>
<li><strong>偶爾存取遠端幾個檔、不想複製下來</strong>：sshfs（掛載）。</li>
<li><strong>本地編輯、遠端執行的長期開發迴圈</strong>：mutagen（雙向即時）或你的 IDE 的 remote 模式。</li>
<li><strong>無人值守跑長任務、跑完自動回收成果</strong>：多工器保住任務 + rsync 拉回產出，見<a href="../../../install/unattended-remote-work/">讓機器跑無人值守的長任務</a>。</li>
</ul>
<p>判準是先分清「你缺的是連線存活、可達性、還是檔案一致」，再在對應那一層挑工具——三層各挑各的，不要拿同步工具去解連線問題。</p>
<h2 id="下一步">下一步</h2>
<ul>
<li>保住遠端 session 的多工器（tmux / zellij）配置與比較：<a href="../">遠端工具總覽</a> 與 <a href="../../cli/">CLI 環境工具</a> 的多工器篇。</li>
<li>連不上、終端機噴亂碼、要從 SSH 操控圖形桌面等連線本身的問題：<a href="../../../debug/ssh-and-terminal-troubleshooting/">除錯與診斷：遠端連線與終端機問題</a>。</li>
<li>機器完全沒回應、域名解析不了、虛擬機起不來：<a href="../../../debug/machine-unreachable/">機器連不到或起不來</a>。</li>
<li>把遠端機器設成無人值守、離開後自己跑完長任務送回成果：<a href="../../../install/unattended-remote-work/">讓機器跑無人值守的長任務</a>。</li>
</ul>
]]></content:encoded></item><item><title>遠端連線與終端機問題</title><link>https://tarrragon.github.io/blog/linux/debug/ssh-and-terminal-troubleshooting/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/debug/ssh-and-terminal-troubleshooting/</guid><description>&lt;p>遠端操作 Linux 時，很多問題出在「你的終端機」與「遠端 session」之間那條連線的狀態，而不在遠端那台機器本身。終端機被上一個程式留在奇怪的模式、字元編碼與終端機能力沒對上、或你想從一條純文字的 SSH 連線去驅動一個需要實體螢幕的圖形桌面——這些問題的共同點是：現象發生在連線的某一層，判斷對是哪一層，修復就很直接。&lt;/p>
&lt;p>SSH「連不上」本身（&lt;code>Permission denied&lt;/code>、&lt;code>Host key verification failed&lt;/code>、&lt;code>Connection refused&lt;/code>）的判讀與修復，見 &lt;a href="../../install/ssh-keyless-bootstrap/">外部連入與無 key 的 bootstrap 路徑&lt;/a> 的重連段落。這篇處理的是「連上了、但終端機或 session 的狀態不對」的那些情況。&lt;/p>
&lt;h2 id="ssh-斷線後本機終端機噴亂碼狂跳字元">SSH 斷線後本機終端機噴亂碼、狂跳字元&lt;/h2>
&lt;p>一個嚇人但無害的情況：SSH 連線被中斷後，你本機的終端機開始瘋狂輸出像 &lt;code>&amp;lt;35;80;24M&lt;/code> 這樣的序列，尤其在你移動滑鼠時狂跳。這不是遠端機器在打字，是&lt;strong>你本機的終端機被卡在滑鼠回報模式&lt;/strong>。&lt;/p>
&lt;p>判讀關鍵在「什麼時候噴」：如果那串亂碼只在你移動滑鼠時出現、而且形如 &lt;code>數字;數字M&lt;/code>，那就是滑鼠座標回報。成因是遠端跑的某個全螢幕程式（TUI、編輯器、終端機多工器）啟動時對終端機開了滑鼠追蹤模式，SSH 被硬斷時它來不及送出「關閉滑鼠模式」的序列就死了，於是你本機終端機還停在回報模式，滑鼠一動就把游標座標當輸入送進來。&lt;/p>
&lt;p>修復是重置終端機的模式，跟遠端機器無關：&lt;/p>
&lt;ul>
&lt;li>最快：開一個新的終端機分頁 / 視窗。模式是「那個終端機 session」的狀態，新視窗是乾淨的。&lt;/li>
&lt;li>救現有視窗：先把滑鼠移開別動（洪流會停），盲打 &lt;code>reset&lt;/code> 再 Enter，送出終端機重置。&lt;/li>
&lt;li>若 &lt;code>reset&lt;/code> 沒清掉，補送關閉滑鼠回報的序列：&lt;code>printf '\033[?1000l\033[?1002l\033[?1003l\033[?1006l'&lt;/code>。&lt;/li>
&lt;/ul>
&lt;p>同一類的還有「alternate screen 沒還原」——遠端的全螢幕程式異常結束時，本機終端機可能卡在替代畫面緩衝區，看起來像畫面清空或凍結。&lt;code>reset&lt;/code> 同樣能救。歸納起來：&lt;strong>SSH 被硬斷後本機終端機行為異常，先懷疑「對端程式來不及還原終端機模式」，用 &lt;code>reset&lt;/code> 或開新視窗處理本機終端機狀態，不必急著重連遠端。&lt;/strong>&lt;/p>
&lt;h2 id="遠端打字變亂碼重複位置錯亂">遠端打字變亂碼、重複、位置錯亂&lt;/h2>
&lt;p>連上遠端後，如果互動式輸入變得不對——打一個字出現好幾個、游標位置錯亂、畫面重繪殘影——通常是兩層問題之一，判讀方式是分開排除。&lt;/p>
&lt;p>第一層是&lt;strong>字元編碼（locale）&lt;/strong>。從某些本機（例如 macOS）SSH 進 Linux 時，本機會把 &lt;code>LC_CTYPE&lt;/code> 之類的變數帶過去；如果遠端沒有對應的 locale、就會退回 POSIX/C locale，讓終端機的行編輯（ZLE、readline）對多位元組字元的寬度判斷出錯，表現為輸入重複或錯位。判斷方式是在遠端 &lt;code>locale&lt;/code> 看目前值、&lt;code>locale -a&lt;/code> 看有沒有裝對應的 UTF-8 locale。修法是在遠端明確設好 &lt;code>LANG&lt;/code> / &lt;code>LC_CTYPE&lt;/code> 到一個實際存在的 UTF-8 locale，而不是讓它繼承一個遠端不認得的值。&lt;/p>
&lt;p>第二層是&lt;strong>終端機能力資料庫（terminfo）&lt;/strong>。你本機終端機的 &lt;code>TERM&lt;/code> 值（例如某些新終端機用 &lt;code>xterm-ghostty&lt;/code> 之類的自訂值）如果在遠端沒有對應的 terminfo 條目，遠端程式就不知道怎麼正確地清行、移動游標、重繪，畫面就會亂。判斷方式是在遠端 &lt;code>echo $TERM&lt;/code> 看值、&lt;code>infocmp $TERM&lt;/code> 看遠端認不認得。修法是把本機的 terminfo 條目送過去讓遠端安裝：&lt;code>infocmp -x $TERM | ssh &amp;lt;遠端&amp;gt; 'tic -x -'&lt;/code>。&lt;/p>
&lt;p>先分清是 locale 還是 terminfo，兩者症狀相似但修法不同：locale 是編碼寬度、terminfo 是繪製指令。查 &lt;code>locale&lt;/code> 跟查 &lt;code>$TERM&lt;/code> + &lt;code>infocmp&lt;/code> 就能分開。&lt;/p>
&lt;h2 id="從-ssh-操控遠端的圖形桌面">從 SSH 操控遠端的圖形桌面&lt;/h2>
&lt;p>想從一條純文字的 SSH 連線去操作遠端的 Wayland 圖形桌面（例如啟動應用、截圖、送 IPC 指令）時，會撞到兩類界線，判斷對是哪一類就知道怎麼繞。&lt;/p>
&lt;p>第一類是&lt;strong>圖形程式需要知道連到哪個顯示&lt;/strong>。SSH 進來的 shell 預設沒有圖形環境的環境變數，直接跑圖形程式會找不到 display。要對著遠端那個已經在跑的 Wayland session 操作，得補上它的環境變數：&lt;code>XDG_RUNTIME_DIR&lt;/code>（通常 &lt;code>/run/user/&amp;lt;uid&amp;gt;&lt;/code>）、&lt;code>WAYLAND_DISPLAY&lt;/code>（socket 名，如 &lt;code>wayland-1&lt;/code>）、必要時還有該 compositor 的 instance 變數與 &lt;code>DBUS_SESSION_BUS_ADDRESS&lt;/code>。這些值怎麼撈：socket 名用 &lt;code>ls /run/user/$(id -u)/wayland-*&lt;/code> 看；其餘變數直接從那個圖形 session 既有行程的環境複製最準——&lt;code>cat /proc/&amp;lt;compositor-pid&amp;gt;/environ | tr '\0' '\n' | grep -E 'WAYLAND_DISPLAY|XDG_RUNTIME_DIR|DBUS_SESSION|_INSTANCE_'&lt;/code>（&lt;code>&amp;lt;compositor-pid&amp;gt;&lt;/code> 用 &lt;code>pgrep -x Hyprland&lt;/code> 之類找）。撈到後 &lt;code>export&lt;/code> 進當前 SSH shell，這條連線就能對遠端的圖形 session 下指令、&lt;code>grim&lt;/code> 截圖。&lt;/p>
&lt;p>第二類是&lt;strong>有些東西必須從實體圖形終端機（VT，即 &lt;code>Ctrl+Alt+F1&lt;/code>~&lt;code>F6&lt;/code> 切換的那些文字主控台）啟動，SSH 的 pty 起不來&lt;/strong>。Wayland 的合成器（compositor，畫桌面、把視窗合成到螢幕、管輸入輸出的核心程式，如 Hyprland）需要一個真正的圖形 VT 上的登入 session，拿到 DRM master（對顯示卡的獨佔繪圖控制權）與 logind seat（一組綁在一起的實體螢幕／鍵鼠裝置）才能啟動；從 SSH 的 pty 起它的&lt;strong>預設 backend&lt;/strong> 會直接失敗（例如報 backend 建立失敗），因為預設 backend 要的 DRM master 與 seat 在 SSH 這條連線上不存在。判讀訊號：合成器一啟動就報 seat / DRM / backend 相關的錯，而你是從 SSH 起的——那就是這個界線。（例外：合成器多半有 headless backend，例如設 &lt;code>WLR_BACKENDS=headless&lt;/code> 就不要 DRM master、不需 VT，專給 CI、雲端、自動化測試用；nested（跑在另一個 Wayland session 裡）也不需要。所以精確說是「預設 backend 需要圖形 VT」，不是「合成器一定起不來」。）&lt;/p></description><content:encoded><![CDATA[<p>遠端操作 Linux 時，很多問題出在「你的終端機」與「遠端 session」之間那條連線的狀態，而不在遠端那台機器本身。終端機被上一個程式留在奇怪的模式、字元編碼與終端機能力沒對上、或你想從一條純文字的 SSH 連線去驅動一個需要實體螢幕的圖形桌面——這些問題的共同點是：現象發生在連線的某一層，判斷對是哪一層，修復就很直接。</p>
<p>SSH「連不上」本身（<code>Permission denied</code>、<code>Host key verification failed</code>、<code>Connection refused</code>）的判讀與修復，見 <a href="../../install/ssh-keyless-bootstrap/">外部連入與無 key 的 bootstrap 路徑</a> 的重連段落。這篇處理的是「連上了、但終端機或 session 的狀態不對」的那些情況。</p>
<h2 id="ssh-斷線後本機終端機噴亂碼狂跳字元">SSH 斷線後本機終端機噴亂碼、狂跳字元</h2>
<p>一個嚇人但無害的情況：SSH 連線被中斷後，你本機的終端機開始瘋狂輸出像 <code>&lt;35;80;24M</code> 這樣的序列，尤其在你移動滑鼠時狂跳。這不是遠端機器在打字，是<strong>你本機的終端機被卡在滑鼠回報模式</strong>。</p>
<p>判讀關鍵在「什麼時候噴」：如果那串亂碼只在你移動滑鼠時出現、而且形如 <code>數字;數字M</code>，那就是滑鼠座標回報。成因是遠端跑的某個全螢幕程式（TUI、編輯器、終端機多工器）啟動時對終端機開了滑鼠追蹤模式，SSH 被硬斷時它來不及送出「關閉滑鼠模式」的序列就死了，於是你本機終端機還停在回報模式，滑鼠一動就把游標座標當輸入送進來。</p>
<p>修復是重置終端機的模式，跟遠端機器無關：</p>
<ul>
<li>最快：開一個新的終端機分頁 / 視窗。模式是「那個終端機 session」的狀態，新視窗是乾淨的。</li>
<li>救現有視窗：先把滑鼠移開別動（洪流會停），盲打 <code>reset</code> 再 Enter，送出終端機重置。</li>
<li>若 <code>reset</code> 沒清掉，補送關閉滑鼠回報的序列：<code>printf '\033[?1000l\033[?1002l\033[?1003l\033[?1006l'</code>。</li>
</ul>
<p>同一類的還有「alternate screen 沒還原」——遠端的全螢幕程式異常結束時，本機終端機可能卡在替代畫面緩衝區，看起來像畫面清空或凍結。<code>reset</code> 同樣能救。歸納起來：<strong>SSH 被硬斷後本機終端機行為異常，先懷疑「對端程式來不及還原終端機模式」，用 <code>reset</code> 或開新視窗處理本機終端機狀態，不必急著重連遠端。</strong></p>
<h2 id="遠端打字變亂碼重複位置錯亂">遠端打字變亂碼、重複、位置錯亂</h2>
<p>連上遠端後，如果互動式輸入變得不對——打一個字出現好幾個、游標位置錯亂、畫面重繪殘影——通常是兩層問題之一，判讀方式是分開排除。</p>
<p>第一層是<strong>字元編碼（locale）</strong>。從某些本機（例如 macOS）SSH 進 Linux 時，本機會把 <code>LC_CTYPE</code> 之類的變數帶過去；如果遠端沒有對應的 locale、就會退回 POSIX/C locale，讓終端機的行編輯（ZLE、readline）對多位元組字元的寬度判斷出錯，表現為輸入重複或錯位。判斷方式是在遠端 <code>locale</code> 看目前值、<code>locale -a</code> 看有沒有裝對應的 UTF-8 locale。修法是在遠端明確設好 <code>LANG</code> / <code>LC_CTYPE</code> 到一個實際存在的 UTF-8 locale，而不是讓它繼承一個遠端不認得的值。</p>
<p>第二層是<strong>終端機能力資料庫（terminfo）</strong>。你本機終端機的 <code>TERM</code> 值（例如某些新終端機用 <code>xterm-ghostty</code> 之類的自訂值）如果在遠端沒有對應的 terminfo 條目，遠端程式就不知道怎麼正確地清行、移動游標、重繪，畫面就會亂。判斷方式是在遠端 <code>echo $TERM</code> 看值、<code>infocmp $TERM</code> 看遠端認不認得。修法是把本機的 terminfo 條目送過去讓遠端安裝：<code>infocmp -x $TERM | ssh &lt;遠端&gt; 'tic -x -'</code>。</p>
<p>先分清是 locale 還是 terminfo，兩者症狀相似但修法不同：locale 是編碼寬度、terminfo 是繪製指令。查 <code>locale</code> 跟查 <code>$TERM</code> + <code>infocmp</code> 就能分開。</p>
<h2 id="從-ssh-操控遠端的圖形桌面">從 SSH 操控遠端的圖形桌面</h2>
<p>想從一條純文字的 SSH 連線去操作遠端的 Wayland 圖形桌面（例如啟動應用、截圖、送 IPC 指令）時，會撞到兩類界線，判斷對是哪一類就知道怎麼繞。</p>
<p>第一類是<strong>圖形程式需要知道連到哪個顯示</strong>。SSH 進來的 shell 預設沒有圖形環境的環境變數，直接跑圖形程式會找不到 display。要對著遠端那個已經在跑的 Wayland session 操作，得補上它的環境變數：<code>XDG_RUNTIME_DIR</code>（通常 <code>/run/user/&lt;uid&gt;</code>）、<code>WAYLAND_DISPLAY</code>（socket 名，如 <code>wayland-1</code>）、必要時還有該 compositor 的 instance 變數與 <code>DBUS_SESSION_BUS_ADDRESS</code>。這些值怎麼撈：socket 名用 <code>ls /run/user/$(id -u)/wayland-*</code> 看；其餘變數直接從那個圖形 session 既有行程的環境複製最準——<code>cat /proc/&lt;compositor-pid&gt;/environ | tr '\0' '\n' | grep -E 'WAYLAND_DISPLAY|XDG_RUNTIME_DIR|DBUS_SESSION|_INSTANCE_'</code>（<code>&lt;compositor-pid&gt;</code> 用 <code>pgrep -x Hyprland</code> 之類找）。撈到後 <code>export</code> 進當前 SSH shell，這條連線就能對遠端的圖形 session 下指令、<code>grim</code> 截圖。</p>
<p>第二類是<strong>有些東西必須從實體圖形終端機（VT，即 <code>Ctrl+Alt+F1</code>~<code>F6</code> 切換的那些文字主控台）啟動，SSH 的 pty 起不來</strong>。Wayland 的合成器（compositor，畫桌面、把視窗合成到螢幕、管輸入輸出的核心程式，如 Hyprland）需要一個真正的圖形 VT 上的登入 session，拿到 DRM master（對顯示卡的獨佔繪圖控制權）與 logind seat（一組綁在一起的實體螢幕／鍵鼠裝置）才能啟動；從 SSH 的 pty 起它的<strong>預設 backend</strong> 會直接失敗（例如報 backend 建立失敗），因為預設 backend 要的 DRM master 與 seat 在 SSH 這條連線上不存在。判讀訊號：合成器一啟動就報 seat / DRM / backend 相關的錯，而你是從 SSH 起的——那就是這個界線。（例外：合成器多半有 headless backend，例如設 <code>WLR_BACKENDS=headless</code> 就不要 DRM master、不需 VT，專給 CI、雲端、自動化測試用；nested（跑在另一個 Wayland session 裡）也不需要。所以精確說是「預設 backend 需要圖形 VT」，不是「合成器一定起不來」。）</p>
<p>繞法是回到那台機器的實體圖形終端機去啟動 compositor，但「回到 VT」這件事也可以從 SSH 遠端做：</p>
<ul>
<li><code>sudo chvt &lt;N&gt;</code> 從 SSH 切換那台機器目前顯示的虛擬終端機到第 N 個，比在虛擬機視窗裡跟宿主的 <code>Ctrl+Alt+Fn</code> 快捷鍵搏鬥穩定。</li>
<li>切過去卻是空白、沒有登入提示，通常是那個 VT 上沒有 getty 在跑：<code>sudo systemctl start getty@tty&lt;N&gt;</code>（開機時 <code>enabled</code> 但 <code>inactive</code> 是常見狀態，logind 的 autovt 沒觸發）。</li>
<li><code>sudo fgconsole</code> 確認目前是哪個 VT 在前景。</li>
</ul>
<p>還有一個容易混淆的點：一台虛擬機可能同時有「序列主控台」跟「圖形顯示」兩個獨立輸出。在 guest 內 <code>chvt</code> 只切圖形那側，序列主控台看到的畫面不會變。如果你在虛擬機軟體裡看的是序列主控台，圖形桌面得切到顯示輸出那個 view 才看得到。判讀：切了 VT 但畫面沒反應，先確認你正在看的是哪個輸出。</p>
<h2 id="判讀路由">判讀路由</h2>
<p>遠端 / 終端機問題的分流：</p>
<ul>
<li>本機終端機噴亂碼、只在動滑鼠時噴 → 滑鼠回報模式沒關（本機終端機狀態），<code>reset</code> 或開新視窗。</li>
<li>遠端打字重複 / 錯位 → 分 locale（查 <code>locale</code>）與 terminfo（查 <code>$TERM</code> + <code>infocmp</code>）。</li>
<li>圖形程式在 SSH 下找不到 display → 補 <code>WAYLAND_DISPLAY</code> / <code>XDG_RUNTIME_DIR</code> 等環境變數。</li>
<li>compositor 從 SSH 起不來、報 seat/DRM 錯 → 它需要實體 VT，用 <code>chvt</code> + <code>getty@tty&lt;N&gt;</code> 回到圖形 VT 啟動。</li>
<li>SSH 連不上（拒絕 / host key / refused）→ 見 <a href="../../install/ssh-keyless-bootstrap/">外部連入與無 key 的 bootstrap 路徑</a>。</li>
</ul>
<p>這幾種分流的共同底線是先讀權威狀態（<code>locale</code>、<code>$TERM</code>、runtime 目錄、<code>loginctl</code>、<code>fgconsole</code>）再下判斷；背後的方法論見 <a href="../diagnosis-read-authoritative-state/">診斷心法</a>。</p>
]]></content:encoded></item><item><title>遠端工具</title><link>https://tarrragon.github.io/blog/linux/tools/remote/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/tools/remote/</guid><description>&lt;p>遠端操作的工具選擇，圍繞一個核心需求：&lt;strong>把工作留在遠端、連線斷了也不掉。&lt;/strong> SSH 連線本身是脆弱的（網路一抖就斷），所以遠端工作的關鍵是「就算斷了，遠端的 session 與長任務還在、重連就接回去」，而非追求連線本身多穩。這一組工具都在解這件事的不同面向。&lt;/p>
&lt;h2 id="核心終端機多工器">核心：終端機多工器&lt;/h2>
&lt;p>遠端工作的地基是終端機多工器（&lt;code>tmux&lt;/code> / &lt;code>zellij&lt;/code>）——它把你的 session 常駐在遠端，SSH 斷了 session 不受影響，重連 &lt;code>attach&lt;/code> 就回到原狀。這也是把長任務交給遠端機器無人值守跑的前提。深入配置與比較見 &lt;a href="../cli/">CLI 環境工具&lt;/a> 裡的多工器篇：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="../cli/tmux-persistence-and-basics/">tmux 持久化與基礎&lt;/a>——最通用的多工器，session 持久化的核心概念。&lt;/li>
&lt;li>&lt;a href="../cli/zellij-pane/">zellij 分頁與 pane&lt;/a>——較新、開箱即用、內建佈局的多工器。&lt;/li>
&lt;li>&lt;a href="../cli/zellij-remote-web-client/">zellij 遠端 web 客戶端&lt;/a>——從瀏覽器連遠端 session 的路徑。&lt;/li>
&lt;/ul>
&lt;h2 id="連線與同步工具">連線與同步工具&lt;/h2>
&lt;p>多工器保住 session 之後，還有兩塊獨立的能力：連線層（怎麼接上遠端、斷了怎麼辦）與同步層（本地與遠端的檔案怎麼一致）。這兩塊各有多個工具、解不同弱點，挑錯會很痛——連線存活、可達性、檔案一致是三層不同的問題。&lt;/p>
&lt;ul>
&lt;li>&lt;a href="connection-and-sync-tools/">遠端連線與同步工具選型&lt;/a>——&lt;code>ssh&lt;/code> / &lt;code>mosh&lt;/code>（漫遊不斷線）/ &lt;code>autossh&lt;/code>（自動重連）、&lt;code>tailscale&lt;/code> / &lt;code>wireguard&lt;/code>（NAT 後可達性）、&lt;code>rsync&lt;/code> / &lt;code>sshfs&lt;/code> / &lt;code>mutagen&lt;/code>（三種同步語義）、IDE remote 模式的定位與取捨。&lt;/li>
&lt;/ul>
&lt;h2 id="低頻寬--手機連線下的介面">低頻寬 / 手機連線下的介面&lt;/h2>
&lt;p>頻寬低或從手機 / 平板連線時，只傳純文字的 TUI 介面（ASCII / Unicode 製圖，不傳影像）最穩。監控、圖表、檔案瀏覽、資料庫操作都有這類工具，整理在 &lt;a href="../cli/">CLI 環境工具&lt;/a>。&lt;/p>
&lt;h2 id="遠端連線與-session-的除錯">遠端連線與 session 的除錯&lt;/h2>
&lt;p>遠端連線本身出問題時（連不上、終端機噴亂碼、要從 SSH 操控圖形桌面），是診斷問題而非工具選擇問題，見 &lt;a href="../../debug/ssh-and-terminal-troubleshooting/">除錯與診斷：遠端連線與終端機問題&lt;/a> 與 &lt;a href="../../debug/machine-unreachable/">機器連不到或起不來&lt;/a>。&lt;/p>
&lt;h2 id="把機器交給遠端無人值守">把機器交給遠端無人值守&lt;/h2>
&lt;p>設好讓遠端機器在你離開後自己跑完長任務、把成果送回來，見 &lt;a href="../../install/unattended-remote-work/">Linux 安裝與機器初始化：讓機器跑無人值守的長任務&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>遠端操作的工具選擇，圍繞一個核心需求：<strong>把工作留在遠端、連線斷了也不掉。</strong> SSH 連線本身是脆弱的（網路一抖就斷），所以遠端工作的關鍵是「就算斷了，遠端的 session 與長任務還在、重連就接回去」，而非追求連線本身多穩。這一組工具都在解這件事的不同面向。</p>
<h2 id="核心終端機多工器">核心：終端機多工器</h2>
<p>遠端工作的地基是終端機多工器（<code>tmux</code> / <code>zellij</code>）——它把你的 session 常駐在遠端，SSH 斷了 session 不受影響，重連 <code>attach</code> 就回到原狀。這也是把長任務交給遠端機器無人值守跑的前提。深入配置與比較見 <a href="../cli/">CLI 環境工具</a> 裡的多工器篇：</p>
<ul>
<li><a href="../cli/tmux-persistence-and-basics/">tmux 持久化與基礎</a>——最通用的多工器，session 持久化的核心概念。</li>
<li><a href="../cli/zellij-pane/">zellij 分頁與 pane</a>——較新、開箱即用、內建佈局的多工器。</li>
<li><a href="../cli/zellij-remote-web-client/">zellij 遠端 web 客戶端</a>——從瀏覽器連遠端 session 的路徑。</li>
</ul>
<h2 id="連線與同步工具">連線與同步工具</h2>
<p>多工器保住 session 之後，還有兩塊獨立的能力：連線層（怎麼接上遠端、斷了怎麼辦）與同步層（本地與遠端的檔案怎麼一致）。這兩塊各有多個工具、解不同弱點，挑錯會很痛——連線存活、可達性、檔案一致是三層不同的問題。</p>
<ul>
<li><a href="connection-and-sync-tools/">遠端連線與同步工具選型</a>——<code>ssh</code> / <code>mosh</code>（漫遊不斷線）/ <code>autossh</code>（自動重連）、<code>tailscale</code> / <code>wireguard</code>（NAT 後可達性）、<code>rsync</code> / <code>sshfs</code> / <code>mutagen</code>（三種同步語義）、IDE remote 模式的定位與取捨。</li>
</ul>
<h2 id="低頻寬--手機連線下的介面">低頻寬 / 手機連線下的介面</h2>
<p>頻寬低或從手機 / 平板連線時，只傳純文字的 TUI 介面（ASCII / Unicode 製圖，不傳影像）最穩。監控、圖表、檔案瀏覽、資料庫操作都有這類工具，整理在 <a href="../cli/">CLI 環境工具</a>。</p>
<h2 id="遠端連線與-session-的除錯">遠端連線與 session 的除錯</h2>
<p>遠端連線本身出問題時（連不上、終端機噴亂碼、要從 SSH 操控圖形桌面），是診斷問題而非工具選擇問題，見 <a href="../../debug/ssh-and-terminal-troubleshooting/">除錯與診斷：遠端連線與終端機問題</a> 與 <a href="../../debug/machine-unreachable/">機器連不到或起不來</a>。</p>
<h2 id="把機器交給遠端無人值守">把機器交給遠端無人值守</h2>
<p>設好讓遠端機器在你離開後自己跑完長任務、把成果送回來，見 <a href="../../install/unattended-remote-work/">Linux 安裝與機器初始化：讓機器跑無人值守的長任務</a>。</p>
]]></content:encoded></item><item><title>環境建置的操作順序</title><link>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/</guid><description>&lt;p>Dotfile 教學模組按主題組織（shell、終端機、視窗管理），適合理解各層概念。但第一次建環境時需要的是另一種順序——&lt;strong>按依賴關係排列的操作清單&lt;/strong>，因為有些步驟是後續步驟的前提。&lt;/p>
&lt;p>SSH key 是典型例子：管理工具和 shell 配置的知識在模組一和模組二，但實際操作時 SSH key 比這兩者都早——因為 &lt;code>git clone git@github.com:...&lt;/code> 本身就需要 SSH key。如果照模組順序走，到模組二才發現 dotfile repo clone 不下來。&lt;/p>
&lt;p>這篇是路線圖，告訴你每一步做什麼、為什麼這個順序、以及去哪個模組看具體操作。&lt;/p>
&lt;h2 id="階段一基礎設施後續所有步驟的前提">階段一：基礎設施（後續所有步驟的前提）&lt;/h2>
&lt;p>這些步驟在任何配置之前完成，因為它們是 Git、遠端存取、dotfile clone 的前提。&lt;/p>
&lt;h3 id="1-安裝作業系統--建立使用者帳號">1. 安裝作業系統 + 建立使用者帳號&lt;/h3>
&lt;p>macOS：開箱即用。Linux：選發行版（Arch 如果要用 Hyprland）、完成安裝、建立非 root 使用者。安裝程式每個選項該怎麼判讀、裝完最小系統缺哪些必要工具（連 &lt;code>sudo&lt;/code> 都可能沒有），見 &lt;a href="https://tarrragon.github.io/blog/linux/install/install-option-decisions/" data-link-title="Linux 安裝選項判讀" data-link-desc="在 Linux 安裝程式面對 locale、網路、磁碟分割、檔案系統、bootloader 等選項、需要判斷依據而非靠預設值硬選時回來讀">Linux 安裝選項判讀&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/linux/install/minimal-install-verify/" data-link-title="最小安裝後的工具驗證與補足" data-link-desc="最小化安裝的 Linux 裝完發現連 sudo 或 which 都沒有、bootstrap 腳本第一行就炸、需要先確認系統缺哪些必要工具再補時回來讀">最小安裝後的工具驗證與補足&lt;/a>——這一步是整個系列展開最深、卻最常被一句帶過的地基。&lt;/p>
&lt;h3 id="2-生成-ssh-key-pair">2. 生成 SSH key pair&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t ed25519 -C &lt;span class="s2">&amp;#34;your-email@example.com&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>為什麼這麼早做：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Git 操作&lt;/strong>：GitHub / GitLab 的 SSH 認證需要 public key。dotfile repo 通常用 SSH URL（&lt;code>git@github.com:...&lt;/code>），clone 前要先把 key 部署到 GitHub。用 HTTPS URL 可以繞過 SSH key，但長期來看 SSH key 是更省事的認證方式。還沒有 key、或想用 HTTPS / PAT 把 dotfile 弄進一台新機器的幾種路徑，見 &lt;a href="https://tarrragon.github.io/blog/linux/install/ssh-keyless-bootstrap/" data-link-title="外部連入、SSH key 與無 key 的 bootstrap 路徑" data-link-desc="要從本機終端機操作新裝好的 Linux 機器、設 SSH key 免密碼、或還沒有 key 就想把 dotfile 弄進機器跑 install.sh 時回來讀">外部連入、SSH key 與無 key 的 bootstrap 路徑&lt;/a>。&lt;/li>
&lt;li>&lt;strong>遠端救援&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七&lt;/a>的場景三（GPU hang）依賴 SSH 作為桌面凍結時的救生通道。key 提前設好，出問題時才有路可走。&lt;/li>
&lt;li>&lt;strong>跨機器操作&lt;/strong>：筆電連桌機、桌機連 VM、VS Code Remote SSH——都靠這把 key。&lt;/li>
&lt;/ul>
&lt;h3 id="3-部署-public-key">3. 部署 public key&lt;/h3>
&lt;p>把 &lt;code>~/.ssh/id_ed25519.pub&lt;/code> 加到需要的服務：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加到 GitHub&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">cat ~/.ssh/id_ed25519.pub
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 複製輸出，貼到 GitHub → Settings → SSH and GPG keys → New SSH key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加到另一台機器（可選，用於跨機器 SSH）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">ssh-copy-id user@target-machine&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-package-manager--git">4. Package manager + Git&lt;/h3>
&lt;p>macOS：先裝 Homebrew（macOS 的套件管理器，後續安裝 stow、tmux 等工具都靠它），再裝 Git：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 Homebrew&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 安裝 Git（或用 xcode-select --install，會一併裝 Apple 的 Git）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">brew install git&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Arch：&lt;code>pacman&lt;/code> 隨 OS 安裝已可用，直接裝 Git：&lt;code>pacman -S git&lt;/code>。&lt;/p></description><content:encoded><![CDATA[<p>Dotfile 教學模組按主題組織（shell、終端機、視窗管理），適合理解各層概念。但第一次建環境時需要的是另一種順序——<strong>按依賴關係排列的操作清單</strong>，因為有些步驟是後續步驟的前提。</p>
<p>SSH key 是典型例子：管理工具和 shell 配置的知識在模組一和模組二，但實際操作時 SSH key 比這兩者都早——因為 <code>git clone git@github.com:...</code> 本身就需要 SSH key。如果照模組順序走，到模組二才發現 dotfile repo clone 不下來。</p>
<p>這篇是路線圖，告訴你每一步做什麼、為什麼這個順序、以及去哪個模組看具體操作。</p>
<h2 id="階段一基礎設施後續所有步驟的前提">階段一：基礎設施（後續所有步驟的前提）</h2>
<p>這些步驟在任何配置之前完成，因為它們是 Git、遠端存取、dotfile clone 的前提。</p>
<h3 id="1-安裝作業系統--建立使用者帳號">1. 安裝作業系統 + 建立使用者帳號</h3>
<p>macOS：開箱即用。Linux：選發行版（Arch 如果要用 Hyprland）、完成安裝、建立非 root 使用者。安裝程式每個選項該怎麼判讀、裝完最小系統缺哪些必要工具（連 <code>sudo</code> 都可能沒有），見 <a href="/blog/linux/install/install-option-decisions/" data-link-title="Linux 安裝選項判讀" data-link-desc="在 Linux 安裝程式面對 locale、網路、磁碟分割、檔案系統、bootloader 等選項、需要判斷依據而非靠預設值硬選時回來讀">Linux 安裝選項判讀</a> 與 <a href="/blog/linux/install/minimal-install-verify/" data-link-title="最小安裝後的工具驗證與補足" data-link-desc="最小化安裝的 Linux 裝完發現連 sudo 或 which 都沒有、bootstrap 腳本第一行就炸、需要先確認系統缺哪些必要工具再補時回來讀">最小安裝後的工具驗證與補足</a>——這一步是整個系列展開最深、卻最常被一句帶過的地基。</p>
<h3 id="2-生成-ssh-key-pair">2. 生成 SSH key pair</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t ed25519 -C <span class="s2">&#34;your-email@example.com&#34;</span></span></span></code></pre></div><p>為什麼這麼早做：</p>
<ul>
<li><strong>Git 操作</strong>：GitHub / GitLab 的 SSH 認證需要 public key。dotfile repo 通常用 SSH URL（<code>git@github.com:...</code>），clone 前要先把 key 部署到 GitHub。用 HTTPS URL 可以繞過 SSH key，但長期來看 SSH key 是更省事的認證方式。還沒有 key、或想用 HTTPS / PAT 把 dotfile 弄進一台新機器的幾種路徑，見 <a href="/blog/linux/install/ssh-keyless-bootstrap/" data-link-title="外部連入、SSH key 與無 key 的 bootstrap 路徑" data-link-desc="要從本機終端機操作新裝好的 Linux 機器、設 SSH key 免密碼、或還沒有 key 就想把 dotfile 弄進機器跑 install.sh 時回來讀">外部連入、SSH key 與無 key 的 bootstrap 路徑</a>。</li>
<li><strong>遠端救援</strong>：<a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七</a>的場景三（GPU hang）依賴 SSH 作為桌面凍結時的救生通道。key 提前設好，出問題時才有路可走。</li>
<li><strong>跨機器操作</strong>：筆電連桌機、桌機連 VM、VS Code Remote SSH——都靠這把 key。</li>
</ul>
<h3 id="3-部署-public-key">3. 部署 public key</h3>
<p>把 <code>~/.ssh/id_ed25519.pub</code> 加到需要的服務：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 加到 GitHub</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">cat ~/.ssh/id_ed25519.pub
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 複製輸出，貼到 GitHub → Settings → SSH and GPG keys → New SSH key</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 加到另一台機器（可選，用於跨機器 SSH）</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">ssh-copy-id user@target-machine</span></span></code></pre></div><h3 id="4-package-manager--git">4. Package manager + Git</h3>
<p>macOS：先裝 Homebrew（macOS 的套件管理器，後續安裝 stow、tmux 等工具都靠它），再裝 Git：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝 Homebrew</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">/bin/bash -c <span class="s2">&#34;</span><span class="k">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 安裝 Git（或用 xcode-select --install，會一併裝 Apple 的 Git）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">brew install git</span></span></code></pre></div><p>Arch：<code>pacman</code> 隨 OS 安裝已可用，直接裝 Git：<code>pacman -S git</code>。</p>
<h3 id="5-clone-dotfile-repo">5. Clone dotfile repo</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">git clone git@github.com:yourname/dotfiles.git ~/dotfiles</span></span></code></pre></div><p>如果是第一次建 dotfile repo（還沒有 repo），先建一個空的再開始往裡面加配置。具體做法見<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a>。</p>
<h2 id="階段二shell-與終端機日常操作的基礎">階段二：Shell 與終端機（日常操作的基礎）</h2>
<p>Shell 是所有操作的介面，終端機是 shell 的容器。這兩層配置好，後續的安裝、設定、除錯效率會高很多。</p>
<h3 id="6-安裝管理工具stow--chezmoi">6. 安裝管理工具（stow / chezmoi）</h3>
<p>把 dotfile repo 裡的配置 symlink 到正確位置。具體選型和操作見<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一</a>。</p>
<h3 id="7-shell-配置zshrc--bashrc">7. Shell 配置（.zshrc / .bashrc）</h3>
<p>模組化拆分、PATH 設定、alias、prompt。做完這一步，終端機操作才順手。見<a href="/blog/linux/dotfile/02-shell-config/" data-link-title="模組二：Shell 配置" data-link-desc="shell 配置檔長成一坨不敢動時回來讀 — .zshrc/.bashrc 的結構化拆分、alias/function/PATH 的模組化設計">模組二：Shell 配置</a>。</p>
<h3 id="8-終端機--編輯器">8. 終端機 + 編輯器</h3>
<p>Terminal emulator 選型、tmux/zellij、neovim 基礎配置。見<a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器</a>。</p>
<p>macOS 用戶到階段二完成後就有一個完整的工作環境。下一步依序讀<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一</a>（管理工具選型）、<a href="/blog/linux/dotfile/02-shell-config/" data-link-title="模組二：Shell 配置" data-link-desc="shell 配置檔長成一坨不敢動時回來讀 — .zshrc/.bashrc 的結構化拆分、alias/function/PATH 的模組化設計">模組二</a>（shell 配置）、<a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三</a>（終端機），然後跳到階段四的 bootstrap script。階段三是 Linux 桌面環境的設定，macOS 用戶跳過。</p>
<h2 id="階段三桌面環境linux-限定">階段三：桌面環境（Linux 限定）</h2>
<p>macOS 用戶到階段二就有一個完整的工作環境了。以下步驟是 Linux 桌面環境的設定，macOS 用戶可以跳到階段四。</p>
<h3 id="9-視窗管理器">9. 視窗管理器</h3>
<p>平鋪式 vs 浮動式的選型，Hyprland 安裝和核心配置。見<a href="/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">模組四</a>和<a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五</a>。</p>
<h3 id="10-桌面配套工具--rice">10. 桌面配套工具 + Rice</h3>
<p>waybar、wofi、mako、配色系統。見<a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a>。</p>
<h3 id="11-啟用-ssh-server--預防措施">11. 啟用 SSH server + 預防措施</h3>
<p>桌面環境可用之後，設定遠端救援通道和預防性配置：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># SSH server（出問題時可以從另一台機器救援）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">sudo systemctl <span class="nb">enable</span> sshd
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">sudo systemctl start sshd
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 停用密碼登入（確保 SSH key 已設好）</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 編輯 /etc/ssh/sshd_config：PasswordAuthentication no</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># swap（OOM 緩衝）</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">sudo fallocate -l 4G /swapfile
</span></span><span class="line"><span class="ln">10</span><span class="cl">sudo chmod <span class="m">600</span> /swapfile <span class="o">&amp;&amp;</span> sudo mkswap /swapfile <span class="o">&amp;&amp;</span> sudo swapon /swapfile
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># systemd-oomd</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">sudo systemctl <span class="nb">enable</span> systemd-oomd</span></span></code></pre></div><p>為什麼放在桌面設定之後：SSH server 和 swap 是預防措施，桌面能用了才有東西要保護。但 SSH key pair（階段一步驟 2-3）要提前做——key pair 是認證基礎設施，server 只是把門打開。</p>
<p>詳細的故障場景和預防措施見<a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七：桌面環境維護與故障排除</a>。</p>
<h2 id="階段四同步與可攜性">階段四：同步與可攜性</h2>
<p>環境建好之後，確保這份配置能搬到下一台機器。</p>
<h3 id="12-bootstrap-script">12. Bootstrap script</h3>
<p>把階段一到三的操作自動化成 script，下次換機器跑一次就好。見<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>。</p>
<h3 id="13-secret-管理">13. Secret 管理</h3>
<p>哪些東西該進 repo、哪些要排除（SSH private key、API token、密碼）。同樣見<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八</a>。</p>
<h2 id="依賴關係速查">依賴關係速查</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">OS 安裝
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ SSH key pair ← 後續所有 Git / SSH 操作的前提
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">       └─ Git 安裝
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">            └─ dotfile repo clone
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">                 └─ 管理工具（stow link）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">                      ├─ Shell 配置
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">                      ├─ 終端機 + 編輯器
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">                      └─ 桌面環境（Linux，macOS 到此為止 → 直接跳階段四）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">                           └─ SSH server + 預防措施
</span></span><span class="line"><span class="ln">10</span><span class="cl">                                └─ Bootstrap script 自動化</span></span></code></pre></div><h2 id="可以亂序的步驟">可以亂序的步驟</h2>
<p>依賴圖裡<strong>同一層級的步驟</strong>可以調換順序。具體來說：</p>
<ul>
<li>Shell 配置、終端機配置、編輯器配置三者互不依賴，先做哪個都行</li>
<li>視窗管理器和桌面配套工具可以交替設定（先裝 Hyprland 再裝 waybar，或反過來）</li>
<li>swap 和 SSH server 互不依賴，先做哪個都行</li>
</ul>
<p>跨層級的依賴必須按順序：SSH key 是 clone repo 的前提，repo 是 stow link 的前提，stow link 是 shell 配置生效的前提。</p>
]]></content:encoded></item><item><title>外部連入、SSH key 與無 key 的 bootstrap 路徑</title><link>https://tarrragon.github.io/blog/linux/install/ssh-keyless-bootstrap/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/install/ssh-keyless-bootstrap/</guid><description>&lt;p>操作一台新機器，從你本機的終端機透過 SSH 連進去是阻力最小的位置。直接在主控台操作有兩個實際的痛點：純文字的主控台（TTY 或虛擬機的序列 console）往往不能貼上，長指令只能手打、還容易掉字；畫面也通常擠、不能捲。把機器的 sshd 跑起來、從本機 SSH 進去之後，貼上、捲動、補全全部回到你熟悉的環境，而且這條路本身就貼近真實的遠端維運。&lt;/p>
&lt;p>這篇處理三件事：把 sshd 跑起來並從本機連入、設 SSH key 達到免密碼、以及一個容易被卡住的情境——你還沒有 SSH key 時，怎麼把 dotfile 弄進機器、跑完基礎安裝。&lt;/p>
&lt;h2 id="啟用-sshd-並從本機連入">啟用 sshd 並從本機連入&lt;/h2>
&lt;p>讓機器能被 SSH 連入只需要兩步：裝 SSH 伺服器、啟動它的服務。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">pacman -S openssh &lt;span class="c1"># 剛裝好的系統套件資料庫是新的，-S 不必先 -Sy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">systemctl &lt;span class="nb">enable&lt;/span> --now sshd &lt;span class="c1"># enable 開機自啟、--now 立刻啟動&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>指令以 Arch 為例。換發行版時套件管理器不同（Fedora &lt;code>dnf&lt;/code>、Debian/Ubuntu &lt;code>apt&lt;/code>），服務名也可能不同——Debian 系的 OpenSSH 服務叫 &lt;code>ssh&lt;/code> 不是 &lt;code>sshd&lt;/code>，那邊要 &lt;code>systemctl enable --now ssh&lt;/code>。&lt;/p>
&lt;p>從本機連的時候用一般使用者、不要用 root：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh user@&amp;lt;機器 IP&amp;gt; &lt;span class="c1"># IP 來自機器上的 ip -brief a&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>用一般使用者是因為多數發行版的 sshd 預設擋 root 密碼登入（&lt;code>PermitRootLogin prohibit-password&lt;/code>）——root 只能用 key、不能用密碼。這個預設是好的安全姿態，順著它走、用你裝系統時建的一般使用者連即可。連進去之後，後續所有需要長指令、需要貼上的操作都在這個 session 裡做，不再回主控台手打。&lt;/p>
&lt;p>這裡啟用 sshd 是為了 bootstrap 期間從本機連入操作，跟 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/" data-link-title="環境建置的操作順序" data-link-desc="第一次從零建立 Linux 或 macOS 開發環境、不確定先做什麼後做什麼時讀 — 依賴順序路線圖，每一步附對應模組連結">操作順序指引&lt;/a> 後段把 sshd 當「桌面就緒後的常駐遠端救援通道」是兩個不同的時間點與目的——同一個 &lt;code>systemctl enable sshd&lt;/code> 動作，這裡是為了「現在好操作」，那裡是為了「之後好救援」。&lt;/p>
&lt;h2 id="ssh-key-免密碼">SSH key 免密碼&lt;/h2>
&lt;p>每次連線都打密碼很快會變成阻力，尤其當你要反覆同步檔案或跑自動化時。SSH key 讓本機免密碼連入，做法是生一把金鑰、把公鑰放進機器、本機用私鑰認證。&lt;/p>
&lt;p>生 key 時建議生一把專用的、不要佔用本機的預設金鑰槽，並在 SSH 設定裡給它一個好記的別名：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t ed25519 -f ~/.ssh/vm_arch -N &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span> -C &lt;span class="s2">&amp;#34;vm_arch host-&amp;gt;target&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在 ~/.ssh/config 加一段別名：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># Host vm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># HostName &amp;lt;機器 IP&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># User &amp;lt;你的使用者&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1"># IdentityFile ~/.ssh/vm_arch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># IdentitiesOnly yes&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>專用 key 的好處是它的權限範圍清楚——這把只給這台機器用，跟你其他身分的金鑰互不牽連。設好別名後，&lt;code>ssh vm&lt;/code> 就免密碼連入，後面的 &lt;code>rsync&lt;/code>、&lt;code>scp&lt;/code> 也跟著免密碼。&lt;/p>
&lt;p>把公鑰放進機器有兩條路。標準工具是 &lt;code>ssh-copy-id&lt;/code>，它會在本機跑、要你輸入一次目標機的密碼。另一條省一次切換的路是：當你已經用密碼連進機器、且這個 session 在真終端機裡（貼上可用），直接把公鑰內容貼進機器的 &lt;code>authorized_keys&lt;/code>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">mkdir -p ~/.ssh &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> chmod &lt;span class="m">700&lt;/span> ~/.ssh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;ssh-ed25519 AAAA... 你的公鑰內容&amp;#34;&lt;/span> &amp;gt;&amp;gt; ~/.ssh/authorized_keys
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">chmod &lt;span class="m">600&lt;/span> ~/.ssh/authorized_keys&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>兩條路等價，選哪條看你當下在哪——還沒連上就用 &lt;code>ssh-copy-id&lt;/code>，已經連上就直接貼，少一次切換。&lt;/p>
&lt;h2 id="還沒有-ssh-key-時怎麼把-dotfile-弄進去">還沒有 SSH key 時，怎麼把 dotfile 弄進去&lt;/h2>
&lt;p>設 SSH key 是讓「之後」連線變方便，但 bootstrap 的第一步——把 dotfile repo 弄進機器——並不一定需要 key。常見的卡點是把「clone repo」跟「有 SSH key」綁在一起，但 clone 有不需要 key 的路徑。怎麼把 dotfile 弄進去，取決於這份 dotfile 放在哪。&lt;/p>
&lt;p>&lt;strong>repo 是公開的（在 GitHub 之類）&lt;/strong>：用 HTTPS clone，公開 repo 的唯讀 clone 不需要任何認證。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">git clone https://github.com/&amp;lt;帳號&amp;gt;/dotfiles ~/dotfiles
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ~/dotfiles &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> ./scripts/install.sh&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這是最直接的路——機器只要能上網就能拉到 dotfile，完全繞過 key 的問題。clone URL 裡的帳號要對；用錯帳號（例如把 email handle 當成 GitHub 帳號）會 clone 失敗或抓到別的 repo，這類筆誤在只看 README 範例時很容易漏掉。SSH key 在這個情境只有「之後要從機器 push 回去」才需要，純粹跑部署用不到。&lt;/p></description><content:encoded><![CDATA[<p>操作一台新機器，從你本機的終端機透過 SSH 連進去是阻力最小的位置。直接在主控台操作有兩個實際的痛點：純文字的主控台（TTY 或虛擬機的序列 console）往往不能貼上，長指令只能手打、還容易掉字；畫面也通常擠、不能捲。把機器的 sshd 跑起來、從本機 SSH 進去之後，貼上、捲動、補全全部回到你熟悉的環境，而且這條路本身就貼近真實的遠端維運。</p>
<p>這篇處理三件事：把 sshd 跑起來並從本機連入、設 SSH key 達到免密碼、以及一個容易被卡住的情境——你還沒有 SSH key 時，怎麼把 dotfile 弄進機器、跑完基礎安裝。</p>
<h2 id="啟用-sshd-並從本機連入">啟用 sshd 並從本機連入</h2>
<p>讓機器能被 SSH 連入只需要兩步：裝 SSH 伺服器、啟動它的服務。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pacman -S openssh             <span class="c1"># 剛裝好的系統套件資料庫是新的，-S 不必先 -Sy</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">systemctl <span class="nb">enable</span> --now sshd   <span class="c1"># enable 開機自啟、--now 立刻啟動</span></span></span></code></pre></div><p>指令以 Arch 為例。換發行版時套件管理器不同（Fedora <code>dnf</code>、Debian/Ubuntu <code>apt</code>），服務名也可能不同——Debian 系的 OpenSSH 服務叫 <code>ssh</code> 不是 <code>sshd</code>，那邊要 <code>systemctl enable --now ssh</code>。</p>
<p>從本機連的時候用一般使用者、不要用 root：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh user@&lt;機器 IP&gt;            <span class="c1"># IP 來自機器上的 ip -brief a</span></span></span></code></pre></div><p>用一般使用者是因為多數發行版的 sshd 預設擋 root 密碼登入（<code>PermitRootLogin prohibit-password</code>）——root 只能用 key、不能用密碼。這個預設是好的安全姿態，順著它走、用你裝系統時建的一般使用者連即可。連進去之後，後續所有需要長指令、需要貼上的操作都在這個 session 裡做，不再回主控台手打。</p>
<p>這裡啟用 sshd 是為了 bootstrap 期間從本機連入操作，跟 <a href="/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/" data-link-title="環境建置的操作順序" data-link-desc="第一次從零建立 Linux 或 macOS 開發環境、不確定先做什麼後做什麼時讀 — 依賴順序路線圖，每一步附對應模組連結">操作順序指引</a> 後段把 sshd 當「桌面就緒後的常駐遠端救援通道」是兩個不同的時間點與目的——同一個 <code>systemctl enable sshd</code> 動作，這裡是為了「現在好操作」，那裡是為了「之後好救援」。</p>
<h2 id="ssh-key-免密碼">SSH key 免密碼</h2>
<p>每次連線都打密碼很快會變成阻力，尤其當你要反覆同步檔案或跑自動化時。SSH key 讓本機免密碼連入，做法是生一把金鑰、把公鑰放進機器、本機用私鑰認證。</p>
<p>生 key 時建議生一把專用的、不要佔用本機的預設金鑰槽，並在 SSH 設定裡給它一個好記的別名：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t ed25519 -f ~/.ssh/vm_arch -N <span class="s2">&#34;&#34;</span> -C <span class="s2">&#34;vm_arch host-&gt;target&#34;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 在 ~/.ssh/config 加一段別名：</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">#   Host vm</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">#       HostName &lt;機器 IP&gt;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">#       User &lt;你的使用者&gt;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">#       IdentityFile ~/.ssh/vm_arch</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1">#       IdentitiesOnly yes</span></span></span></code></pre></div><p>專用 key 的好處是它的權限範圍清楚——這把只給這台機器用，跟你其他身分的金鑰互不牽連。設好別名後，<code>ssh vm</code> 就免密碼連入，後面的 <code>rsync</code>、<code>scp</code> 也跟著免密碼。</p>
<p>把公鑰放進機器有兩條路。標準工具是 <code>ssh-copy-id</code>，它會在本機跑、要你輸入一次目標機的密碼。另一條省一次切換的路是：當你已經用密碼連進機器、且這個 session 在真終端機裡（貼上可用），直接把公鑰內容貼進機器的 <code>authorized_keys</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">mkdir -p ~/.ssh <span class="o">&amp;&amp;</span> chmod <span class="m">700</span> ~/.ssh
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;ssh-ed25519 AAAA... 你的公鑰內容&#34;</span> &gt;&gt; ~/.ssh/authorized_keys
</span></span><span class="line"><span class="ln">3</span><span class="cl">chmod <span class="m">600</span> ~/.ssh/authorized_keys</span></span></code></pre></div><p>兩條路等價，選哪條看你當下在哪——還沒連上就用 <code>ssh-copy-id</code>，已經連上就直接貼，少一次切換。</p>
<h2 id="還沒有-ssh-key-時怎麼把-dotfile-弄進去">還沒有 SSH key 時，怎麼把 dotfile 弄進去</h2>
<p>設 SSH key 是讓「之後」連線變方便，但 bootstrap 的第一步——把 dotfile repo 弄進機器——並不一定需要 key。常見的卡點是把「clone repo」跟「有 SSH key」綁在一起，但 clone 有不需要 key 的路徑。怎麼把 dotfile 弄進去，取決於這份 dotfile 放在哪。</p>
<p><strong>repo 是公開的（在 GitHub 之類）</strong>：用 HTTPS clone，公開 repo 的唯讀 clone 不需要任何認證。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">git clone https://github.com/&lt;帳號&gt;/dotfiles ~/dotfiles
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">cd</span> ~/dotfiles <span class="o">&amp;&amp;</span> ./scripts/install.sh</span></span></code></pre></div><p>這是最直接的路——機器只要能上網就能拉到 dotfile，完全繞過 key 的問題。clone URL 裡的帳號要對；用錯帳號（例如把 email handle 當成 GitHub 帳號）會 clone 失敗或抓到別的 repo，這類筆誤在只看 README 範例時很容易漏掉。SSH key 在這個情境只有「之後要從機器 push 回去」才需要，純粹跑部署用不到。</p>
<p><strong>repo 是私有的、但機器能上網</strong>：機器可以直接 clone，用 GitHub Personal Access Token（PAT）走 HTTPS——這是私有 repo 免 SSH key 的標準解。clone 時把 PAT 當密碼填進認證，機器就拉得到，一樣不必在它上面設 SSH key。</p>
<p><strong>repo 還沒推到任何遠端、或機器離線</strong>：從本機把檔案傳進去。如果本機到機器的 SSH 已經能用（即使只是密碼登入），用 <code>tar</code> over SSH 一次傳進去（跟 <code>scp -r</code> 等價，差別只在 tar 能一次打包、又好控制要不要帶 <code>.git</code>）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">tar czf - --exclude <span class="s1">&#39;.git&#39;</span> . <span class="p">|</span> ssh user@host <span class="s1">&#39;mkdir -p ~/dotfiles &amp;&amp; tar xzf - -C ~/dotfiles&#39;</span></span></span></code></pre></div><p>這條只需要兩邊都有的 <code>ssh</code> 跟 <code>tar</code>，不依賴目標機有 rsync。從 macOS 傳的時候要關掉 AppleDouble 中繼檔，否則會夾帶一堆 <code>._</code> 開頭的中繼檔到 Linux 上：在指令前加 <code>COPYFILE_DISABLE=1</code>。完全離線、連 SSH 都還沒通時，最後手段是把 repo 放進 USB、掛載到機器上複製出來。</p>
<p>把 dotfile 弄進去之後，跑它的 <code>install.sh</code> 完成基礎安裝。如果安裝腳本一開始就要用 sudo，記得 sudo 必須在工具驗證階段就備好——它是 <a href="../minimal-install-verify/">最小安裝後的工具驗證與補足</a> 的前置，bootstrap 自身補不了。</p>
<h2 id="換一台新機器或重裝時ssh-為什麼突然連不上">換一台新機器（或重裝）時，SSH 為什麼突然連不上</h2>
<p>SSH 的別名、金鑰、<code>known_hosts</code> 都是綁在「某一台特定機器」上的，所以當你重裝、或換一台新 VM，先前設好的 <code>ssh &lt;別名&gt;</code> 往往會以看似無關的錯誤失敗——那套設定是為舊機器建的，而重裝後是另一台機器：不同的 IP、不同的 SSH host key、還沒裝 sshd、<code>authorized_keys</code> 也是空的。判讀的起點是把重裝後的機器當成全新的一台，重做第一次連線的設定，而不是沿用舊別名。</p>
<p>失敗會以三種形式出現，各對應不同層、各有各的修法：</p>
<p><code>Permission denied (publickey)</code> 是認證被拒，代表 sshd 有在跑、連線有到（這是進度），卡在金鑰這關。常見於你用的別名設了 <code>IdentitiesOnly yes</code> 只送某一把 key，而新機器的 <code>authorized_keys</code> 還沒有它。修法是改用帳號加 IP 直連、走密碼，繞過那個鎖死金鑰的別名：<code>ssh user@&lt;新 IP&gt;</code>，密碼是「這次安裝」為該使用者設的（每次重裝各自獨立，不是舊機器那個）。連進去後再把公鑰貼回新機器的 <code>authorized_keys</code>、把別名的 <code>HostName</code> 更新成新 IP，免密碼才會恢復。</p>
<p><code>Host key verification failed</code>（或 <code>REMOTE HOST IDENTIFICATION HAS CHANGED</code>）發生在新機器剛好拿到跟舊機器一樣的 IP 時：你本機 <code>known_hosts</code> 存的是舊機器的 host key，SSH 偵測到同一個 IP 換了 key、當成可能的中間人攻擊而拒連。修法是刪掉那筆舊紀錄，再重連時接受新 key：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -R &lt;IP&gt;       <span class="c1"># 刪掉該 IP 的舊 host key</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">ssh-keygen -R &lt;別名&gt;     <span class="c1"># 有用別名的話一併刪</span></span></span></code></pre></div><p><code>Connection refused</code> 代表沒有 sshd 在監聽，也就是新機器還沒把 SSH server 起來。修法回到最開始——在新機器的 console 裝 openssh、啟動服務（見本篇開頭「啟用 sshd」），這一步在每台全新機器上都要重做。</p>
<p>三個症狀的共同根因是同一件事：SSH 的便利設定（別名、金鑰、host key 快取）綁的是機器身分、不會跟著「重裝」自動轉移。把它們當成「為某一台機器設好的」，換機器就重做第一次連線，能省下對著看似無關的錯誤瞎猜的時間。</p>
<h2 id="連入後可能遇到的兩個終端機問題">連入後可能遇到的兩個終端機問題</h2>
<p>SSH 連線本身通了之後，互動 shell 還可能因為終端機環境不對而出現「打字變亂碼、prompt 重繪錯位」。這類問題在你用現代終端機（如 Ghostty、Kitty）連進一台剛裝好的最小 Linux、又跑了 unicode 較重的 prompt（如 Powerlevel10k）時最容易出現，根源是兩個跟字元處理有關的終端機設定，跟你的 shell 配置無關。</p>
<p>第一個是 locale。macOS 的終端機 SSH 連線時常把 <code>LC_CTYPE=UTF-8</code> 送到遠端，但 <code>UTF-8</code> 不是合法的 Linux locale 名稱，Linux 收到後 fallback 成 <code>POSIX</code>/C locale——於是 shell 的行編輯器把輸入當單位元組處理，配上 unicode 字元的 prompt 就重繪成一個字母重複好幾次的累加亂碼。判讀方式是在遠端跑 <code>locale</code>，看 <code>LANG</code> 是不是空的、<code>LC_CTYPE</code> 是不是 <code>POSIX</code>。修法是在 shell 設定裡強制一個合法的 UTF-8 locale（前提是該 locale 已生成，見 <a href="../install-option-decisions/">安裝選項判讀</a> 的 locale 段）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">export</span> <span class="nv">LANG</span><span class="o">=</span>en_US.UTF-8
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">export</span> <span class="nv">LC_CTYPE</span><span class="o">=</span>en_US.UTF-8</span></span></code></pre></div><p>第二個是 terminfo。現代終端機會把 <code>TERM</code> 設成自己的值（Ghostty 是 <code>xterm-ghostty</code>、Kitty 是 <code>xterm-kitty</code>），而一台剛裝好的 Linux 的 terminfo 資料庫沒有這些條目，shell 的行編輯器做「清行重繪」時找不到對應的控制序列、就把畫面畫壞。判讀方式是在遠端 <code>echo $TERM</code> 看是哪個值、<code>toe | grep &lt;值&gt;</code> 看遠端認不認得。修法有兩條：把你終端機的 terminfo 灌進遠端（保留完整功能），或退而求其次強制一個遠端一定有的 <code>TERM</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 把本機終端機的 terminfo 灌進遠端的 ~/.terminfo（推薦）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">infocmp -x <span class="s2">&#34;</span><span class="nv">$TERM</span><span class="s2">&#34;</span> <span class="p">|</span> ssh remote <span class="s1">&#39;tic -x -&#39;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 或：連線時強制遠端一定有的 TERM（功能略降，但保證能用）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ssh -t remote <span class="s1">&#39;TERM=xterm-256color exec zsh -l&#39;</span></span></span></code></pre></div><p>這兩個問題的共同點是：它們在你裝了 unicode 較重的互動 shell 之後才浮現，而陽春的 shell（ASCII prompt）即使 locale 跟 terminfo 都不對也照樣能用。所以排查時，先確認是不是這層、而不是去懷疑剛裝的 shell 配置壞了。</p>
<h2 id="連入傳輸安裝的順序">連入、傳輸、安裝的順序</h2>
<p>這三件事有一個固定的先後，順序錯了會在中間卡住。先把 sshd 跑起來、從本機連入，取得一個能貼上、可捲動的 session；再把 dotfile 弄進機器（公開 repo 走 HTTPS clone、私有或本地走傳輸）；最後在機器上跑 install.sh 完成安裝。SSH key 是讓「連入」從每次打密碼變成免密碼的優化，可以在任何時候補，不是這條鏈的必要環節、也不是 bootstrap 的前置。</p>
<p><a href="/blog/linux/dotfile/00-dotfile-mindset/setup-order-guide/" data-link-title="環境建置的操作順序" data-link-desc="第一次從零建立 Linux 或 macOS 開發環境、不確定先做什麼後做什麼時讀 — 依賴順序路線圖，每一步附對應模組連結">模組零的操作順序指引</a> 把「生成 SSH key、部署公鑰」列為標準流程的一環，那是預設你會建 key 的主路徑。這篇補的是它沒展開的另一面：當你手上還沒有 key、或這台機器的 dotfile 根本不需要 key 就能取得時，怎麼一樣把 bootstrap 跑完。</p>
<h2 id="下一步">下一步</h2>
<p>連入、傳輸、安裝都跑通之後，真正的考驗是當 install.sh 中途失敗時——而它遲早會撞到失敗——你能不能快速看出哪裡錯了。這取決於安裝腳本有沒有把可觀測性內建進去，<a href="../observable-bootstrap/">可除錯的 bootstrap</a> 談的就是怎麼內建。</p>
]]></content:encoded></item><item><title>讓機器跑無人值守的長任務</title><link>https://tarrragon.github.io/blog/linux/install/unattended-remote-work/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/install/unattended-remote-work/</guid><description>&lt;p>一台機器能被連入、能跑 bootstrap（把它從空機器設定成可用環境的安裝流程）之後，下一個層次是讓它在你不盯著的時候自己跑完一個長任務——一次耗時的編譯、一個批次作業、一個無人值守的 agent。能不能放著走人，取決於有沒有把三件會中斷無人值守執行的事先解決掉：互動提示、斷線即死、結果出不去。這三件是「讓任務能在無人時順利啟動並交付」的障礙；任務跑起來之後的資源耗盡、OOM、額度或憑證到期是另一條軸（執行期的持久性），最後一段會接到那裡。這篇逐一拆解這三個障礙與對應的解法，並說明它們共同的代價判讀——這些便利大多拿安全性換自主性，該不該開要看這台機器的爆炸半徑。&lt;/p>
&lt;p>底下用一個具體情境當例子：在一台用完即丟的測試 VM 上，讓 Claude Code 這類 agent 自己跑完一段工作、把成果推回 GitHub 給你早上 review。同一組障礙換成 overnight 編譯或 cron 批次也成立。&lt;/p>
&lt;h2 id="障礙一互動提示擋住自動執行">障礙一：互動提示擋住自動執行&lt;/h2>
&lt;p>無人值守的程序沒有人在鍵盤前，所以任何「停下來等你輸入」的提示都會讓它卡死，其中最常見的是 sudo 密碼。一個要裝套件、改系統設定的任務，跑到 &lt;code>sudo&lt;/code> 那行就停在密碼提示、永遠等不到輸入，整個任務卡在那裡直到你回來。&lt;/p>
&lt;p>解法是讓這台機器的 sudo 免密碼（NOPASSWD），但這是一個明確的安全取捨、不是預設該開的東西。設定方式是給 sudoers 加一條 NOPASSWD 規則：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>whoami&lt;span class="k">)&lt;/span>&lt;span class="s2"> ALL=(ALL:ALL) NOPASSWD: ALL&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> sudo tee /etc/sudoers.d/20-nopasswd &lt;span class="c1"># $(whoami) 會填入你的登入帳號&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">sudo chmod &lt;span class="m">440&lt;/span> /etc/sudoers.d/20-nopasswd&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>開了 NOPASSWD，等於放棄「sudo 密碼」這道在你被入侵或程序失控時的最後防線。判讀軸是這台機器的爆炸半徑——它持有哪些憑證、能觸及哪些系統，也就是最壞情況下會波及多大範圍。一台範圍受限、沒有任何真實憑證、出事就重建的測試 VM，放棄這道防線換取自動執行是划算的；一台共享主機、生產伺服器、或裝著真實憑證與資料的機器，不該為了方便開 NOPASSWD。關鍵是「可不可丟」不等於「爆炸半徑小」：一台用完即丟的 VM，一旦塞進能碰到生產系統或你帳號的憑證，爆炸半徑就不小了——看的不是機器本身，是它最壞情況能波及什麼。&lt;/p>
&lt;h2 id="障礙二ssh-斷線就把任務一起殺掉">障礙二：SSH 斷線就把任務一起殺掉&lt;/h2>
&lt;p>直接在 SSH session 裡跑的程序，會隨著 SSH 連線中斷而一起死掉——你闔上筆電、網路斷一下、或單純關掉終端機，正在跑的任務就沒了。對一個要跑好幾小時的無人值守任務，這條等於「你不能離開」，跟無人值守的目的矛盾。&lt;/p>
&lt;p>把任務搬進終端機多工器（zellij、tmux 這類，配置見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/03-terminal-ecosystem/multiplexer-tmux-zellij/" data-link-title="Multiplexer：tmux vs zellij" data-link-desc="在終端機裡切分 pane、管理多個 session、SSH 斷線後保持工作時回來讀 — tmux 和 zellij 的配置與選型">模組三&lt;/a>）就解決了。多工器的 session 活在那台機器上、獨立於你的 SSH 連線：你在多工器裡啟動任務、然後 detach（卸離），任務繼續在機器上跑，你這頭關掉 SSH 都不影響；之後再連回來 attach（接回）就能看它跑到哪。典型流程是連入機器、起多工器、在裡面啟動任務、detach、走人：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh user@host
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">zellij &lt;span class="c1"># 起多工器（tmux 同理）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">./run-my-long-task.sh &lt;span class="c1"># 在裡面啟動你的長任務（換成你的實際指令）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 然後 detach：zellij 預設 Ctrl+o 再按 d（tmux 是 Ctrl+b 再按 d）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># 此時關掉 SSH 不影響任務，它在 host 上繼續跑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 之後連回來看進度：再 ssh 進去，然後&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">zellij attach &lt;span class="c1"># tmux 是 tmux attach&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>判讀訊號是「這個任務跑完前，我會不會斷線」。只要會（過夜、跨小時、不穩的網路），就把它放進多工器；幾秒鐘就結束的指令不需要這層。&lt;/p>
&lt;h2 id="障礙三成果推不出去等於沒做">障礙三：成果推不出去，等於沒做&lt;/h2>
&lt;p>無人值守任務的產出留在那台機器上，你看不到——除非它能把結果送出去。最常見的形式是把改動 commit 後 push 回 git 遠端，你在別處 pull 來看。但 push 需要認證，而一台剛連入的機器通常還沒設好推送的憑證，於是任務做完了、commit 也建了，卻卡在 push 那步推不出去，你隔天連回來才發現結果根本沒送出去。&lt;/p>
&lt;p>先在這台機器上設好推送認證，這個障礙就消失。用 GitHub CLI 是直接的一條路，它認證後會一併把 git 的 credential helper（git 用來自動帶出認證、不必每次手打的機制）設好，後續 &lt;code>git push&lt;/code> 就能用——但 &lt;code>gh auth login&lt;/code> 本身是互動式的、要你在場完成一次，屬於離開前的人工前置：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">gh auth login &lt;span class="c1"># 選 HTTPS、完成認證、同意設定 git 認證&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>判讀軸是「這個任務的價值要怎麼回到你手上」。如果你打算從遠端（GitHub）看結果，那 push 認證就是必要前置——沒設好，整段工作就被困在機器裡。連帶的紀律是讓任務頻繁 commit 當檢查點、做完務必確認 push 成功：對一個你不在場的任務，「沒推出去」跟「沒做」對你是一樣的。機器若沒裝 &lt;code>gh&lt;/code>，也可以用 PAT 走 HTTPS，見 &lt;a href="../ssh-keyless-bootstrap/">外部連入篇&lt;/a> 的私有 repo 段。&lt;/p>
&lt;p>把 push 憑證設進這台機器，等於提高了它的爆炸半徑——它現在能動你的 repo 了。這會回頭讓障礙一的 NOPASSWD、以及下面 agent 段的權限放行更該謹慎：最壞情況從「弄壞這台機器」升級成「污染你的 repo」，而後者不是重建一台 VM 就能還原的。所以設了 push 憑證之後，要連帶重估前面那些「因為機器可丟所以放心」的取捨。&lt;/p>
&lt;h2 id="額外一層宿主暫停會連帶停掉任務">額外一層：宿主暫停會連帶停掉任務&lt;/h2>
&lt;p>當這台機器是跑在某個宿主上的虛擬機，還有一個容易忽略的中斷源：宿主睡著，VM 跟著暫停，裡面的無人值守任務也一起停。你以為它整夜在跑，回來發現它從你離開那刻就凍在那裡。判讀方式是想一下「這台機器的存在依賴什麼」——VM 依賴宿主醒著、雲端主機依賴帳單沒欠費。對 VM 的情況，離開前確保宿主不會自動睡眠（macOS 用 &lt;code>caffeinate&lt;/code>、Linux 宿主用 &lt;code>systemd-inhibit&lt;/code> 或停用 suspend、Windows 調電源設定，或直接關掉節能的自動睡眠）。&lt;/p>
&lt;h2 id="如果無人值守的工作者是-ai-agent">如果無人值守的工作者是 AI agent&lt;/h2>
&lt;p>當你放著跑的是一個 AI agent，除了上面三個障礙，還多一個它自己的互動提示要處理：agent 預設會在每個有風險的動作前停下來問你確認，而無人值守時沒人回答，它就卡住。對應的是 agent 的「跳過確認」模式（如 Claude Code 的權限放行旗標），讓它不停下來問。這跟 NOPASSWD 是同一類取捨、判讀軸也一樣：放給一個無人盯著的 agent 在一台範圍受限、用完即丟的機器上自主動作是可接受的；在一台有真實資料或共享的機器上不該這樣。降低風險的兩個做法是把 agent 的工作範圍用清楚的指引限定（只動哪些目錄、別碰系統其他地方），以及讓它在分支上做、產出交給你 review，而不是直接動到你會依賴的東西。&lt;/p></description><content:encoded><![CDATA[<p>一台機器能被連入、能跑 bootstrap（把它從空機器設定成可用環境的安裝流程）之後，下一個層次是讓它在你不盯著的時候自己跑完一個長任務——一次耗時的編譯、一個批次作業、一個無人值守的 agent。能不能放著走人，取決於有沒有把三件會中斷無人值守執行的事先解決掉：互動提示、斷線即死、結果出不去。這三件是「讓任務能在無人時順利啟動並交付」的障礙；任務跑起來之後的資源耗盡、OOM、額度或憑證到期是另一條軸（執行期的持久性），最後一段會接到那裡。這篇逐一拆解這三個障礙與對應的解法，並說明它們共同的代價判讀——這些便利大多拿安全性換自主性，該不該開要看這台機器的爆炸半徑。</p>
<p>底下用一個具體情境當例子：在一台用完即丟的測試 VM 上，讓 Claude Code 這類 agent 自己跑完一段工作、把成果推回 GitHub 給你早上 review。同一組障礙換成 overnight 編譯或 cron 批次也成立。</p>
<h2 id="障礙一互動提示擋住自動執行">障礙一：互動提示擋住自動執行</h2>
<p>無人值守的程序沒有人在鍵盤前，所以任何「停下來等你輸入」的提示都會讓它卡死，其中最常見的是 sudo 密碼。一個要裝套件、改系統設定的任務，跑到 <code>sudo</code> 那行就停在密碼提示、永遠等不到輸入，整個任務卡在那裡直到你回來。</p>
<p>解法是讓這台機器的 sudo 免密碼（NOPASSWD），但這是一個明確的安全取捨、不是預設該開的東西。設定方式是給 sudoers 加一條 NOPASSWD 規則：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="k">$(</span>whoami<span class="k">)</span><span class="s2"> ALL=(ALL:ALL) NOPASSWD: ALL&#34;</span> <span class="p">|</span> sudo tee /etc/sudoers.d/20-nopasswd  <span class="c1"># $(whoami) 會填入你的登入帳號</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo chmod <span class="m">440</span> /etc/sudoers.d/20-nopasswd</span></span></code></pre></div><p>開了 NOPASSWD，等於放棄「sudo 密碼」這道在你被入侵或程序失控時的最後防線。判讀軸是這台機器的爆炸半徑——它持有哪些憑證、能觸及哪些系統，也就是最壞情況下會波及多大範圍。一台範圍受限、沒有任何真實憑證、出事就重建的測試 VM，放棄這道防線換取自動執行是划算的；一台共享主機、生產伺服器、或裝著真實憑證與資料的機器，不該為了方便開 NOPASSWD。關鍵是「可不可丟」不等於「爆炸半徑小」：一台用完即丟的 VM，一旦塞進能碰到生產系統或你帳號的憑證，爆炸半徑就不小了——看的不是機器本身，是它最壞情況能波及什麼。</p>
<h2 id="障礙二ssh-斷線就把任務一起殺掉">障礙二：SSH 斷線就把任務一起殺掉</h2>
<p>直接在 SSH session 裡跑的程序，會隨著 SSH 連線中斷而一起死掉——你闔上筆電、網路斷一下、或單純關掉終端機，正在跑的任務就沒了。對一個要跑好幾小時的無人值守任務，這條等於「你不能離開」，跟無人值守的目的矛盾。</p>
<p>把任務搬進終端機多工器（zellij、tmux 這類，配置見 <a href="/blog/linux/dotfile/03-terminal-ecosystem/multiplexer-tmux-zellij/" data-link-title="Multiplexer：tmux vs zellij" data-link-desc="在終端機裡切分 pane、管理多個 session、SSH 斷線後保持工作時回來讀 — tmux 和 zellij 的配置與選型">模組三</a>）就解決了。多工器的 session 活在那台機器上、獨立於你的 SSH 連線：你在多工器裡啟動任務、然後 detach（卸離），任務繼續在機器上跑，你這頭關掉 SSH 都不影響；之後再連回來 attach（接回）就能看它跑到哪。典型流程是連入機器、起多工器、在裡面啟動任務、detach、走人：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh user@host
</span></span><span class="line"><span class="ln">2</span><span class="cl">zellij                       <span class="c1"># 起多工器（tmux 同理）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">./run-my-long-task.sh        <span class="c1"># 在裡面啟動你的長任務（換成你的實際指令）</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 然後 detach：zellij 預設 Ctrl+o 再按 d（tmux 是 Ctrl+b 再按 d）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 此時關掉 SSH 不影響任務，它在 host 上繼續跑</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 之後連回來看進度：再 ssh 進去，然後</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">zellij attach                <span class="c1"># tmux 是 tmux attach</span></span></span></code></pre></div><p>判讀訊號是「這個任務跑完前，我會不會斷線」。只要會（過夜、跨小時、不穩的網路），就把它放進多工器；幾秒鐘就結束的指令不需要這層。</p>
<h2 id="障礙三成果推不出去等於沒做">障礙三：成果推不出去，等於沒做</h2>
<p>無人值守任務的產出留在那台機器上，你看不到——除非它能把結果送出去。最常見的形式是把改動 commit 後 push 回 git 遠端，你在別處 pull 來看。但 push 需要認證，而一台剛連入的機器通常還沒設好推送的憑證，於是任務做完了、commit 也建了，卻卡在 push 那步推不出去，你隔天連回來才發現結果根本沒送出去。</p>
<p>先在這台機器上設好推送認證，這個障礙就消失。用 GitHub CLI 是直接的一條路，它認證後會一併把 git 的 credential helper（git 用來自動帶出認證、不必每次手打的機制）設好，後續 <code>git push</code> 就能用——但 <code>gh auth login</code> 本身是互動式的、要你在場完成一次，屬於離開前的人工前置：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">gh auth login    <span class="c1"># 選 HTTPS、完成認證、同意設定 git 認證</span></span></span></code></pre></div><p>判讀軸是「這個任務的價值要怎麼回到你手上」。如果你打算從遠端（GitHub）看結果，那 push 認證就是必要前置——沒設好，整段工作就被困在機器裡。連帶的紀律是讓任務頻繁 commit 當檢查點、做完務必確認 push 成功：對一個你不在場的任務，「沒推出去」跟「沒做」對你是一樣的。機器若沒裝 <code>gh</code>，也可以用 PAT 走 HTTPS，見 <a href="../ssh-keyless-bootstrap/">外部連入篇</a> 的私有 repo 段。</p>
<p>把 push 憑證設進這台機器，等於提高了它的爆炸半徑——它現在能動你的 repo 了。這會回頭讓障礙一的 NOPASSWD、以及下面 agent 段的權限放行更該謹慎：最壞情況從「弄壞這台機器」升級成「污染你的 repo」，而後者不是重建一台 VM 就能還原的。所以設了 push 憑證之後，要連帶重估前面那些「因為機器可丟所以放心」的取捨。</p>
<h2 id="額外一層宿主暫停會連帶停掉任務">額外一層：宿主暫停會連帶停掉任務</h2>
<p>當這台機器是跑在某個宿主上的虛擬機，還有一個容易忽略的中斷源：宿主睡著，VM 跟著暫停，裡面的無人值守任務也一起停。你以為它整夜在跑，回來發現它從你離開那刻就凍在那裡。判讀方式是想一下「這台機器的存在依賴什麼」——VM 依賴宿主醒著、雲端主機依賴帳單沒欠費。對 VM 的情況，離開前確保宿主不會自動睡眠（macOS 用 <code>caffeinate</code>、Linux 宿主用 <code>systemd-inhibit</code> 或停用 suspend、Windows 調電源設定，或直接關掉節能的自動睡眠）。</p>
<h2 id="如果無人值守的工作者是-ai-agent">如果無人值守的工作者是 AI agent</h2>
<p>當你放著跑的是一個 AI agent，除了上面三個障礙，還多一個它自己的互動提示要處理：agent 預設會在每個有風險的動作前停下來問你確認，而無人值守時沒人回答，它就卡住。對應的是 agent 的「跳過確認」模式（如 Claude Code 的權限放行旗標），讓它不停下來問。這跟 NOPASSWD 是同一類取捨、判讀軸也一樣：放給一個無人盯著的 agent 在一台範圍受限、用完即丟的機器上自主動作是可接受的；在一台有真實資料或共享的機器上不該這樣。降低風險的兩個做法是把 agent 的工作範圍用清楚的指引限定（只動哪些目錄、別碰系統其他地方），以及讓它在分支上做、產出交給你 review，而不是直接動到你會依賴的東西。</p>
<h2 id="下一步">下一步</h2>
<p>把這三到四個障礙解決掉，一台機器就能在你離開後自己跑完工作、把成果送回你手上。這篇是 <a href="../ssh-keyless-bootstrap/">外部連入</a>（怎麼連進去）的延伸——從「我連進去手動操作」進到「我設好讓它自己跑」。而要讓那個無人值守的任務在失敗時還留得下可診斷的痕跡，回到 <a href="../observable-bootstrap/">可除錯的 bootstrap</a> 的原則：無人盯著的任務尤其需要把可觀測性內建進去，因為你不在場、只能事後從 log 重建發生了什麼。</p>
]]></content:encoded></item><item><title>SSH Key 設定筆記（macOS / Linux / Windows）</title><link>https://tarrragon.github.io/blog/work-log/ssh-key-%E8%A8%AD%E5%AE%9A%E7%AD%86%E8%A8%98macos-/-linux-/-windows/</link><pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/work-log/ssh-key-%E8%A8%AD%E5%AE%9A%E7%AD%86%E8%A8%98macos-/-linux-/-windows/</guid><description>&lt;h2 id="0-產生金鑰如果還沒有的話">0. 產生金鑰（如果還沒有的話）&lt;/h2>
&lt;p>目前推薦使用 &lt;strong>Ed25519&lt;/strong> 演算法，相比 RSA 更安全、金鑰更短、驗證速度更快：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t ed25519 -C &lt;span class="s2">&amp;#34;your_email@example.com&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>若需相容較舊的系統（不支援 Ed25519），可退而使用 RSA-4096：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh-keygen -t rsa -b &lt;span class="m">4096&lt;/span> -C &lt;span class="s2">&amp;#34;your_email@example.com&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;p>產生時會提示設定 &lt;strong>passphrase（密碼短語）&lt;/strong>，強烈建議設定。即使私鑰外洩，攻擊者仍需要密碼才能使用。&lt;/p>
&lt;hr>
&lt;h2 id="1-寫入金鑰檔案">1. 寫入金鑰檔案&lt;/h2>
&lt;h3 id="macos--linux">macOS / Linux&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">cat &amp;gt; ~/.ssh/&amp;lt;key_name&amp;gt; &lt;span class="s">&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="s">（貼上金鑰內容）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="s">EOF&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;code>'EOF'&lt;/code> 加單引號 → 防止 shell 解析內容中的特殊字元（如 &lt;code>$&lt;/code>、&lt;code>`&lt;/code>）&lt;/p>&lt;/blockquote>
&lt;h3 id="windowspowershell">Windows（PowerShell）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">New-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh&amp;#34;&lt;/span> &lt;span class="n">-ItemType&lt;/span> &lt;span class="n">Directory&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">Set-Content&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh\&amp;lt;key_name&amp;gt;&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="sh">@&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="sh">（貼上金鑰內容）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="sh">&amp;#34;@&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 的 SSH 金鑰預設路徑為 &lt;code>C:\Users\&amp;lt;使用者&amp;gt;\.ssh\&lt;/code>&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="2-設定權限">2. 設定權限&lt;/h2>
&lt;h3 id="macos--linux-1">macOS / Linux&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">chmod &lt;span class="m">600&lt;/span> ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;code>chmod 600&lt;/code> → 僅擁有者可讀寫，SSH 要求私鑰權限不可過於開放，否則會拒絕使用。&lt;/p>&lt;/blockquote>
&lt;h3 id="windowspowershell以系統管理員執行">Windows（PowerShell，以系統管理員執行）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">icacls&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh\&amp;lt;key_name&amp;gt;&amp;#34;&lt;/span> &lt;span class="p">/&lt;/span>&lt;span class="n">inheritance&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nb">r &lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">grant&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nb">r &lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">$(&lt;/span>&lt;span class="nv">$env:USERNAME&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="s2">:(R)&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 需透過 &lt;code>icacls&lt;/code> 移除繼承權限，並限制為只有當前使用者可讀取。
若權限過於開放，OpenSSH 同樣會拒絕載入金鑰。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="3-加入-ssh-agent">3. 加入 SSH Agent&lt;/h2>
&lt;h3 id="macos">macOS&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 啟動 agent（通常 macOS 已自動啟動）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>ssh-agent -s&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加入金鑰，並存入 Keychain 避免重開機後失效&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">ssh-add --apple-use-keychain ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>若要讓金鑰在每次登入時自動載入，可在 &lt;code>~/.ssh/config&lt;/code> 中加入：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Host *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> AddKeysToAgent yes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> UseKeychain yes
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> IdentityFile ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="linux">Linux&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 啟動 agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>ssh-agent -s&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 加入金鑰&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">ssh-add ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Linux 重開機後 agent 會重置。可將 &lt;code>eval &amp;quot;$(ssh-agent -s)&amp;quot;&lt;/code> 加入 &lt;code>~/.bashrc&lt;/code> 或 &lt;code>~/.zshrc&lt;/code> 讓它自動啟動。&lt;/p>&lt;/blockquote>
&lt;h3 id="windowspowershell以系統管理員執行-1">Windows（PowerShell，以系統管理員執行）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># 啟用 ssh-agent 服務（預設為停用）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">Get-Service&lt;/span> &lt;span class="nb">ssh-agent&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Set-Service&lt;/span> &lt;span class="n">-StartupType&lt;/span> &lt;span class="n">Automatic&lt;/span> &lt;span class="n">-PassThru&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Start-Service&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c"># 加入金鑰&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nb">ssh-add&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$env:USERPROFILE&lt;/span>&lt;span class="s2">\.ssh\&amp;lt;key_name&amp;gt;&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 的 &lt;code>ssh-agent&lt;/code> 是系統服務，啟用後重開機也會自動執行，不需額外設定。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="4-測試連線">4. 測試連線&lt;/h2>
&lt;p>三個平台指令相同：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">ssh -i ~/.ssh/&amp;lt;key_name&amp;gt; -T git@&amp;lt;host&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>Windows 請在 PowerShell 或 Git Bash 中執行，路徑會自動對應到 &lt;code>$env:USERPROFILE\.ssh\&lt;/code>。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="備註">備註&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>macOS&lt;/th>
 &lt;th>Linux&lt;/th>
 &lt;th>Windows&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>金鑰路徑&lt;/td>
 &lt;td>&lt;code>~/.ssh/&lt;/code>&lt;/td>
 &lt;td>&lt;code>~/.ssh/&lt;/code>&lt;/td>
 &lt;td>&lt;code>C:\Users\&amp;lt;使用者&amp;gt;\.ssh\&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>權限設定&lt;/td>
 &lt;td>&lt;code>chmod 600&lt;/code>&lt;/td>
 &lt;td>&lt;code>chmod 600&lt;/code>&lt;/td>
 &lt;td>&lt;code>icacls&lt;/code> 移除繼承&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Agent 持久化&lt;/td>
 &lt;td>Keychain&lt;/td>
 &lt;td>需加入 shell rc&lt;/td>
 &lt;td>系統服務，自動持久&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>預裝 SSH&lt;/td>
 &lt;td>是&lt;/td>
 &lt;td>大多數發行版已預裝&lt;/td>
 &lt;td>Windows 10 1809+ 內建 OpenSSH&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="安全性建議">安全性建議&lt;/h2>
&lt;h3 id="優先使用-ed25519">優先使用 Ed25519&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>演算法&lt;/th>
 &lt;th>金鑰長度&lt;/th>
 &lt;th>安全性&lt;/th>
 &lt;th>效能&lt;/th>
 &lt;th>相容性&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>Ed25519&lt;/strong>&lt;/td>
 &lt;td>256 bit&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>最快&lt;/td>
 &lt;td>2014 年後的 OpenSSH 6.5+&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>RSA-4096&lt;/td>
 &lt;td>4096 bit&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>較慢&lt;/td>
 &lt;td>最廣泛，適合舊系統&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ECDSA&lt;/td>
 &lt;td>256/384/521 bit&lt;/td>
 &lt;td>中高&lt;/td>
 &lt;td>快&lt;/td>
 &lt;td>已被 Ed25519 取代&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DSA&lt;/td>
 &lt;td>1024 bit&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>-&lt;/td>
 &lt;td>已棄用，OpenSSH 7.0+ 預設停用&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="為私鑰設定-passphrase">為私鑰設定 Passphrase&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 為已存在的金鑰補設或更換 passphrase&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">ssh-keygen -p -f ~/.ssh/&amp;lt;key_name&amp;gt;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>即使私鑰檔案被他人取得，沒有 passphrase 就無法使用。搭配 SSH Agent 後只需輸入一次，不影響日常使用體驗。&lt;/p></description><content:encoded><![CDATA[<h2 id="0-產生金鑰如果還沒有的話">0. 產生金鑰（如果還沒有的話）</h2>
<p>目前推薦使用 <strong>Ed25519</strong> 演算法，相比 RSA 更安全、金鑰更短、驗證速度更快：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t ed25519 -C <span class="s2">&#34;your_email@example.com&#34;</span></span></span></code></pre></div><blockquote>
<p>若需相容較舊的系統（不支援 Ed25519），可退而使用 RSA-4096：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh-keygen -t rsa -b <span class="m">4096</span> -C <span class="s2">&#34;your_email@example.com&#34;</span></span></span></code></pre></div></blockquote>
<p>產生時會提示設定 <strong>passphrase（密碼短語）</strong>，強烈建議設定。即使私鑰外洩，攻擊者仍需要密碼才能使用。</p>
<hr>
<h2 id="1-寫入金鑰檔案">1. 寫入金鑰檔案</h2>
<h3 id="macos--linux">macOS / Linux</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cat &gt; ~/.ssh/&lt;key_name&gt; <span class="s">&lt;&lt; &#39;EOF&#39;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s">（貼上金鑰內容）
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="s">EOF</span></span></span></code></pre></div><blockquote>
<p><code>'EOF'</code> 加單引號 → 防止 shell 解析內容中的特殊字元（如 <code>$</code>、<code>`</code>）</p></blockquote>
<h3 id="windowspowershell">Windows（PowerShell）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">New-Item</span> <span class="n">-Path</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh&#34;</span> <span class="n">-ItemType</span> <span class="n">Directory</span> <span class="n">-Force</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">Set-Content</span> <span class="n">-Path</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh\&lt;key_name&gt;&#34;</span> <span class="n">-Value</span> <span class="sh">@&#34;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="sh">（貼上金鑰內容）
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="sh">&#34;@</span></span></span></code></pre></div><blockquote>
<p>Windows 的 SSH 金鑰預設路徑為 <code>C:\Users\&lt;使用者&gt;\.ssh\</code></p></blockquote>
<hr>
<h2 id="2-設定權限">2. 設定權限</h2>
<h3 id="macos--linux-1">macOS / Linux</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">chmod <span class="m">600</span> ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><blockquote>
<p><code>chmod 600</code> → 僅擁有者可讀寫，SSH 要求私鑰權限不可過於開放，否則會拒絕使用。</p></blockquote>
<h3 id="windowspowershell以系統管理員執行">Windows（PowerShell，以系統管理員執行）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">icacls</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh\&lt;key_name&gt;&#34;</span> <span class="p">/</span><span class="n">inheritance</span><span class="err">:</span><span class="nb">r </span><span class="p">/</span><span class="n">grant</span><span class="err">:</span><span class="nb">r </span><span class="s2">&#34;</span><span class="p">$(</span><span class="nv">$env:USERNAME</span><span class="p">)</span><span class="s2">:(R)&#34;</span></span></span></code></pre></div><blockquote>
<p>Windows 需透過 <code>icacls</code> 移除繼承權限，並限制為只有當前使用者可讀取。
若權限過於開放，OpenSSH 同樣會拒絕載入金鑰。</p></blockquote>
<hr>
<h2 id="3-加入-ssh-agent">3. 加入 SSH Agent</h2>
<h3 id="macos">macOS</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 啟動 agent（通常 macOS 已自動啟動）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 加入金鑰，並存入 Keychain 避免重開機後失效</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ssh-add --apple-use-keychain ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><p>若要讓金鑰在每次登入時自動載入，可在 <code>~/.ssh/config</code> 中加入：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Host *
</span></span><span class="line"><span class="ln">2</span><span class="cl">  AddKeysToAgent yes
</span></span><span class="line"><span class="ln">3</span><span class="cl">  UseKeychain yes
</span></span><span class="line"><span class="ln">4</span><span class="cl">  IdentityFile ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><h3 id="linux">Linux</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 啟動 agent</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 加入金鑰</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ssh-add ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><blockquote>
<p>Linux 重開機後 agent 會重置。可將 <code>eval &quot;$(ssh-agent -s)&quot;</code> 加入 <code>~/.bashrc</code> 或 <code>~/.zshrc</code> 讓它自動啟動。</p></blockquote>
<h3 id="windowspowershell以系統管理員執行-1">Windows（PowerShell，以系統管理員執行）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># 啟用 ssh-agent 服務（預設為停用）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">Get-Service</span> <span class="nb">ssh-agent</span> <span class="p">|</span> <span class="nb">Set-Service</span> <span class="n">-StartupType</span> <span class="n">Automatic</span> <span class="n">-PassThru</span> <span class="p">|</span> <span class="nb">Start-Service</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c"># 加入金鑰</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">ssh-add</span> <span class="s2">&#34;</span><span class="nv">$env:USERPROFILE</span><span class="s2">\.ssh\&lt;key_name&gt;&#34;</span></span></span></code></pre></div><blockquote>
<p>Windows 的 <code>ssh-agent</code> 是系統服務，啟用後重開機也會自動執行，不需額外設定。</p></blockquote>
<hr>
<h2 id="4-測試連線">4. 測試連線</h2>
<p>三個平台指令相同：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh -i ~/.ssh/&lt;key_name&gt; -T git@&lt;host&gt;</span></span></code></pre></div><blockquote>
<p>Windows 請在 PowerShell 或 Git Bash 中執行，路徑會自動對應到 <code>$env:USERPROFILE\.ssh\</code>。</p></blockquote>
<hr>
<h2 id="備註">備註</h2>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>macOS</th>
          <th>Linux</th>
          <th>Windows</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>金鑰路徑</td>
          <td><code>~/.ssh/</code></td>
          <td><code>~/.ssh/</code></td>
          <td><code>C:\Users\&lt;使用者&gt;\.ssh\</code></td>
      </tr>
      <tr>
          <td>權限設定</td>
          <td><code>chmod 600</code></td>
          <td><code>chmod 600</code></td>
          <td><code>icacls</code> 移除繼承</td>
      </tr>
      <tr>
          <td>Agent 持久化</td>
          <td>Keychain</td>
          <td>需加入 shell rc</td>
          <td>系統服務，自動持久</td>
      </tr>
      <tr>
          <td>預裝 SSH</td>
          <td>是</td>
          <td>大多數發行版已預裝</td>
          <td>Windows 10 1809+ 內建 OpenSSH</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="安全性建議">安全性建議</h2>
<h3 id="優先使用-ed25519">優先使用 Ed25519</h3>
<table>
  <thead>
      <tr>
          <th>演算法</th>
          <th>金鑰長度</th>
          <th>安全性</th>
          <th>效能</th>
          <th>相容性</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Ed25519</strong></td>
          <td>256 bit</td>
          <td>高</td>
          <td>最快</td>
          <td>2014 年後的 OpenSSH 6.5+</td>
      </tr>
      <tr>
          <td>RSA-4096</td>
          <td>4096 bit</td>
          <td>高</td>
          <td>較慢</td>
          <td>最廣泛，適合舊系統</td>
      </tr>
      <tr>
          <td>ECDSA</td>
          <td>256/384/521 bit</td>
          <td>中高</td>
          <td>快</td>
          <td>已被 Ed25519 取代</td>
      </tr>
      <tr>
          <td>DSA</td>
          <td>1024 bit</td>
          <td>低</td>
          <td>-</td>
          <td>已棄用，OpenSSH 7.0+ 預設停用</td>
      </tr>
  </tbody>
</table>
<h3 id="為私鑰設定-passphrase">為私鑰設定 Passphrase</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 為已存在的金鑰補設或更換 passphrase</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">ssh-keygen -p -f ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><p>即使私鑰檔案被他人取得，沒有 passphrase 就無法使用。搭配 SSH Agent 後只需輸入一次，不影響日常使用體驗。</p>
<h3 id="定期輪換金鑰">定期輪換金鑰</h3>
<p>建議每 1-2 年輪換一次 SSH 金鑰。可在金鑰名稱中加入年份作為提醒，例如 <code>id_ed25519_2026</code>。</p>
<h3 id="其他注意事項">其他注意事項</h3>
<ul>
<li><strong>不要將私鑰上傳到雲端同步服務</strong>（如 iCloud、Google Drive、OneDrive），除非經過加密</li>
<li><strong>不要在多台機器之間複製同一把私鑰</strong>，應為每台機器各自產生獨立金鑰</li>
<li><strong>避免使用已棄用的 DSA 金鑰</strong>，部分新版 OpenSSH 已預設拒絕 DSA</li>
<li><strong><code>~/.ssh/</code> 目錄本身權限應為 <code>700</code></strong>，<code>authorized_keys</code> 應為 <code>600</code></li>
</ul>
<hr>
<h2 id="常見問題排查">常見問題排查</h2>
<h3 id="permission-denied-publickey">Permission denied (publickey)</h3>
<ol>
<li>確認私鑰權限：<code>chmod 600 ~/.ssh/&lt;key_name&gt;</code></li>
<li>確認 <code>~/.ssh/</code> 目錄權限：<code>chmod 700 ~/.ssh</code></li>
<li>確認上傳的是 <strong>公鑰</strong>（<code>.pub</code>）而非私鑰</li>
<li>使用 verbose 模式查看詳細錯誤：<code>ssh -vvv user@host</code></li>
</ol>
<h3 id="agent-中沒有金鑰">Agent 中沒有金鑰</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 確認 agent 是否正在運行</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">ssh-add -l
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 如果顯示 &#34;Could not open a connection to your authentication agent&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>ssh-agent -s<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">ssh-add ~/.ssh/&lt;key_name&gt;</span></span></code></pre></div><h3 id="金鑰類型被伺服器拒絕">金鑰類型被伺服器拒絕</h3>
<p>部分較新的伺服器已停用 DSA 和較短的 RSA 金鑰。檢查方式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">ssh -vvv user@host 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep <span class="s2">&#34;Offering&#34;</span></span></span></code></pre></div><p>如果你的金鑰類型不在伺服器接受的清單中，需要重新產生 Ed25519 金鑰。</p>
]]></content:encoded></item></channel></rss>