SSH 只能用於遠程 Linux 主機?那説明你見識太小了!

語言: CN / TW / HK

關注 公眾號, 回覆“ 1024 ”獲取 2TB 學習資源!

Telnet和SSH

Telnet

Telnet是一個遠程連接服務是一個C/S架構,具有Server端和Client端,Client通過telnet協議連接到服務器端,這是早期常用的遠程連接方法.然後改方法進行連接的過程中使用的都是明文進行傳輸,在同一網絡中的其他用户很容易通過網絡工具捕捉到該數據包進行分析進而獲取到密碼.這是一個很不安全的連接方法.telnet協議使用的是23號端口是基於tcp的鏈接。

SSH

telnet在本質上都是不安全的 ,因為它們在網絡上用明文傳送口令和數據,別有用心的人非常容易就可以截獲這些口令和數據。而且,這些服務程序的安全驗證方式也是有其弱點的, 就是很容易受到“中間人”(man-in-the-middle)這種方式的攻擊。所謂“中間人”的攻擊方式, 就是“中間人”冒充真正的服務器接收你的傳給服務器的數據,然後再冒充你把數據傳給真正的服務器。

服務器和你之間的數據傳送被“中間人”一轉手做了手腳之後,就會出現很嚴重的問題。

SSH 簡介

SSH的英文全稱是 Secure Shell 。通過使用SSH,你可以把所有傳輸的數據進行加密,這樣“中間人”這種攻擊方式就不可能實現了,而且也能夠防止DNS和IP欺騙。還有一個額外的好處就是傳輸的數據是經過壓縮的,所以可以加快傳輸的速度。SSH有很多功能,它既可以代替telnet,又可以為ftp、pop、甚至ppp提供一個安全的“通道”。

最初SSH是由芬蘭的一家公司開發的。但是因為受版權和加密算法的限制,現在很多人都轉而使用OpenSSH。OpenSSH是SSH的替代軟件,而且是免費的,可以預計將來會有越來越多的人使用它而不是SSH。SSH是由客户端和服務端的軟件組成的,有兩個不兼容的版本分別是:1.x和2.x。用SSH 2.x的客户程序是不能連接到SSH 1.x的服務程序上去的。OpenSSH 2.x同時支持SSH 1.x和2.x。

SSH是如何工作的?

SSH 由 服務器客户端 組成,在整個通信過程中,為建立安全的SSH通道,會經歷 如下幾個階段

連接建立

SSH服務器在指定的端口偵聽客户端的連接請求,在客户端向服務器發起連接請求後,雙方建立一個TCP連接。

版本協商

SSH協議目前存在SSH1.X(SSH2.0之前的版本)和SSH2.0版本。SSH2.0協議相比SSH1.X協議來説,在結構上做了擴展,可以支持更多的認證方法和密鑰交換方法,同時提高了服務能力。SSH服務器和客户端通過協商確定最終使用的SSH版本號。

算法協商

SSH支持多種加密算法,雙方根據各自支持的算法,協商出最終用於產生會話密鑰的密鑰交換算法、用於數據信息加密的加密算法、用於進行數字簽名和認證的公鑰算法以及用於數據完整性保護的HMAC算法。

密鑰交換

服務器和客户端通過密鑰交換算法,動態生成共享的會話密鑰和會話ID,建立加密通道。會話密鑰主要用於後續數據傳輸的加密,會話ID用於在認證過程中標識該SSH連接。

用户認證

SSH客户端向服務器端發起認證請求,服務器端對客户端進行認證。SSH支持以下幾種認證方式:

  • 密碼(password)認證 :客户端通過用户名和密碼的方式進行認證,將加密後的用户名和密碼發送給服務器,服務器解密後與本地保存的用户名和密碼進行對比,並向客户端返回認證成功或失敗的消息。

  • 密鑰(publickey)認證 :客户端通過用户名,公鑰以及公鑰算法等信息來與服務器進行認證。

  • password-publickey認證 :指用户需要 同時滿足 密碼認證和密鑰認證才能登錄。
  • all認證 :只要滿足密碼認證和密鑰認證其中一種即可。

基於口令的認證過程:

基於密鑰的認證過程:

會話請求

認證通過後,SSH客户端向服務器端發送會話請求,請求服務器提供某種類型的服務,即請求與服務器建立相應的會話。

會話交互

會話建立後,SSH服務器端和客户端在該會話上進行數據信息的交互。

SSH 配置選項

SSH 的配置文件主要分為 服務器端客户端 :

  • 服務器端:/etc/ssh/sshd_config

  • 客户端:/etc/ssh/ssh_config

限制root用户遠程登錄

# vi /etc/ssh/sshd_config
PermitRootLogin no

通過控制用户訪問限制 SSH 訪問

# vi /etc/ssh/sshd_config
AllowUsers fsmythe bnice swilson
DenyUsers jhacker joebadguy jripper
# vi /etc/ssh/sshd_config
Protocol 2

不要支持閒置會話,並配置 Idle Log Out Timeout 間隔:

#當客户端連上服務器端後,若沒有任何操作則,服務器端默認會
#每隔一定時間發送一個alive消息給客户端尋求客户端應答,
#默認一共發三次.若都沒有迴應,則斷開連其中            
#ClientAliveInterval設置每隔多少秒發送一次alive消息
#ClientAliveCountMax 設置一共發多少次.
# vi /etc/ssh/sshd_config
ClientAliveInterval 600      
# (Set to 600 seconds = 10 minutes)
ClientAliveCountMax 0

禁用基於主機的身份驗證:(這種認證方式是不安全的)

# vi /etc/ssh/sshd_config
HostbasedAuthentication no
6.使用 Chroot SSHD 將 SFTP 用户侷限於其自己的主目錄
# vi /etc/ssh/sshd_config
ChrootDirectory /home/%u

禁用空密碼

# vi /etc/ssh/sshd_config
PermitEmptyPasswords no

指令壓縮

Compression {yes|no|delayed}
#默認是delayed 意思就是等到用户認證結束後再對數據進行壓縮

設置日誌級別

#LogLevel INFO 默認是INFO級別,調試的時候可以選擇DEBUG
#級別,這樣調試信息更加詳細

支持圖形界面操作

X11Forwarding yes
#對Xwindows的數據進行轉發,開啟該項並使用xshell程序遠程登
#錄服務器是可以打開圖形界面程序

其它選項

PrintMotd no             
# 登入後是否顯示出一些信息呢?例如上次登入的時間、地點等,預設是 yes ,但是,如果為了安全,可以考慮改為 no !
PrintLastLog yes     
# 顯示上次登入的信息!可以啊!預設也是 yes !
KeepAlive yes    
# 一般而言,如果設定這項目的話,那麼 SSH Server 會傳送
# KeepAlive 的訊息給 Client 端,以確保兩者的聯機正常!
# 在這個情況下,任何一端死掉後, SSH 可以立刻知道!而不會
# 有殭屍程序的發生!
Banner /to/somefile
# 指定在用户完成認證前輸出到終端的信息
GSSAPIAuthentication no
#指定是否使用基於GSSAPI的用户認證默認為no
MaxAuthTries 默認值為6
#每一個鏈接最多嘗試驗證的次數為這個值得一半,此外失敗的信息還會記錄在/var/log/message
UseDNS yes
#是否對主機名進行dns解析(常用與沒有固定IP只有域名的場合)

SSH 密鑰認證實踐

生成密鑰(客户端操作)

[root@wwww ~]# ssh-keygen -t rsa -b 4096
#-t 指定生成密鑰的算法 -b指定密鑰的位數
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
#詢問生成的密鑰放在的位置,默認放在家目錄下的.ssh目錄
Enter passphrase (empty for no passphrase):
#詢問是否對私鑰再進行加密
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
e7:60:45:fe:d8:09:24:c1:1e:ef:35:cc:c1:c3:24:e4 [email protected]
#生成的指紋信息

: 這裏使用ssh-keygen -t rsa -b 4096 -f /path/to/KEY_FILE -P '指定加密私鑰的密碼' 這樣既可實現自動化,不要交互了。

傳送密鑰至遠程服務器

方式一:

[root@wwww ~]# scp ~/.ssh/id_rsa.pub [email protected]:~/
The authenticity of host '192.168.157.132 (192.168.157.132)' can't be established.
RSA key fingerprint is 6e:0f:f8:f8:c7:c2:11:e6:4d:99:aa:16:6a:81:4a:02.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.157.132' (RSA) to the list of known hosts.
[email protected]'s password: 
id_rsa.pub                                                                                   100%  740     0.7KB/s   00:00 

然後遠程連接到遠程主機進行設置:

[root@wwww ~]# ssh [email protected]
[email protected]'s password:
Last login: Sat Jan 18 21:16:49 2014 from 192.168.157.128
[root@bogon ~]# mkdir .ssh
#這個目錄默認不存在,但是這太機子若連接過其他服務器就會自動生成.ssh目錄用來保存known_hosts文件
[root@bogon ~]# chmod 700 .ssh
#這個權限很重要,設置不對的話是沒法通過驗證
[root@bogon ~]# cat id_rsa.pub >> ~/.ssh/authorized_keys
#這裏最好每次都是以>>追加的形式輸出,不然會覆蓋其他主機的公鑰
[root@bogon ~]# chmod 600 ~/.ssh/authorized_keys
#修改權限放在其他用户修改查看

到此配置完成

方式二

[root@wwww ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]
[email protected]'s password:
Now try logging into the machine, with "ssh '[email protected]'", and check in:
  .ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
#直接通過命令完成.推薦使用,但是並不是每台機器上都會裝有這個命令.

SSH雙機互信腳本

#!/usr/bin/expect
#author Jeff_linux
#Time 2022-01-18
set local_passwd "server"
set des_passwd "server"
set timeout 10
set localip "192.168.0.254"
set desip "192.168.0.251"
spawn ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -P ""
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@$desip
expect {
"yes/no" { send "yes\r";exp_continue}
"password:" {send "$des_passwd\r";exp_continue}
}
spawn ssh $desip "ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -P \"\""
spawn scp $desip:/root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
expect {
"yes/no" { send "yes\r";exp_continue}
"password:" {send "$local_passwd\r";exp_continue}
}

SSH 命令

語法格式

ssh [options] [user@]hostname [command]

參數説明

-b bind_address :在本地主機上綁定用於ssh連接的地址,當系統有多個ip時才生效。
-E log_file :將debug日誌寫入到log_file中,而不是默認的標準錯誤輸出stderr。
-F configfile :指定用户配置文件,默認為~/.ssh/config。
-f :請求ssh在工作在後台模式。該選項隱含了"-n"選項,所以標準輸入將變為/dev/null。
-i identity_file:指定公鑰認證時要讀取的私鑰文件。默認為~/.ssh/id_rsa。
-l login_name :指定登錄在遠程機器上的用户名。也可以在全局配置文件中設置。
-N :顯式指明ssh不執行遠程命令。一般用於端口轉發,見後文端口轉發的示例分析。
-n :將/dev/null作為標準輸入stdin,可以防止從標準輸入中讀取內容。ssh在後台運行時默認該項。
-p port :指定要連接遠程主機上哪個端口,也可在全局配置文件中指定默認的連接端口。
-q :靜默模式。大多數警告信息將不輸出。
-T :禁止為ssh分配偽終端。
-t :強制分配偽終端,重複使用該選項"-tt"將進一步強制。
-v :詳細模式,將輸出debug消息,可用於調試。"-vvv"可更詳細。
-V :顯示版本號並退出。
-o :指定額外選項,選項非常多。

user@hostname :指定ssh以遠程主機hostname上的用户user連接到的遠程主機上,若省略user部分,則表示使用本地當前用户。

:如果在hostname上不存在user用户,則連接將失敗(將不斷進行身份驗證)。

command :要在遠程主機上執行的命令。指定該參數時,ssh的行為將不再是登錄,而是執行命令,命令執行完畢時ssh連接就關閉。

更多關於SSH 命令的操作詳解請參考: 每天學一個 Linux 命令(59):ssh

SSH 端口轉發

SSH 不僅僅能夠自動加密和解密 SSH 客户端與服務端之間的網絡數據,同時,SSH 還能夠提供了一個非常有用的功能,那就是端口轉發,即將TCP 端口的網絡數據,轉發到指定的主機某個端口上,在轉發的同時會對數據進行相應的加密及解密。如果工作環境中的防火牆限制了一些網絡端口的使用,但是允許 SSH 的連接,那麼也是能夠通過使用SSH轉發後的端口進行通信。轉發主要分為本地轉發與遠程轉發兩種類型。

轉發的參數

-C:壓縮數據
-f :後台認證用户/密碼,通常和-N連用,不用登錄到遠程主機。
-N :不執行腳本或命令,通常與-f連用。
-g :在-L/-R/-D參數中,允許遠程主機連接到建立的轉發的端口,如果不加這個參數,只允許本地主機建立連接。
-L : 本地端口:目標IP:目標端口
-D : 動態端口轉發
-R : 遠程端口轉發
-T :不分配 TTY 只做代理用
-q :安靜模式,不輸出錯誤/警告信息

本地轉發

有本地網絡服務器的某個端口,轉發到遠程服務器某個端口。説白了就是,將發送到本地端口的請求,轉發到目標端口。格式如下:

ssh -L 本地網卡地址:本地端口:目標地址:目標端口 用户@目標地址。

現在我們利用本地轉發來解決一個問題,比如我們有兩台機器,如下:

  • centos A(192.168.13.139)

  • centos B(192.168.13.142)

現在,centos B(192.168.13.142)機器上面安裝了mysql,並設置了運行任何主機連接,如下:

此時,在centos A(192.168.13.139)上面是可以連上centos B(192.168.13.142)的mysql,如下:

那麼,現在我開始centos B(192.168.13.142)限制不允許外部ip連接,僅僅讓127.0.0.1連接,如下:

此時,centos A(192.168.13.139)上面怎麼連接上centos B(192.168.13.142)的mysql呢?

此時,我們還是使用上面的mysql連接方式,肯定會報錯,如下:

當然在centos B(192.168.13.142)mysql還是可訪問的。

這個時候,我們就可以使用本地端口轉發了,將本地的某個端口,映射到centos B(192.168.13.142)機器上面的,如下:

ssh -L 127.0.0.1:3306:127.0.0.1:3306 [email protected]

因為本地網卡地址是可以省略的,上面的轉發,可以簡寫為:

ssh -L 3306:127.0.0.1:3306 [email protected]

當然,ssh連接的時候,若兩台機器的用户名相同,也是可以省略的,即命令可以簡寫為:

ssh -L 3306:127.0.0.1:3306 192.168.13.14

上面的代碼就是將本地的3306端口,轉發到192.168.13.142的3306端口。因為centos B(192.168.13.142)上面的mysql使用的3606端口。當然,我們首先得看看本地的3306端口是否被佔用,如被佔用,可以使用其他的端口。

數據流向如圖

  • 首先,centos A(192.168.13.139)上的應用將數據發送到本地的127.0.0.1上面的3306端口。

  • 然後,centos A(192.168.13.139)將3306端口的數據,通過SSH轉發到centos B(192.168.13.142)的3306端口。

  • 接着,centos B(192.168.13.142)將處理後的數據,原路返回給centos A(192.168.13.139)。

如果是首次通過ssh連接cetosB該機器,則會提示確認公鑰,並讓你選擇是否確定連接。

此時,我們在centos A上面連接centos B上面的mysql,就可以這麼寫了。

bin/mysql -h127.0.0.1 -uroot -p

如下: 我們可以通過下面命令,在centosA查看ssh轉發監聽的進程。

遠程轉發

由遠程服務器的某個端口,轉發到本地網絡的服務器某個端口。説白了,就是將發送到遠程端口的請求,轉發到目標端口。格式如下:

ssh -R 遠程網卡地址:遠程端口:目標地址:目標端口

下面三台機器為例,如下:

  • centos A(192.168.13.139)

  • centos B(192.168.13.142)

  • win7(10.18.78.135)

假設,win7(10.18.78.135)與centos B(192.168.13.142)不能直接連接,但是win7(10.18.78.135)與centos A(192.168.13.139)可以連接centos B(192.168.13.142)也可以centos A(192.168.13.139)連接,那麼,我們就可以在centos A(192.168.13.139)上面使用遠程端口轉發了,讓win7(10.18.78.135)與centos B(192.168.13.142)進行通信。

ssh -R 127.0.0.1:80:10.18.78.135:80 [email protected]

即centos B(192.168.13.142)監聽自己的80端口,然後將所有數據,由centos A(192.168.13.139)發給win7(10.18.78.135)。

SSH的遠程操作

ssh遠程操作,主要用於在遠程的機器上面執行某個操作,格式如下:

ssh user@host 'command'

案例1、在機器A(192.168.13.148)中查看機器B(192.168.13.149)的操作系統類型。

在A機器上面執行如下代碼:

ssh  [email protected]  'uname -a'

案例2、將機器A(192.168.13.148)中test文件夾複製到B機器(192.168.13.149)。

在A機器上面,執行如下命令:

tar -cz test | ssh [email protected] 'tar -xz'

當然,我們也可以使用scp命令或rz命令,傳輸文件。

案例3、在機器A(192.168.13.148)處查看B機器(192.168.13.149)是否監聽了1080端口。

在A機器上面,執行如下命令:

ssh [email protected] 'netstat -tln |grep 1080'

SSH的本地轉發

本地轉發,説白了,就是把發到本地的某個端口請求,轉發到遠程的某台機器上面。格式如下:

ssh -L  [本地地址:]本地端口:遠程地址:遠程端口 遠程用户@遠程地址

案例1、在機器B(192.168.13.149)上面訪問機器A(192.168.13.148)的服務。

現在,我們在A機器上面,啟動了Nginx服務,如下:

我們希望B機器也能夠這樣使用A機器上面的服務。需要把B機器上面80端口請求,轉發到A機器上面。目前在B機器這樣執行,是報錯的,如下:

需要在B機器上面,執行如下代碼:

ssh -f -N -L 127.0.01:80:192.168.13.148:80 [email protected]

然後,在B機器上面,訪問A機器的服務,就想訪問自身的服務一樣。

SSH的遠程轉發

遠程轉發,即把發給遠程機器的某個端口請求,轉發到本地的機器上面。格式如下:

ssh -R [遠程地址:]遠程端口:本地地址:本地端口 遠程用户@遠程地址

在上面的案例中,我們也可以通過遠程轉發來實現。即在A機器上面執行如下代碼:

sudo ssh -f -N -R 8081:127.0.0.1:80 [email protected]

我們監聽了B機器的8081端口,把該端口的請求,轉發到A機器上面。

可以在B機器上面看到,我們的監聽,如下:

此時,執行如下命令,就會被轉發到A機器的127.0.0.1的80端口,如下:

利用遠程轉發,實現代理功能

目前B機器,只能在自己127.0.0.1的80端口監聽並轉發,如何讓B機器作為代理,轉發其他機器的請求到A機器上面呢?比如,現在有一台機器C(192.168.13.143),C不能訪問A,但是能夠訪問B。如何讓C利用B來訪問A呢?

此時,需要將B的監聽,由127.0.0.1:8081,改為0:0.0.0:8081,修改sshd的配置/etc/ssh/sshd_config。

vim /etc/ssh/sshd_config 如果有GatewayPorts no改為GatewayPorts yes
沒有,添加即可然後重啟sshd
sudo service sshd restart

然後重新,設置動態轉發,如下:

ssh -f -g  -N -R 8081:127.0.0.1:80 [email protected]

可以看到,此時B機器,已經監聽了0:0.0.0:8081

在C機器上面,我們通過curl模擬請求,利用B機器做代理,如下:

curl -x 192.168.13.149:8081 127.0.0.1

當然,如果還有其他機器,也可以使用類似的方式,來請求A機器。

SSH 的動態轉發

對於SSH的本地轉發和遠程轉發,都需要將本地端口和遠程端口一一綁定,格式如下:

ssh -D [本地地址:]本地端口號 遠程用户@遠程地址

比如,把發到B機器上面的請求,都轉發到A機器上面,讓A機器去執行請求。

SSH存在的問題

如果有人截獲了登錄請求,然後冒充遠程主機,將偽造的公鑰發給用户,那麼用户很難辨別真偽。因為不像https協議,SSH協議的公鑰是沒有證書中心(CA)公證的,也就是説,都是自己簽發的。

可以設想,如果攻擊者插在用户與遠程主機之間(比如在公共的wifi區域),用偽造的公鑰,獲取用户的登錄密碼。再用這個密碼登錄遠程主機,那麼SSH的安全機制就蕩然無存了。這種風險就是著名的"中間人攻擊"(Man-in-the-middle attack)。

注:整理於網絡素材,版權歸原作者所有。如需轉載請註明出自:民工哥技術之路公眾號。

推薦閲讀   點擊標題可跳轉

領導:誰再用 Redis 過期監聽實現關閉訂單,立馬滾蛋!

Linux 安裝包管理工具中文手冊!別再説不會了,丟人

刪除所有用户數據!永久關閉

為了家庭和生活,我妥協了,從華為離職

Linux 下讓工作效率翻倍的 4 個實用技巧

Jenkins+Docker 實現一鍵自動化部署項目!少走坑路

誰再説不會 K8S 高可用部署,就把這個給他甩過去!

轉發 關注

點亮下方“在看”圖標

更多人看到