SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

Rocky Linux 與 AlmaLinux 修改 SSH 連接埠教學

在 RHEL 系列發行版修改 SSH 埠需設定 firewalld、SELinux 與 sshd_config。本教學提供正確操作順序,避免因 SELinux 權限錯誤導致 sshd 無法啟動或被鎖在伺服器外。

為何在此處變更 SSH 埠需要三個步驟

若要在 Rocky Linux、AlmaLinux、CentOS Stream 或 Fedora 上變更 SSH 埠,僅進行一次編輯是不夠的。三個獨立的系統各自決定了新埠上的連線是否能運作。firewalld 決定封包是否能抵達機器。SELinux 決定 sshd 是否被允許綁定該埠號。sshd_config 則決定 daemon 請求使用哪個埠。若遺漏 SELinux 步驟,daemon 將拒絕啟動。若遺漏 firewalld 步驟,服務雖會啟動並監聽,但外部將無法連線。

在 Ubuntu 上,同樣的工作只需編輯一次並重啟即可,因為 Ubuntu 使用 AppArmor 而非 SELinux,且預設未提供限制 sshd 可綁定埠號的設定檔。若該系統執行了 ufw,只需新增一條規則。這就是兩者間的全部差異。RHEL 系列發行版在全新安裝時預設執行 firewalld 並強制執行 SELinux,兩者皆會檢查埠號。

請依照此順序執行,以確保目前的連線在每個步驟中皆不會中斷:

  1. 在 firewalld 中開啟新埠,暫時保留埠 22 開啟。
  2. 使用 semanage 為新埠新增 SELinux 標籤。
  3. 在 sshd 設定檔中設定埠號。
  4. 重啟 sshd,接著從第二個終端機以新埠登入,確認無誤後再關閉第一個終端機。
在開始之前,請先找到供應商提供的網頁控制台(VNC 或序列埠),並確認您可以透過該控制台登入。若變更過程出錯,該控制台是您唯一的復原途徑。變更埠號是租戶將自己鎖在剛購買的伺服器外最常見的原因之一。

首先,安裝 semanage

semanage 是用於編輯 SELinux 政策設定的工具,但 Rocky Linux 或 AlmaLinux 的最小化安裝並不包含此工具。它位於 policycoreutils-python-utils 套件中。

sudo dnf install -y policycoreutils-python-utils

若在安裝該套件前執行此指令,系統會回傳 sudo: semanage: command not found,許多使用者因此誤以為系統未安裝 SELinux 而跳過此步驟。事實上 SELinux 已安裝,僅缺少管理工具。若您對 dnf 的語法感到陌生,dnf 與 apt 指令對照表 可協助您將其對應至您熟悉的指令。

選擇連接埠並確認未被佔用

1024 到 65535 之間的任何閒置 TCP 連接埠皆可使用。在決定使用前,請執行以下兩項檢查:

sudo ss -tlnp | grep -w 2222
sudo semanage port -l | grep -w 2222

第一項檢查顯示是否有處理程序正在監聽該連接埠。第二項檢查顯示 SELinux 政策是否已將該連接埠指派給其他服務類型。若連接埠閒置,兩項指令皆不會回傳任何內容。若該連接埠已被政策佔用,步驟 2 中的 semanage port -a 將會失敗並顯示 ValueError: Port tcp/2222 already defined,此時請選擇其他連接埠號碼。

本指南全程以 2222 作為範例。由於這是掃描器在 22 埠之後最先嘗試的連接埠,在實際伺服器上請選擇較不顯眼的號碼。

步驟 1:在 firewalld 中開啟連接埠

sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports

--permanent 會將規則寫入磁碟上的區域設定檔,但不會影響執行中的防火牆。--reload 則會將磁碟上的設定載入至執行中的防火牆。若跳過重新載入步驟,規則雖已存在卻不會生效,直到下次 firewalld 重啟為止;這是導致此程序看似無故失敗的最常見原因之一。

暫時保留 ssh 服務項目。該項目負責維持 22 埠開啟,是您測試時的備援機制。

請同時檢查您的供應商控制台。許多主機商會在 VPS 前端架設作業系統之外的網路防火牆,因此即使您已在 firewalld 開啟連接埠,流量仍可能在上游被丟棄。VPS 的 firewalld 基礎指南 涵蓋了區域概念以及執行階段與永久設定的差異,若您對此模式不熟悉,請參考該文。

步驟 2:為 SELinux 標記連接埠

sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -l | grep ssh_port_t

-a 用於新增連接埠指派。-t ssh_port_t 是 SSH 連接埠所使用的類型。第二個指令會列出目前 ssh_port_t 所涵蓋的所有項目,以便您在修改 daemon 之前,確認該連接埠號碼已成功加入。

為何 SELinux 會封鎖該連接埠

SELinux (Security-Enhanced Linux) 會為系統上的每個物件加上標籤,TCP 連接埠編號也不例外。SSH daemon 在名為 sshd_t 的網域中受限執行。安全性原則允許 sshd_t 綁定標籤為 ssh_port_t 的 TCP 連接埠,而系統預設僅有 22 號連接埠擁有此標籤。若要求 daemon 綁定 2222 號連接埠,核心會檢查該標籤,找出原則指派給該編號的通用類型,並拒絕該 socket 的 name_bind 權限。

這就是為何此錯誤看起來不像防火牆問題。核心在監聽 socket 建立前就已拒絕請求,因此 sshd 會回報錯誤並結束執行。防火牆問題則恰恰相反:daemon 運作正常,但封包在進入時被丟棄。

getenforce 可顯示伺服器目前的模式。在 Permissive 模式下,拒絕行為會被記錄但不會強制執行,因此變更連接埠看起來似乎成功,但當有人執行 setenforce 1 或伺服器重啟進入 enforcing 模式時就會失效。無論如何,請務必為該連接埠加上標籤。伺服器 SELinux 基礎指南 對模式、上下文 (context) 與布林值 (boolean) 有更詳盡的說明。

步驟 3:在 sshd 設定中指定連接埠

在 Rocky Linux 9 與 10、AlmaLinux 9 與 10 以及目前的 Fedora 版本中,/etc/ssh/sshd_config 的開頭包含一行 include 指令,因此最妥善的修改方式是建立一個獨立的設定檔。如此一來,套件更新便不會覆蓋您的設定。

grep -n '^Include' /etc/ssh/sshd_config
echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf
sudo sshd -t

grep 中找不到 Include 設定行(例如在 Rocky Linux 8 或其他舊版映像檔中),請直接將 Port 2222 加入 /etc/ssh/sshd_configsshd -t 會解析包含獨立設定檔在內的完整組態,並回報語法錯誤。在重新啟動服務前,請務必修正所有回報的錯誤,因為若設定檔解析失敗,daemon 將無法重新啟動。

Port 可能會出現多次,sshd 會監聽所有列出的連接埠。在啟用新連接埠的第一天,同時保留 Port 22Port 2222 是一個低成本的安全備援措施,但請務必記得在確認連線正常後將其移除。

您的 sshd 是否由 socket 單元啟動?

部分映像檔透過 systemd socket activation 啟動 SSH,而非作為常駐服務執行。在此設定下,systemd 會接管監聽中的 socket 並將連線轉交給 sshd,因此 Portsshd_config 中的設定會被完全忽略。在重新啟動任何服務前,請先進行檢查:

systemctl is-enabled sshd.socket

若輸出為 enabled,代表連接埠是在 socket 單元中設定,而非在 sshd_config 內:

sudo systemctl edit sshd.socket
[Socket]
ListenStream=
ListenStream=2222

必須加入空的 ListenStream=。由於設定值會在各個 drop-in 檔案間累加,若未先透過空賦值清除列表,該 socket 將會同時監聽 22 與 2222 埠。請使用 sudo systemctl daemon-reload 套用設定,接著執行 sudo systemctl restart sshd.socket。若您的伺服器上未啟用或不存在該單元,則本節內容不適用於您。

步驟 4:重新啟動,並從第二個終端機進行測試

sudo systemctl restart sshd
systemctl status sshd
sudo ss -tlnp | grep sshd

請保持此終端機開啟,不要登出。在您的本機開啟第二個終端機,並透過新的連接埠進行連線:

ssh -p 2222 youruser@203.0.113.10

僅在確認第二次登入成功後,再關閉第一個連線。若連線失敗,您仍保有目前的 shell,可從中復原所有變更。養成此習慣,能讓您避免耗費整個下午在供應商控制台進行修復,僅需五分鐘即可完成變更。

防火牆丟棄封包還是 SELinux 拒絕存取?如何區分

從您的筆記型電腦來看,這兩種失敗情形幾乎一模一樣。但在伺服器端,它們的表現截然不同。

  • systemctl status sshd 顯示服務單元失敗,代表 daemon 從未成功取得 socket。這通常是設定錯誤或 SELinux 拒絕存取所致。
  • 若服務單元為 active 且 ss -tlnp 顯示 sshd 已綁定至新連接埠,則 daemon 運作正常,問題出在網路路徑上:可能是 firewalld、供應商的外部防火牆,或是您連線的位址與連接埠有誤。

針對 SELinux 的情況,請直接讀取稽核紀錄,不要憑空猜測:

sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pager

name_bind 拒絕存取類別 tcp_socket 時,會指出 comm="sshd" 中的處理程序、src= 中的連接埠號,以及 tcontext= 中該連接埠實際持有的標籤。最後一個欄位即為答案。若顯示的不是 ssh_port_t,代表步驟 2 的設定未套用到您使用的連接埠,通常是連接埠號輸入錯誤或通訊協定不符。若您希望 sealert 將紀錄轉換為易讀的語句,請安裝 setroubleshoot-server

當核心拒絕綁定時,sshd 本身寫入的訊息如下:

error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.

在 1024 以上的連接埠出現 Permission denied(此處無需 root 權限即可綁定)是 SELinux 的典型特徵。若同一行出現 Address already in use 則是不同的錯誤:代表有其他處理程序正佔用該連接埠。從客戶端來看,連線被拒絕與連線逾時的差異 可區分兩種網路狀況;「被拒絕」代表封包已抵達主機但無人監聽,「逾時」則代表完全沒有回應。

關閉 22 埠並更新客戶端設定

確認已能透過新埠號成功登入多次後,即可關閉 22 埠:

sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

無須更動 22 埠的 SELinux 標籤。該標籤源自基礎原則,一旦防火牆停止放行封包,該標籤便不再具備任何權限。

接著修正客戶端設定,因為所有預設使用該埠的工具現在都需要重新指定。請將設定寫入您本機的 ~/.ssh/config,即可免去每次輸入 -p 的麻煩:

Host myvps
  HostName 203.0.113.10
  Port 2222
  User youruser

scpsftprsync 與 Ansible 都會讀取該檔案。備份工作、監控檢查與寫死 22 埠的 cron script 則不會讀取,請趁著對變更記憶猶新時將其找出並一併修正。

變更連接埠的實際效益與限制

此舉能減少日誌雜訊。自動化掃描器會持續攻擊 22 埠,將服務移至其他埠可過濾掉大部分日誌紀錄,讓真實事件更易於辨識。但這並非安全防護手段。任何掃描整個埠範圍的工具,都能發現您的 daemon 並讀取其版本標頭。請將變更連接埠視為日常維護,真正的防護應落實於僅限金鑰驗證並停用密碼登入,詳細步驟請參考 VPS 的 SSH 強固指南

上述內容在兩個主要的 RHEL 重建版上運作方式完全相同,因為它們源自相同的原始碼。若您仍在兩者間抉擇,請參閱 Rocky Linux 與 AlmaLinux 比較。在參考舊版指南前,請先使用 cat /etc/os-release 確認您實際使用的發行版本。針對 Rocky Linux 8 編寫的指南目前仍有不錯的搜尋排名,且其 semanagefirewall-cmd 步驟依然正確;但 Rocky 8 沒有 sshd_config.d 引入行,也不需考慮 socket 單元,因此這些指南中關於 sshd 的部分與目前的系統並不相符。

必須告知 fail2ban 新的連接埠

fail2ban 並未包含在基礎軟體庫中。它來自 EPEL (Extra Packages for Enterprise Linux):

sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalld

fail2ban-firewalld 子套件能讓 fail2ban 透過 firewalld 執行封鎖,這正是當 firewalld 負責管理規則集時所需要的設定。

預設的 sshd jail 設定了 port = ssh,該名稱會透過 /etc/services 解析為 22。在您變更連接埠後,jail 會監控一個無人攻擊的連接埠,導致在 2222 埠上累積失敗登入嘗試時,卻無法封鎖任何來源。請在 /etc/fail2ban/jail.local 中直接以數字設定連接埠:

[sshd]
enabled = true
port = 2222
backend = systemd
maxretry = 5
bantime = 3600

backend = systemd 會從 journal 讀取失敗記錄,而非從 /var/log/secure 讀取;在未安裝 rsyslog 的最小化安裝環境中,這是較安全的選擇。使用 sudo systemctl enable --now fail2ban 啟動它,並使用 sudo fail2ban-client status sshd 檢查 jail 狀態。其 jail 語法與 Ubuntu 24.04 的 fail2ban SSH 設定 所使用的相同。僅有套件來源與封鎖動作有所差異。

修補漏洞比更換連接埠更重要

若伺服器僅更換 SSH 連接埠,卻長達四個月未套用安全性更新,其安全性遠低於使用預設 22 埠但每晚自動修補的伺服器。在取得 root 權限的當下,請順手啟用自動更新:Rocky Linux 與 AlmaLinux 的自動 dnf 更新 說明了如何設定計時器,以及在下載更新與自動套用更新之間進行選擇。

FAQ

為什麼在 Rocky Linux 修改連接埠後 sshd 無法啟動?

通常是因為缺少 SELinux 連接埠標籤。sshdsshd_t 網域中受限執行,且原則僅允許其綁定標記為 ssh_port_t 的連接埠,預設僅包含 22 埠。核心會拒絕綁定請求,導致 daemon 無法監聽並直接退出,此時 journalctl -u sshd 會記錄類似 error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. 的錯誤訊息。請使用您的連接埠號碼執行 sudo semanage port -a -t ssh_port_t -p tcp 2222,隨後重啟服務。若找不到 semanage,請先安裝 policycoreutils-python-utils

若 SELinux 處於 permissive 模式,我還需要使用 semanage 嗎?

需要。在 permissive 模式下,系統會記錄拒絕存取的事件但仍允許綁定,因此變更看起來像是成功了,但標籤實際上並未補上。一旦有人執行 setenforce 1,或是系統以 SELINUX=enforcing 參數在 /etc/selinux/config 開機,sshd 就會停止在該連接埠啟動。補上標籤只需一個指令,能避免未來發生難以排查的失敗。

連接埠標籤已設定且 sshd 正在執行,為什麼連線仍逾時?

daemon 正常執行代表 SELinux 檢查已通過,封包是在進入過程中被丟棄。請檢查 sudo firewall-cmd --list-ports 是否包含您的連接埠,並確認在執行 --permanent 規則後是否已執行 firewall-cmd --reload,因為永久規則若未重載,不會立即套用到執行中的防火牆。接著檢查 VPS 前端的網路防火牆控制面板,這是第二個常見的阻擋點,且作業系統內部的日誌無法顯示該處的阻擋行為。

除了 22 埠,我應該使用哪個連接埠?

任何 1024 到 65535 之間未被佔用的 TCP 連接埠皆可。在正式伺服器上應避免使用 2222 與 22222,因為掃描器在嘗試 22 埠後會優先測試這些連接埠。請使用 sudo ss -tlnp 確認該號碼未被佔用,使用 sudo semanage port -l 確認 SELinux 原則未將其指派給其他服務,並避開未來可能安裝的服務所使用的連接埠。使用一個高位且不易記憶的數字即可,因為您只需將其寫入 ~/.ssh/config 一次,之後便無需手動輸入。