利用 SSH 建立 SOCKS Proxy

最近因為疫情又開始 WFH 了。 公司有提供一些 VPN solution 讓員工存取公司內網路, 但有一些架在 public cloud 上的服務後台因為有擋來源 IP,無法在家直接存取。

這時候 SSH 內建的 SOCKS proxy server 功能就可以派上用場了!

SSH 可以在建立連線時,一併在本機端開出一個 SOCKS (version 4 and 5) 的 server, 接下來任何應用程式都可以將任意的 TCP 連線透過這個 SOCKS server,轉送到 SSH server 後再與目標站台連線。 因為大家一定在公司裡有台可以 SSH 的機器(?),於是這種限制公司 IP 的管理後台就可以順利存取。 :D

使用方式很簡單,SSH 連線時多下參數即可。

1
ssh "target-machine" -D "localhost:1080" -N
  • -D localhost:1080: 決定要開在 local 的 SOCKS port,RFC 建議是 1080
  • -N: 如果不需要開一個 shell,只是要 SOCKS proxy 功能,那可以多帶此參數

Note: SSH 有支援 SOCKS5 (可做 IPv6 proxy) 但不支援 authentication,不過因為 SOCKS server 可以如上述設定只開在 localhost 上,所以沒麼問題。

接著我們就可以設定 OS 層級或是 application 層級的 proxy 設定來使用這個 proxy 了! 以我一開始遇到的問題來說,通常我會多開一個 Firefox 並設定使用 proxy 來存取公司的各種管理後台。 這樣就可以保持其他網路流量還是直接往外打,不需要過 proxy。 :D

若要快速啟動 proxy,可以使用 Windows Terminal 並設定一個 profile,執行上述 SSH 指令。

PuTTY 作為 Windows 上最多人使用的 SSH client,也有支援 SOCKS proxy 功能, 詳見: How To Set up a SOCKS Proxy Using Putty & SSH - Security Musings

Reference

在 Ubuntu Server 上自動啟用 SSH Agent

當 我們的 SSH private key 有上 pass phrase 保護時, SSH agent 是個方便的好東西。因為它可以幫我們記住已經解鎖過的 private key。

可惜的是,Ubuntu server 18.04 的環境預設並不會幫你生一個 SSH agent 出來。

本文章記錄一點摸索的過程…

系統自帶的 SSH agent systemd unit

我看別人的 Ubuntu 登入之後就有 SSH agent 可以用啊?

很可惜的是我的環境沒有。研究一陣子之後,發現 SSH agent 應是在有圖形介面 的情況下才會被自動帶起。

dpkg --listfiles openssh-client 下可看到幾個重要的檔案

  • /usr/lib/openssh/launch-agent
  • /usr/lib/systemd/user/ssh-agent.service
  • /usr/lib/systemd/user/graphical-session-pre.target.wants/ssh-agent.service

看了這幾個檔案的內容後可得知

  1. 這是設計給圖形介面的登入 session 使用的 service
  2. 即使想要直接 enable ssh-agent.service 也無法,因為裡面沒有寫任何的 [Install] 參數

自行撰寫並啟用一個 SSH agent 服務

為了解決沒有 SSH agent 的問題,我們可以自己寫一個 systemd 的 user service, 讓系統在發現我登入之後,自動幫我把 SSH agent 拉起來。

首先編輯 ~/.local/share/systemd/user/ssh-agent.service (參考 man systemd.unit 此為預設的 user unit 路徑)

1
2
3
4
5
6
7
8
9
[Unit]
Description=SSH authentication agent

[Service]
ExecStart=/usr/bin/ssh-agent -a %t/ssh-agent.socket -D
Type=simple

[Install]
WantedBy=default.target

注意 ssh-agent-D 參數與 Type=simple 設定。

接著執行 systemctl --user enable ssh-agent.service。 這一步會在 .config/systemd/user/default.target.wants 資料夾下創出一個 symbolic link, 連回剛剛我們寫的 service file,表示要在登入時自動啟用此 unit。

接著重新登入該機器,應該就可以看到一個 ssh-agent process 跑起來了。

設定 SSH agent 所需的的環境變數

雖然 SSH agent 起來了,但此時若下 ssh-add -L 依然會發現無法連上 SSH agent。

Could not open a connection to your authentication agent.

這是因為 ssh 以及 ssh-add 等工具預設都是看 SSH_AUTH_SOCK 環境變數來得知 要透過哪個 Unix socket 與 agent 溝通。

為了處理此問題,我們需在 ~/.profile 內加入一行環境變數設定,確保在登入時能自動設定完成。

1
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"

註: $XDG_RUNTIME_DIR/ssh-agent.socket 與前述 unit file 內的 -a %t/ssh-agent.socket 對應。詳細可參考 man systemd.unit

下次登入重新讀取 profile 之後即可正常使用 SSH agent 囉。 :D

Alternative Solution

尋找解決方式的過程中,注意到了一些解法,透過純 shell script 的方式處理重複登入的問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SSH_ENV="$HOME/.ssh/environment"

function start_agent {
/usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
chmod 600 "${SSH_ENV}"
. "${SSH_ENV}" > /dev/null
/usr/bin/ssh-add;
}

if [ -f "${SSH_ENV}" ]; then
. "${SSH_ENV}" > /dev/null
ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
start_agent;
}
else
start_agent;
fi

若不考慮 race condition,該作法其實也很值得參考。可以在沒有 systemd 輔助的的生態系底下使用。

雜談

看 systemd 的文件時,發現 systemd 的 user mode 會非常遵守 XDG_ 系列的環境變數。 不過因為我們是在 Ubuntu server edition 下,所以大部分都略過不看。 :D

XDG_RUNTIME_DIR 這個變數除外,此變數雖然也是由 XDG Base Directory Specification 所規範,但在一般 Linux 發行版,此變數是由 pam_systemd 直接維護的。所以即使是在 server 環境也會有此變數存在。

References