跳轉到內容

OpenSSH/Cookbook/代理和跳躍主機

100% developed
來自 Wikibooks,開放世界中的開放書籍

代理是轉發來自客戶端到其他伺服器的請求的中間人。效能改進、負載均衡、安全性或訪問控制是使用代理的一些原因。

跳躍主機 - 透過一個或多個閘道器

[編輯 | 編輯原始碼]

可以透過一個或多箇中間人連線到另一個主機,以便客戶端可以表現得好像連線是直接的。

主要方法是使用 SSH 連線透過一個或多個跳躍主機轉發 SSH 協議,使用ProxyJump指令,到目標目標主機上執行的 SSH 伺服器。這是最安全的方法,因為加密是端到端的。除了任何其他進行的加密之外,鏈的端點相互加密和解密彼此的流量。因此,透過中間主機傳遞的流量始終被加密。但是,如果中間主機拒絕埠轉發,則不能使用此方法。

使用ProxyCommand選項將 Netcat 作為鏈中的最後一個呼叫是針對非常舊的客戶端的此方法的變體。SSH 協議由nc而不是ssh轉發。還必須注意在 SSH 連線鏈中使用者名稱是否從主機到主機發生變化。過時的netcat方法不允許更改使用者名稱。其他方法可以。

當埠轉發可用時,最簡單的方法是在配置檔案中使用ProxyJump或在執行時引數中使用-J-J使用示例為

$ ssh -J firewall.example.org:22 server2.example.org

ProxyJump指令(-J)非常有用,它有一個完整的子部分下面

在舊版本中,-J不可用。在這種情況下,最安全、最直接的方法是使用ssh(1)的 stdio 轉發(-W)模式來“反彈”透過中間主機的連線。

$ ssh -o ProxyCommand="ssh -W %h:%p firewall.example.org" server2.example.org

這種方法支援埠轉發,無需其他技巧。

更舊的客戶端不支援-W選項。在這種情況下,可以使用ssh -tt。這強制分配 TTY 並按原樣傳遞 SSH 流量,儘管這不太安全。要透過跳躍主機firewall連線到server2

$ ssh -tt firewall.example.com ssh -tt server2.example.org

該示例開啟到遠端機器的 SSH 會話。您還可以傳遞命令。例如,要使用screen重新附加到遠端螢幕會話,您可以執行以下操作

$ ssh -tt firewall.example.com ssh -tt server2.example.org screen -dR

鏈可以任意長,並不侷限於只有兩個主機。這種方法相對於使用-W-J在網路層進行 stdio 轉發的缺點是,您的會話、任何轉發的代理、X11 伺服器和套接字都暴露給了中間主機。

使用 ProxyJump 透過一個或多個閘道器

[編輯 | 編輯原始碼]

從 2016 年 8 月釋出的 OpenSSH 7.3[1]開始,透過一個或多個跳躍主機最簡單的方法是在ssh_config(5)中使用ProxyJump指令。

Host server2
        HostName 192.168.5.38
        ProxyJump user1@jumphost1.example.org:22
        User fred

可以將多個跳躍主機指定為逗號分隔的列表。主機將按照列出的順序訪問。

Host server3
        HostName 192.168.5.38
        ProxyJump user1@jumphost1.example.org:22,user2@jumphost2.example.org:2222
        User fred

它在用作執行時引數時也有-J的快捷方式。

$ ssh -J user1@jumphost1.example.org:22 fred@192.168.5.38

多個跳躍主機可以以相同的方式連結。

$ ssh -J user1@jumphost1.example.org:22,user2@jumphost2.example.org:2222 fred@192.168.5.38

在同一主機配置中,無法同時使用ProxyJumpProxyCommand指令。找到第一個指令,然後阻塞另一個指令。

穿過具有多個 RDomain/路由表的跳躍主機

[編輯 | 編輯原始碼]

當穿過一個跳躍主機時,它的相關介面每個都在不同的rdomain(4)上,則需要手動操作路由表。具體來說,這意味著回到一種舊的傳輸方法,該方法依賴於netcat並使用ProxyCommand而不是ProxyJump,以便可以在中間新增route(8)。以下是用 run time 引數透過 rdomain 1 進行傳輸的示例

$ ssh -o ProxyCommand="ssh user1@jumphost.example.org route -T 1 exec nc %h %p" fred@server.example.org

可以透過將該配置新增到客戶端配置檔案ssh_config(5)中使其保持永續性

Host jump jumphost.example.org
        HostName jumphost.example.org
        User user1
        IdentitiesOnly yes
        IdentityFile ~/.ssh/jump_key

Host server server.example.com
        HostName 10.100.200.44
        User fred
        IdentitiesOnly yes
        IdentityFile ~/.ssh/inside_key
        ProxyCommand ssh jump route -T 1 exec nc %h %p

使用這些設定,只需使用ssh server行就可以透過跳躍主機連線到 LAN 上的主機,所有設定都將被使用。

否則,建議的方式是使用ProxyJump。有關使用舊的ProxyCommand指令的更多資訊,請參見下面的部分帶有 Netcat 的 ProxyCommand

跳躍主機的條件使用

[編輯 | 編輯原始碼]

可以使用Match exec來選擇困難的模式或進行其他複雜的決策,例如客戶端連線的網路或可用網路的網路連線。下面是兩種情況,說明如何在自動選擇使用ProxyJump透過中間主機連線時使用它。第一個示例適用於當連線到僅有 IPv6[2] [3]的遠端機器時沒有本地 IPv6 連線的情況。第二種情況[4]適用於目標機器在另一個網路上的情況。

Match host ipv6only.example.org
        User fred

Match host ipv6only.example.org !exec "route -n get -inet6 %h"
        ProxyJump dualstack.example.org

使用這些設定,到ipv6only.example.org機器的連線將僅在無法透過 IPv6 直接訪問時透過跳躍主機進行。

在 GNU/Linux 系統上,該功能將更加複雜。

Match host ipv6only.example.org
        User fred

Match host ipv6only.example.org !exec "/sbin/ip route get $(host -t AAAA %h | sed 's/^.* //')"
        ProxyJump dualstack.example.org

指令碼使用$SHELL環境變數中命名的 shell 呼叫。請記住,配置指令是根據第一個匹配的指令應用的。因此,特定規則放在頂部附近,更通用的規則放在末尾。

另一種方法是使用nc(1)實用程式直接檢查目標主機在相關埠的連線性。

Match !host jumphost.example.org !exec "nc -z -w 1 %h %p"
        ProxyJump jumphost.example.org

由於以上假設只使用一個跳躍主機,因此它可以與其他Match條件結合使用,以提高精度。

Match host 192.168.1.* !host jumphostone.example.org !exec "nc -z -w 1 %h %p"
        ProxyJump jumphostone.example.org

Match host 192.168.2.* !host jumphosttwo.example.org !exec "nc -z -w 1 %h %p"
        ProxyJump jumphosttwo.example.org

這將捕獲所有連線到網路 192.168.1.* 的連線(如果沒有直接連線),並將它們透過第一個跳躍主機發送。同樣,如果不存在直接連線,它也會將所有連線到網路 192.168.2.* 的連線透過第二個跳躍主機發送。但是,如果存在直接連線,客戶端將無需跳躍主機即可繼續。

當然,如果客戶端機器在具有相同編號的多個不同網路之間移動,則測試將更加複雜。

使用位於跳躍主機後面的規範主機名

[edit | edit source]

一些區域網 (LAN) 擁有自己的內部域名服務,以分配自己的主機名。這些名稱無法從 LAN 外部訪問。因此,無法從外部使用-JProxyJump選項,因為客戶端將無法在跳躍主機另一側的 LAN 上查詢名稱。但是,跳躍主機本身可以查詢這些名稱,因此可以使用ProxyCommand選項來呼叫跳躍主機上的 SSH 客戶端,並使用其功能在 LAN 上查詢名稱。

在以下序列中,顯示了內部主機缺少外部 DNS 記錄。然後,使用跳躍主機的 SSH 客戶端和-W選項透過跳躍主機連線到內部主機。金鑰或證書是推薦的連線方式,但本例中使用密碼,以便更清楚地顯示各個階段。

$ host fuloong04.localnet.lan
Host fuloong04.localnet.lan not found: 3(NXDOMAIN)

$ host fuloong04
Host fuloong04 not found: 2(SERVFAIL)

$ ssh -o ProxyCommand="ssh -W fuloong04.localnet.lan:22 jumphost.example.org" fred@fuloong04
fred@jumphost.example.org's password: 
fred@fuloong04's password: 
Last login: Sun May 24 04:06:21 2020 from 203.0.113.131
OpenBSD 6.7-current (GENERIC) #44: Sun May 24 04:06:31 MDT 2020

$ host fuloong04.localnet.lan
fuloong04.localnet.lan has address 10.10.10.193

-W選項確保連線透過安全通道轉發,並且只是透過跳躍主機而不會被解密。跳躍主機必須既能夠為 LAN 名稱執行 DNS 查詢,又能夠使用 SSH 客戶端。

以下是客戶端收集主機金鑰並詢問其資訊的初始連線的外觀。

$ ssh -o ProxyCommand="ssh -W fuloong04.localnet.lan:22 jumphost.example.org" fred@fuloong04
fred@jumphost.example.org's password: 
The authenticity of host 'fuloong04 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:TGH6fbXRswaP7iR1OBrJuhJ+c1JiEvvc2GJ2krqaJy4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'fuloong04' (ECDSA) to the list of known hosts.
fred@fuloong04's password: 
Last login: Thu May  7 21:06:18 2020 from 203.0.113.131
OpenBSD 6.7-current (GENERIC) #44: Sun May 24 04:06:21 MDT 2020

請注意,雖然名稱必須出現在通常的位置,但它只用於標識要與連線關聯的主機金鑰。在初始連線之後,將在known_hosts中為作為目標給出的名稱fuloong04(而不是ProxyCommand選項中給出的名稱)儲存主機金鑰。

$ ssh-keygen -F fuloong04
# Host fuloong04 found: line 55 
fuloong04 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKwZOXY7wP1lCvlv5YC64QvWPxSrhCa0Dn+pK4b97jjeUqGy/wII5zCnuv6WZKCEjSQnKFP+8K9cmSGnIvUisPg= 

$ ssh-keygen -F fuloong04.localnet.lan

目標主機名僅用於標識預期使用的主機金鑰。它與ProxyCommand選項中傳遞的實際主機名或地址無關,甚至可以是隨機字串。上面fuloong04是單獨使用的,但同樣,它可以是任何隨機字串,甚至可以透過使用HostKeyAlias選項來覆蓋。

通常,這些 SSH 選項可以記錄在ssh_config(5)中的永久快捷方式中,以減少輸入工作量並避免錯誤。

透過跳躍主機的舊方法

[edit | edit source]
關於舊方法的說明:這些舊方法已棄用,因為 OpenSSH 的新版本提供了更輕鬆的透過跳躍主機的途徑。請參閱上面“使用 ProxyJump 透過一個或多個閘道器”部分。但是,各種 GNU/Linux 發行版的某些長期支援版本可能會故意保留 OpenSSH 的非常舊的版本。因此,雖然不太可能遇到這些版本,但在未來幾年內仍然可能需要使用這些方法。

在 OpenSSH 的舊版本(特別是 7.2 及更早版本)中,透過一個或多個閘道器更加複雜,需要使用 stdio 轉發或在 5.4 之前使用netcat(1)實用程式。

舊方法:使用 stdio 轉發(Netcat 模式)透過閘道器

[edit | edit source]

在 OpenSSH 5.4[5]到 7.2(含)之間,'netcat 模式' 可以將客戶端上的 stdio 連線到伺服器上轉發的單個埠。這也可以用於使用ssh(1)連線,但它需要ProxyCommand選項作為執行時引數或~/.ssh/config的一部分。但是,它不再需要在中介機器上安裝netcat(1)。以下是如何在執行時引數中使用它的示例。

$ ssh -o ProxyCommand="ssh -W %h:%p jumphost.example.org" server.example.org

在該示例中,身份驗證將發生兩次,第一次在跳躍主機上,然後在最終主機上,它將啟動一個 shell。

如果在配置檔案中標識了閘道器,語法相同。ssh(1)將從配置檔案中擴充套件閘道器和目標的完整名稱。以下允許透過在終端中輸入ssh server來訪問目標主機。

Host server
        Hostname server.example.org
        ProxyCommand ssh -W %h:%p jumphost.example.org

SFTP 可以執行相同的操作。這裡,透過輸入sftp sftpserver可以訪問目標 SFTP 伺服器,配置檔案將處理其餘操作。如果最終主機金鑰混淆,則需要新增HostKeyAlias來明確命名將用於標識目標系統的金鑰。

Host sftpserver
        HostName sftpserver.example.org
        HostKeyAlias sftpserver.example.org
        ProxyCommand ssh -W %h:%p jumphost.example.org

可以將閘道器的金鑰新增到正在執行的 ssh 代理,或者在配置檔案中指定它。User選項指的是目標上的使用者名稱。如果使用者名稱在目標和源機器上相同,則不需要使用它。如果閘道器上的使用者名稱不同,則可以在ProxyCommand選項中使用-l選項。這裡,本地機器上的使用者 'fred' 以 'fred2' 的身份登入閘道器,並以 'fred3' 的身份登入目標伺服器。

Host server
        HostName server.example.org
        User fred3
        ProxyCommand ssh -l fred2 -i /home/fred/.ssh/rsa_key -W %h:%p jumphost.example.org

如果閘道器和目標都使用金鑰,則配置中的IdentityFile選項用於指向閘道器的私鑰,命令列上指定的IdentityFile選項指向目標的私鑰。

Host jump
        HostName server.example.org
        IdentityFile /home/fred/.ssh/rsa_key_2
        ProxyCommand ssh -i /home/fred/.ssh/rsa_key -W %h:%p jumphost.example.org

OpenSSH 5.4 之前的舊方法使用 netcat,nc(1)

Host server
        Hostname server.example.org
        ProxyCommand ssh jumphost.example.org nc %h %p

但現在不應該再使用它,而應該使用-W提供的 netcat 模式。新方法在任何機器上都不需要netcat

舊方法:使用 stdio 轉發遞迴連結閘道器
[edit | edit source]

執行此操作的簡單方法是使用ProxyJump,該方法從 OpenSSH 7.3 開始可用,如上所述。對於舊版本,如果路由始終以相同的順序包含相同的主機,則可以在配置檔案中新增一個簡單的鏈。這裡,三個主機與目標連結,目標被賦予快捷方式machine3

Host machine1
        Hostname server.example.org
        User fred
        IdentityFile /home/fred/.ssh/machine1_e25519
        Port 2222

Host machine2
        Hostname 192.168.15.21
        User fred
        IdentityFile /home/fred/.ssh/machine2_e25519
        Port 2222
        ProxyCommand ssh -W %h:%p machine1

Host machine3
        Hostname 10.42.0.144
        User fred
        IdentityFile /home/fred/.ssh/machine3_e25519
        Port 2222
        ProxyCommand ssh -W %h:%p machine2

因此,可以透過單行訪問鏈中的任何機器。例如,可以透過ssh machine3訪問最終機器並正常使用它。這包括埠轉發和任何其他功能。

每個Host指令只需要hostname,以及對於第二臺及後續主機,ProxyCommand。如果未使用金鑰,則不需要IdentityFile。如果所有主機的使用者都相同,則可以跳過它。如果埠是預設埠,則可以跳過Port。如果在代理中同時使用多個金鑰,並且在客戶端端出現“身份驗證過多”的錯誤,則可能需要將IdentitiesOnly yes新增到每個主機的配置中。

舊方法:遞迴連結任意數量的主機
[edit | edit source]

同樣,執行此操作的簡單方法是使用ProxyJump,該方法從 OpenSSH 7.3 開始可用。對於舊版本,可以使配置更抽象,並允許透過任意數量的閘道器。您可以使用-l設定使用者名稱,但該使用者名稱將用於您連線或透過的所有主機。

Host */* 
        ProxyCommand ssh %r@$(dirname %h) -W $(basename %h):%p

這樣,主機之間用斜線 (/) 分隔,數量可以是任意的[6]

$ ssh host1/host2/host3/host4

使用斜線作為分隔符會有一些限制,就像使用其他符號一樣。但是,它允許使用 dirname(1)basename(1) 處理主機名。

以下配置使用 sed(1) 允許使用不同的埠號和使用者名稱,使用加號 (+) 作為主機分隔符,冒號 (:) 作為埠分隔符,百分號 (%) 作為使用者名稱分隔符。基本結構為 ssh -W $() $(),其中 %h 被替換為目標主機名。

Host *+*
        ProxyCommand ssh -W $(echo %h | sed 's/^.*+//;s/^\([^:]*$\)/\1:22/') $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:\([^:+]*\)$/ -p \1/')

埠可以省略,預設埠為 22,也可以用冒號 (:) 分隔非標準埠[7]

$ ssh host1+host2:2022+host3:2224

原樣,冒號會干擾 sftp(1),因此上述配置只能在使用標準埠的情況下才能與它一起使用。如果需要在非標準埠上使用 sftp(1),那麼可以使用其他分隔符,比如下劃線 (_) 來進行配置。

可以使用指定的定界符為給定主機指定除最後一個以外的任何使用者名稱,在上面的例子中,它是一個百分號 (%)。目標主機的使用者名稱使用 -l 指定,其他所有使用者名稱都可以用分隔符與它們對應的主機名連線起來。

$ ssh -l user3 user1%host1+user2%host2+host3

如果指定了使用者名稱,根據分隔符的不同,ssh(1) 可能無法將最終主機與 IP 號碼和 known_hosts 中的金鑰指紋匹配。在這種情況下,它將在每次建立連線時要求驗證,但如果使用等號 (=) 或百分號 (%),則這不會有問題。

如果要使用金鑰,那麼將它們載入到代理中,然後如果使用 -A 選項進行代理轉發,客戶端會自動識別它們。但是,如果可以使用 ProxyJump 選項 (-J),則不需要代理轉發。許多人認為這實際上是一個安全漏洞,是一個普遍的錯誤特徵。因此,如果可以使用 ProxyJump 選項,請使用它。此外,由於伺服器上 MaxAuthTries 的預設值為 6,因此通常在代理中使用金鑰會將金鑰或跳躍的次數限制為 6 次,伺服器端日誌將在超過一半的時間後被觸發。

舊:帶有 Netcat 的 ProxyCommand

[edit | edit source]

另一種方法,適用於 OpenSSH 5.3 及更早版本,是使用 ProxyCommand 配置指令和 netcat。工具 nc(1) 用於直接讀取和寫入網路連線。它可以用來將連線傳遞到第二臺機器。在這種情況下,destination 是透過中間機器 jumphost 訪問的另一臺伺服器。

$ ssh -o 'HostKeyAlias=destination.example.edu' \
        -o 'ProxyCommand ssh %h nc destination.example.edu 22' \
        jumphost.example.org

金鑰和不同的登入名也可以使用。使用 ProxyCommandssh(1) 將首先連線到 jumphost,然後從那裡連線到 destination.example.edu。需要使用 HostKeyAlias 指令查詢 destination.example.edu 的正確金鑰,因為如果沒有它,將會嘗試使用 jumphost 的金鑰,這當然會失敗,除非兩個系統具有相同的主機金鑰。在這個例子中,帳戶 user2 存在於 jumphost 上。

$ ssh -o 'HostKeyAlias=destination.example.edu' \
        -o 'ProxyCommand ssh -i jumphost_key-rsa -l user2 %h \
                nc destination.example.edu 22' \
        jumphost.example.org

也可以透過編輯 ssh_config 來使這種安排更加持久,並減少輸入。這樣一來,只要在目標主機名完整的情況下使用它,就會透過 jumphost 連線到 destination

Host destination.example.org
        ProxyCommand ssh %r@jumphost.example.org nc %h %p

這裡透過 jumphost 連線到 destination,使用快捷名稱 'jump'。

Host jump
        HostKeyAlias destination.example.org
        Hostname jumphost.example.org
        User fred
        ProxyCommand ssh %h nc destination.example.org 22

可以更通用地進行操作。第一個段落用於確保堡壘機或跳躍主機不會自身迴圈。第二個段落使用通用規則,透過堡壘機或跳躍主機來訪問以 .example.edu 結尾的所有域名,或訪問快捷名稱 my-private-host

Host jumphost.example.org
        ProxyCommand none

Host *.example.org my-private-host
        ProxyCommand ssh -l %r jumphost.example.org nc %h %p

sftp(1) 也可以這樣做,透過將引數傳遞給 ssh(1)。以下是一個簡單的例子,使用 sftp(1),其中 jumphost 是用於連線到 machine2 的中間主機。使用者名稱對兩個主機都相同。

$ sftp -o 'HostKeyAlias=machine2.example.edu' \
        -o 'ProxyCommand=ssh %h nc machine2.example.edu 22' \
        fred@jumphost.example.edu

以下是一個更復雜的例子,使用 jumphost 的金鑰,但使用基於密碼的常規登入方式登入 SFTP 伺服器。

$ sftp -o 'HostKeyAlias=destination.example.edu' \
        -o 'ProxyCommand ssh -i /home/fred/.ssh/jumphost_rsa \
        -l user2 jumphost.example.edu nc destination.example.edu 22'   \
        jumphost.example.edu

如果兩個機器上的使用者帳戶名稱不同,也是可以的。這裡,'fred' 是第二臺機器的帳戶,它是最終的目標。使用者 'user2' 是中間機器或跳躍主機的帳戶。

$ ssh -i fred -i /home/fred/.ssh/destination_rsa \
       -o 'HostKeyAlias destination.example.org' \
       -o 'ProxyCommand ssh -i /home/fred/.ssh/jumphost_rsa \
              -l user2 %h nc destination.example.org 22'
       jumphost.example.org

如果可能,請升級或回溯到 OpenSSH 的更新版本,以便可以使用 ProxyJump-J 選項。

舊:沒有 Netcat 的 ProxyCommand,使用 Bash 的 /dev/tcp/ 偽裝置
[edit | edit source]

在沒有 nc(1) 的 GNU/Linux 跳躍主機上,但擁有 Bash shell 直譯器,偽裝置 /dev/tcp[8] 可以是另一種選擇。它利用了一些內建的 Bash 功能,以及 cat(1) 工具

$ ssh -o HostKeyAlias=server.example.org \
	-o ProxyCommand="ssh -l fred %h 'exec 3<>/dev/tcp/server.example.com/22; cat <&3 & cat >&3;kill $!'" fred@jumphost.example.com

需要將 HostKeyAlias 設定為目標主機名或 IP 地址,因為 SSH 連線只是透過跳躍主機進行,需要期望正確的金鑰。

此方法的主要前提是在具有 Bash 作為預設 shell 的 GNU/Linux 跳躍主機上擁有一個登入 shell。如果使用其他 shell,比如 POSIX shell,那麼偽裝置將不存在,而是會發生錯誤。

sh: 1: cannot create /dev/tcp/server.example.com/22: Directory nonexistent

使用 ksh(1),也會發生類似的錯誤。

ksh: cannot create /dev/tcp/server.example.com/22: No such file or directory

zsh(1) shell 也會缺少 /dev/tcp 偽裝置,但會產生不同的錯誤。

zsh:1: no such file or directory: /dev/tcp/server.example.com/22
zsh:1: 3: bad file descriptor
zsh:1: 3: bad file descriptor
zsh:kill:1: not enough arguments

因此,這又是特定於 bash(1) shell 的一個過程。此外,此方法特定於 GNU/Linux 發行版上的 Bash,即使 bash(1) 存在,也不會在任何 BSD 或 Mac OS 上作為跳躍主機提供。

可以使用 '-E 選項捕獲來自客戶端視角的連線過程的完整詳細資訊到日誌檔案,同時提高除錯資訊的詳細程度。

$ ssh -vvv -E /tmp/ssh.dev.tcp.log \
	-o HostKeyAlias=server.example.org \
	-o ProxyCommand="ssh -l fred %h 'exec 3<>/dev/tcp/server.example.com/22; cat <&3 & cat >&3;kill $!'" fred@jumphost.example.com

此偽裝置方法主要作為一種奇特的方式包含在此處,但在某些極端情況下可以作為最後的手段。如前所述,所有最近部署的 OpenSSH 都將具有 -J 選項,用於 ProxyJump

透過一個或多箇中間主機進行埠轉發

[edit | edit source]

隧道,也稱為埠轉發,是指將一臺機器上的埠對映到另一臺機器上的埠連線。這樣一來,就可以像訪問本地服務一樣訪問遠端服務。或者,在反向埠轉發的情況下,反過來。轉發可以從一臺機器直接轉發到另一臺機器,也可以透過中間機器進行轉發。

如果可以使用,ProxyJump 選項是更優的選擇。它與埠轉發一樣容易使用。以下是在 localhostmachine2 之間建立的隧道,該隧道位於跳躍主機後面。

$ ssh -L 8900:localhost:80 -J jumphost.example.org machine2

因此,localhost 上的埠 8900 將實際上是到 machine2 上埠 80 的隧道。它可以像這樣簡單。與通常的 ProxyJump 選項一樣,跳躍主機可以透過用逗號連線起來進行鏈式連線。

$ ssh -L 8900:localhost:80 -J jumphost1.example.org,jumphost2.localnet.lan machine2

如果需要,也可以指定備用帳戶和埠。這種方法在使用基於金鑰或證書的身份驗證時效果最好。可以以這種方式使用所有選項,例如 -X 用於 X11 轉發。-D 也是如此,用於建立 SOCKS 代理。

舊:透過單箇中間主機進行埠轉發,不使用 ProxyJump

[edit | edit source]

下面,我們將在 localhostmachine2 之間建立一條隧道,machine2 位於防火牆 machine1 後面。隧道將透過 machine1,它可以公開訪問,並且也可以訪問 machine2

$ ssh -L 2222:machine2.example.org:22 machine1.example.org

接下來,連線到隧道將實際上連線到第二臺主機,machine2

$ ssh -p 2222 remoteuser@localhost

以下是在使用 machine1 作為中間機器,使用上述設定,在兩臺主機之間執行 rsync(1) 的示例。

$ rsync -av -e "ssh -p 2222"  /path/to/some/dir/   localhost:/path/to/some/dir/

SOCKS 代理

[edit | edit source]

可以使用 SOCKS 代理透過中間機器進行連線。OpenSSH 目前支援 SOCKS4 和 SOCKS5 代理。SOCKS5[9] 允許應用程式透明地穿越防火牆或其他障礙,並且可以在 GSS-API 的幫助下使用強身份驗證。動態應用程式級埠轉發允許在執行時分配傳出埠,從而在 TCP 會話級別建立代理。

這裡,Web 瀏覽器可以連線到本地主機上埠 3555 的 SOCKS 代理

$ ssh -D 3555 server.example.org

使用 ssh(1) 作為 SOCKS5 代理,或在任何其他使用轉發的場合,可以在一個操作中指定多個埠

$ ssh -D 80 -D 8080 -f -C -q -N fred@server.example.org

您還希望 DNS 請求透過您的代理。例如,在最新版本的 Firefox 中,可能有一個選項“使用 SOCKS v5 時代理 DNS”可供選中。或者在舊版本中,about:config 需要將 network.proxy.socks_remote_dns 設定為 true。但是,在 Chromium[10] 中,您需要在新增兩個執行時選項 --proxy-server--host-resolver-rules 時啟動瀏覽器,以指向代理並告訴瀏覽器不要透過開放網路傳送任何 DNS 請求。

對於支援 SOCKS 代理的其他程式,情況類似。因此,您也可以透過 ssh(1) 對 Samba 進行隧道。

透過跳躍主機只是使用 ProxyJump 選項的問題。

$ ssh -D 8899 -J jumphost.example.org machine2

也可以透過這種方式將多個跳躍主機連結在一起。有關討論和示例,請參見有關此主題的先前部分。

舊的:透過單箇中間主機進行 SOCKS 代理

[編輯 | 編輯原始碼]

如果您想透過中間主機開啟一個 SOCKS 代理,這是可能的。

$ ssh -D 3555 -J user1@middle.example.org user2@server.example.org

在舊的客戶端上,需要一個額外的步驟。

$ ssh -L 8001:localhost:8002 user1@middle.example.org -t ssh -D 8002 user2@server.example.org

在第二個示例中,客戶端將在本地主機的埠 8001 上看到一個 SOCKS 代理,這實際上是與 machine1 的連線,流量最終將透過 machine2 進入和離開網路。本地主機上的埠 8001 連線到 machine1 上的埠 8002,這是一個到 machine2 的 SOCKS 代理。埠號可以根據需要選擇,但轉發特權埠仍然需要 root 許可權。

SSH 透過 Tor

[編輯 | 編輯原始碼]

有兩種方法可以使用 SSH 透過 Tor。一種是透過 Tor 使用客戶端,另一種是將伺服器託管為洋蔥服務。透過 Tor 執行客戶端可以隱藏其來源。在 Tor 後面託管伺服器可以隱藏其位置。這兩種方法可以結合使用。

使用 Netcat 透過 Tor 對 SSH 客戶端進行隧道

[編輯 | 編輯原始碼]

除了使用 ssh(1) 作為 SOCKS 代理外,還可以透過另一個 SOCKS 代理(如 Tor)對 SSH 協議本身進行隧道。它是一種匿名軟體和相應的網路,使用中繼主機來隱藏使用者的定位和網路活動。其體系結構旨在防止監控和流量分析。Tor 可用於在必須隱藏 SSH 客戶端的來源的情況下使用。它還可用於連線到洋蔥服務。不幸的是,透過 Tor 連線通常會以明顯的延遲為代價。

在客戶端看到的端點上,Tor 是一個常規的 SOCKS5 代理,可以像任何其他 SOCKS5 代理一樣使用。所以這是透過 SOCKS 代理對 SSH 進行隧道。如果可用,使用 torsocks 實用程式是執行此操作的最簡單方法,只需在 SSH 命令前面加上它即可:[11]

$ torsocks ssh fred@server.example.org

但是,這也可以透過使用 netcat 來完成。

$ ssh -o ProxyCommand="nc -X 5 -x localhost:9050 %h %p" fred@server.example.org

在嘗試這種連線時,非常重要的是它不會洩露資訊。尤其是,DNS 查詢應該透過 Tor 進行,而不是由客戶端本身進行。確保如果使用 VerifyHostKeyDNS,則將其設定為“no”。預設值為“no”,但請檢查以確保。它可以作為執行時引數傳遞以消除任何疑慮或不確定性。

$ ssh -o VerifyHostKeyDNS=no -o ProxyCommand="nc -X 5 -x localhost:9050 %h %p" server.example.org

使用 netcat-openbsd nc(1) 軟體包,這似乎不會洩露任何 DNS 資訊。其他 netcat 軟體包可能相同也可能不同。目前也不清楚此方法是否還有其他可能洩露資訊的方式。YMMV。

由於這些方法透過 Tor 代理,因此它們都將允許連線到洋蔥服務。您可以配置 SSH 自動透過 Tor 連線到這些服務,而不會影響其他型別的連線。可以在 ssh_config~/.ssh/config 中新增類似以下內容的條目。

Host *.onion
        VerifyHostKeyDNS no
        ProxyCommand nc -x localhost:9050 -X 5 %h %p

您可以在任何 Host 宣告之前進一步新增 CanonicalizeHostname yes,以便如果您為洋蔥服務提供了一個暱稱,SSH 將在確定主機名是 .onion 地址後應用此配置。

提供 SSH 作為洋蔥服務

[編輯 | 編輯原始碼]

SSH 可以從 .onion 地址提供服務,為客戶端和在一定程度上為伺服器提供匿名性和隱私。兩者都不會知道對方的位置,儘管必須採取很多額外的預防措施才能接近匿名化伺服器本身,並且至少需要信任使用者。

透過 Tor 使 SSH 可用的第一步是設定 sshd_config(5),以便 SSH 服務僅監聽 localhost 地址上的 localhost 地址。

ListenAddress 127.0.0.1:22

如果要提供多個埠,可以使用多個 ListenAddress 指令。但是,提供的任何 ListenAddress 指令都應該只繫結到 127.0.0.0/8 網路中的地址或 IPv6 等效地址。監聽任何 WAN 或 LAN 地址將透過允許從 SSH 伺服器的公鑰識別它來破壞 Tor 的匿名性。

透過 Tor 提供 SSH 的下一步是設定一個 Tor 客戶端,並將隱藏服務轉發到本地 SSH 伺服器。按照 Tor 專案網站上提供的安裝 Tor 客戶端的說明[12],但如果不需要,則跳過有關 Web 伺服器的部分。新增相應的 HiddenServicePort 指令以匹配地址和 sshd(8) 正在使用的地址。

HiddenServicePort 22 127.0.0.1:22

如果需要,請為其他埠新增其他指令。在較新的 Tor 版本中,可以透過在支援它的版本中將 HiddenServiceVersion 3 新增到 Tor 配置中,來建立 56 個字元的版本 3 洋蔥地址[13]

確保 HiddenServiceDir 指向適合您系統的某個位置。新 SSH 服務的洋蔥地址將位於 HiddenServiceDir 指令指示的目錄內的 hostname 檔案中,並且無論它在網路上的什麼位置或有多少層 NAT 掩蓋它,它都將可訪問。要使用 SSH 服務並驗證它是否確實可以透過 Tor 使用,請參見前面有關使用 Netcat 透過 Tor 使用 SSH 客戶端的部分。

僅僅透過 Tor 使它可用不足以匿名化伺服器。但是,此方法的另一個優勢是 NAT 穿透。如果 SSH 服務位於多層 NAT 後面,那麼將其作為洋蔥服務提供允許無縫地穿過這些層,而無需配置每一層的每個路由器。這樣就無需將反向隧道連線到外部伺服器,並且可以穿過任意數量的 NAT 層,例如現在手機調變解調器中發現的 NAT 層。

透過使用臨時 VPN 的閘道器

[編輯 | 編輯原始碼]

透過在端點上配置網路路由以使用隧道,可以將兩個子網連線到 SSH。結果是 VPN。缺點是需要在兩個主機上都有 root 訪問許可權,或者至少需要對 sudo(8) 訪問 ifconfig(8)route(8)。相關但更有限且更古老的方法(這裡沒有介紹,但不需要在遠端端使用 root)是使用 ssh 建立連線,然後使用 PPP 和 slirp 建立網路。[14]

請注意,真正需要使用 VPN 的情況很少,這並非因為 VPN 非法(事實上,恰恰相反,許多國家的資料保護法規定必須使用 VPN 來保護傳輸中的內容),而是因為 OpenSSH 通常足夠靈活,能夠使用正常的 SSH 方法按需完成大多數常規系統管理員和操作任務。因此,這種 SSH 臨時 VPN 方法僅在極少數情況下需要。

以兩個網路為例。一個網路的地址範圍是 10.0.50.1 到 10.0.50.254。另一個網路的地址範圍是 172.16.99.1 到 172.16.99.254。每個網路都有一臺機器(分別是 10.0.50.1 和 172.16.99.1),它們將充當閘道器。本地機器編號從 3 開始,因為 2 將用於每個區域網上的隧道介面。

              +----10.0.50.1       172.16.99.1----+
              +    10.0.50.2 ===== 172.16.99.2    +
              |                                   |
10.0.50.3-----+                                   +---172.16.99.3
              |                                   |
10.0.50.4-----+                                   +---172.16.99.4
              |                                   |
10.0.50.5-----+                                   +---172.16.99.5
              |                                   |
10.0.50.etc---+                                   +---172.16.99.etc
              |                                   |
10.0.50.254---+                                   +---172.16.99.254

首先,在每臺機器上建立一個 tun 裝置,這是一個用於點對點 IP 隧道的虛擬網路裝置。然後,這兩個閘道器上的 tun 介面透過 SSH 隧道連線。每個 tun 介面都被分配一個 IP 地址。

隧道將機器 10.0.50.1 和 172.16.99.1 連線在一起,並且它們都已連線到各自的區域網 (LAN)。這是一個 VPN,其中客戶端為 10.0.50.0/24,遠端為 172.16.99.0/24。首先,在客戶端上設定

$ ssh -f -w 0:1 192.0.2.15 true
$ ifconfig tun0 10.1.1.1 10.1.1.2 netmask 255.255.255.252
$ route add 172.16.99.0/24 10.1.1.2

在伺服器上

$ ifconfig tun1 10.1.1.2 10.1.1.1 netmask 255.255.255.252
$ route add 10.0.50.0/24 10.1.1.1

臨時 VPN 故障排除

[編輯 | 編輯原始碼]

以下型別的錯誤訊息可能有多種原因

channel 0: open failed: connect failed: open failed
Tunnel forwarding failed

一種可能是伺服器上尚未啟用隧道功能。SSH 伺服器的預設配置已關閉隧道功能,因此必須使用 PermitTunnel 配置指令在嘗試使用臨時 VPN 之前明確啟用它。如果未啟用隧道功能,則在客戶端使用 -w 選項連線時會發生類似上述的錯誤。在這種情況下,解決方案是在伺服器上將 PermitTunnel 設定為 'yes'。

另一種可能是遠端系統或本地系統(或兩者)缺少必要的網路介面偽裝置。之所以會出現這種情況,是因為一個或兩個帳戶都缺乏在執行時建立 tun(4) 裝置的必要許可權。存在多種解決方法,包括使用特權帳戶預先在每個系統上建立 tun(4) 裝置。換句話說,解決方案是手動建立用於隧道的必要網路介面偽裝置。

第三種可能是,一個或兩個帳戶都沒有訪問網路介面偽裝置的適當許可權。檢查並(如果需要)更正組成員身份。

 

參考文獻

  1. "OpenSSH 7.3 釋出說明". OpenSSH. 2016-08-01. 檢索自 2016-08-01.
  2. Peter Hessler (2018-12-04). "phessler". Mastodon. 檢索自 2018-12-05.
  3. Mike Lee Williams (2017-07-13). "使用 Match 在必要時隧道化 SSH 連線". 檢索自 2019-09-05.
  4. Andrew Hewus Fresh (2019-08-25). "afresh1". Mastodon. 檢索自 2019-09-05.
  5. "OpenSSH 5.4 釋出說明". OpenSSH. 2010-04-07. 檢索自 2013-04-19.
  6. Josh Hoblitt (2011-09-03). "遞迴地連結 SSH ProxyCommand". [閱讀本精細材料] 來自 Joshua Hoblitt. 檢索自 2014-02-14.
  7. Mike Hommey (2016-02-08). "SSH 透過跳躍主機,重溫". glandium. 檢索自 2016-02-09.
  8. "關於 ssh ProxyCommand 的一切". Stack Exchange. 2011.
  9. "SOCKS 協議版本 5". IETF. 檢索自 2011-02-17.
  10. "在 Chrome 中配置 SOCKS 代理伺服器". Chromium 專案. 檢索自 2016-12-10.
  11. "透過 Tor 進行 SSH". Tor 專案. 2012-08-28. 檢索自 2013-05-04.
  12. "為 Tor 配置隱藏服務". Tor 專案,Inc. 檢索自 2016-12-09.
  13. "Tor 交會規範 - 版本 3". Tor 專案,Inc. 2015-05-26. 檢索自 2018-12-14.
  14. Jeremy Impson (2008-09-16). "pppsshslirp: 透過 SSH 建立一個 PPP 會話到您沒有 root 許可權的遠端機器". 檢索自 2016-12-10.

 


華夏公益教科書