保衛 OpenSSH
<<TableOfContents: execution failed [Argument "maxdepth" must be an integer value, not "[1]"] (see also the log)>>
OpenSSH(或者 Secure Shell)以經成為一個取代 telnet 協議作遠端存取用的現有標準。SSH 已經令 telnet 等協議多餘的,當中絕大部份原因是由於連線被加密,以及不再以純文字公開地傳送密碼。
然而,預設的 ssh 安裝並非完美。當你營運一個 ssh 伺服器時,有數個簡單的步驟可以明顯地加固你的安裝。
1. 採用難猜測的密碼/用戶名稱
如果你所營運的 ssh 是對外的,你首先會發現的事情,很可能就是駭客嘗試猜/測用戶名稱/密碼的記錄。駭客一般會掃描連接埠 22(ssh 預設聆聽的連接埠)來找尋執行 ssh 的機器,然後嘗試強行攻擊它。借著使用難猜測的密碼,我們希望任何攻擊在成功前會被記錄底及被留意到。
盼望你已經採用了難猜測的密碼。要不然,請嘗試選擁有以下特徵的密碼:
- 最少有 8 個字元
- 同時有大寫和小寫字母
- 同時有字母和數目字
- 有非英數的字元(例如 ! " £ $ % ^ 等特別字元)
使用難測密碼的好處並不止於 ssh,它更會影響到系統安全的各個範疇。有關密碼的更多資訊可以在 CentOS 的文檔內找到:
http://www.centos.org/docs/4/html/rhel-sg-en-4/s1-wstation-pass.html
如果你完全沒法阻止你的用戶選用易猜測的密碼,請考慮以隨機產生或難猜測的字串作為用戶名稱。如果壞人不能猜測用戶名稱,他們便不能強加猜測密碼。然而,這只是隱晦資訊來換取安全,所以要留心用戶名稱透過用戶發送的電郵等途徑而被洩漏。
2. 停用 root 登入
SSH 伺服器的設定都儲存在 /etc/ssh/sshd_confg 這個檔案。要停用 root 登入,請確定你有以下一行:
# 阻止 root 登入: PermitRootLogin no
然後請重新啟動 sshd 服務:
service sshd restart
你果你需要 root 的權限,請登入為一般用戶,然後使用 su 這個指令。
3. 限制用戶登入
SSH 登入可以局限給某些需要遠端存取的用戶。如果你的系統有很多用戶,一個合理的做法就是局限遠端存取給那真正有需要的用戶,藉以減低其他用戶採用易測密碼的影響。在 /etc/ssh/sshd_config 內加入 AllowUsers 一行,以空格隔開用戶名稱。例如:
AllowUsers alice bob
接著請重新啟動 sshd 服務。
4. 停用第 1 類協議
SSH 可以採用兩款協議:第 1 類及第 2 類協議。較舊的第 1 類協議的安全性較低,因此它應該被停用,除非你知道你必須要使用它。請在 /etc/ssh/sshd_config 檔內找尋以下一行,解除註釋,並作出如下修改:
# Protocol 2,1 Protocol 2
然後請重新啟動 sshd 服務。
5. 採用非標準的連接埠
根據預設值,ssh 在連接埠 22 聆聽進入的連線。一個駭客如果要斷定 ssh 是否在你的機器上運行,他最大可能就是掃描連接埠 22。一個有效混淆他的方法就是在非標準的連接埠上運行 ssh。任何未被使用的連接埠都可行,但首選的是 1024 以上的。很多人選用 2222 作為替代的連接埠(它很易記),正如 8080 經常被用作 HTTP 的替代連接埠。正正由於這個原因令它不是個好的選擇,因為任何掃描連接埠 22 的駭客亦不會放過連接埠 2222。隨機地選用一個未被使用的高位連接埠會比較合宜。要進行改動,請在你的 /etc/ssh/sshd_config 檔內加入以下一行:
# 在非標準的連接埠上執行 ssh: Port 2345 #修改我
然後重新啟動 sshd 服務。請勿忘記在你的路由器及相關的防火牆規則裡作出任何必要的改動。譬如 CentOS 7(或以上)版本,你可以透過複製 firewalld 在 /etc/firewalld/ 內的服務檔,然後更改 ssh 服務的連接埠:
$ cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/ssh-custom.xml
請更改 /etc/firewalld/services/ssh-custom.xml 令連接埠與 ssh 設定檔內的相同:
<port protocol="tcp" port="2345"/>
最後,移除 ssh 服務,新增 ssh-custom 服務,並重啟 firewalld 令改動生效:
$ firewall-cmd --permanent --remove-service='ssh' $ firewall-cmd --permanent --add-service='ssh-custom' $ firewall-cmd --reload
又或者在 CentOS 6 上新增 iptable 的規則來開啟新的 ssh 埠:
$ iptables -I INPUT -p tcp --dport 2345 -j ACCEPT
請不要忘記關閉舊的連接埠。
在 CentOS 6 及以上版本,你亦需要更新 selinux,並正確地標籤所選用的連接埠,否則 sshd 便無法存取它。舉個例說:
$ semanage port -a -t ssh_port_t -p tcp 2345 #請更改這處
因為 ssh 不再在標準的連接埠上聆聽連線,你須要告訴客戶端要連線到哪個連接埠。在指令行上執行 ssh 客戶端時,你可以用 -p 選項來指定連接埠:
$ ssh -p 2345 myserver
又或者如果你使用 konqueror 的 fish 協議,你可用:
fish://myserver:2345/remote/dir
如果你覺得每次連線時都要指定連接埠似乎很痛苦,你只需在你個人的 ~/.ssh/config 檔案裡加入一個指定連接埠的記錄:
# 客戶端 ~/.ssh/config Host myserver HostName 72.232.194.162 User bob Port 2345
~/.ssh/config 必須有以下存取權:
$ chmod 600 ~/.ssh/config
6. 在防火牆過濾 SSH
如果你只須由一個 IP 位址進行遠端存取(例如由辦工室進入家中的伺服器),請考慮在你的路由器或 iptables 內加入一條防火牆的規則,將連接埠 22 的存取權限制到特定的 IP 位址,藉此對連線進行過濾。舉個例說,在 iptables 內你可以用這類型的 iptables 規則(CentOS 6)達至這個目的:
$ iptables -A INPUT -p tcp -s 72.232.194.162 --dport 22 -j ACCEPT
又或者採用 firwalld(CentOS 7)的加強規則局限 ssh 至特定的連接埠。來源位址可以是單一位址或是基本位址及位元遮罩:
# 視乎已啟用及現存的設定,選用 ssh 或 ssh-custom $ firewall-cmd --permanent --remove-service="ssh" $ firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="72.232.194.162" service name="ssh-custom" accept' $ firewall-cmd --reload
SSH 亦對 TCP 包裝函式有內置支援,因此 ssh 服務的存取權亦可同時用 host.allow 及 hosts.deny 來進行管制。
如果你無法限制來源地的 IP 位址,而必須公開 ssh 連接埠,那麼 iptables 依然可以透過記錄及攔截來自同一 IP 位址的重覆登入嘗試,幫助你阻止強行的攻擊。例如透過 iptables:
$ iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name ssh --rsource $ iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent ! --rcheck --seconds 60 --hitcount 4 --name ssh --rsource -j ACCEPT
第一條規則利用 recent 模塊來記錄每個存取連接埠 22 的新嘗試。第二條規則檢查這個 IP 位址在過去 60 秒內有否嘗試 4 次或以上的連線,若然沒有更接納封包。注意這個規則須要輸入鏈採用 DROP 的預設政策。
如果你在非標準的連接埠上執行 ssh,請不要忘記對連接埠作出相應修改。情況許可的話,利用防火牆進行過濾是一個非常有效的方法來保衛 ssh 伺服器。
那些採用 FirewallD 服務的系統(CentOS 7 或以上)可使用 firewall-cmd: # 視乎已啟用及現存的設定,選用 ssh 或 ssh-custom $ firewall-cmd --permanent --remove-service="ssh" $ firewall-cmd --permanent --add-rich-rule='rule service name="ssh-custom" accept limit value="4/m" log' $ firewall-cmd --reload}}} 首個指令移除較寬鬆的的規則,而第二個指令建立一條每分鐘只指納 4 個連線並記錄所有連線的規則。
7. 採用公鑰/私鑰來驗證
採用加密金鑰來驗證提供兩大好處。首先,如果你應用公鑰/私鑰,是方便,因為你不用再輸入密碼(除非你用密碼來保護你的金鑰)。第二,當伺服器能進行金鑰對的驗證,你便可以完全停用密碼驗證,意即存取時靠賴授權的金鑰 —— 因此不再有猜測密碼的嘗試。
建立及在你的 ssh 伺服器上安裝金鑰對是個相對地簡單的過程。
首先,在你會用來連線到伺服器的客戶端上建立一對金鑰(你須要在每台用來連線的機器上這樣做):
$ ssh-keygen -t rsa
這樣做會在你的(隱藏了的)~/.ssh 目錄內建立兩個檔案,名叫:id_rsa 及 id_rsa.pub。第一個檔案:id_rsa 是你的私鑰,而另一個:id_rsa.pub 是你的公鑰。
如果你不想每次連線時都被問及密碼(它是用來解開特定的公鑰),在建立金鑰對的時候,你只須按 enter 作為密碼。建立金鑰對時,是否以密碼加密純粹是你的決定。如何你不將金鑰加密,任何人奪得你的本地機器後,便自動擁有遠端伺服器的 ssh 存取權。此外,本地機器上的 root 能夠存取你的金鑰:但假若你不能信任 root(或者 root 已被攻佔),你已經大禍臨頭。將金鑰加密捨棄了不用密碼的 ssh 伺服器,來換取額外的安全,得來的就是輸入密碼來使用這條金鑰。你可利用 ssh_agent 這個程式進一步簡化這個程序。
現在為你的私鑰設定權限:
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/id_rsa
請將公鑰(id_rsa.pub)複製到伺服器上,然後安裝它在 authorized_keys 清單內:
$ cat id_rsa.pub >> ~/.ssh/authorized_keys
註:一旦你匯入了公鑰,你可以在伺服器上刪除它。
最後,設定伺服器上的檔案權限:
$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/authorized_keys
如果 /etc/ssh/sshd_config 內的 StrictModes 被啟用(預設值),以上的權限是必須的。
請確保你已設置正確的 SELinux 脈絡:
$ restorecon -Rv ~/.ssh
現在當你登入伺服器的時候,你便不用再輸入密碼(除非你在建立金鑰對的時候輸入了一個密碼)。ssh 預設是會先利用金鑰進行驗證。如何它找不到金鑰,或驗證失敗,ssh 會回落到平常的密碼驗證。
一旦你檢查過可以用金鑰對來登入伺服器,你可以在你的 /etc/ssh/sshd_conf 檔內加入以下設定來停用密碼驗證:
# 停用密碼驗證,強制使用金鑰 PasswordAuthentication no
8. 常見問題(FAQ)
問:CentOS 採用 X 版的 OpenSSH,而最新版本是 Y 版。X 版藏有一個嚴重的安全性漏洞,我應否升級?
答:不應該。上游供應者有一個政策,會將最新版本的安全性修正反向移植到現有的發行版本內。只要你擁有最新的更新,你的 CentOS 發行版本已經得到全面修正。有關反向移植安全性修正的詳情,請參閱這裡:
http://www.redhat.com/advice/speaks_backport.html
問:我如何令 ssh 容讓以 NFS 共享用戶主目錄的機器採用無密碼的驗證?
答:SELinux 預設攔阻 root 存取以 NFS 共享、非公用的目錄及檔案,因此 ssh 無法讀取 ~/.ssh 內的用戶金鑰檔。若要批準存取權,請用以下指令更改 use_nfs_home_dirs 的設定值:
setsebool -P use_nfs_home_dirs 1
https://www.centos.org/forums/viewtopic.php?t=49194
9. 連結
http://www.centos.org/docs/5/html/Deployment_Guide-en-US/ch-openssh.html
http://www.dragonresearchgroup.org/insight/sshpwauth-tac.html
Translation of revision 54