通過帶有套接字啟動的 systemd 使用者單元的按需 SSH Socks 代理不會按希望重新啟動
為了訪問一個隔離的網路,我使用ssh
-D
socks proxy。為了避免每次我將它們添加到時都必須輸入詳細資訊
~/.ssh/config
:$ awk '/Host socks-proxy/' RS= ~/.ssh/config Host socks-proxy Hostname pcit BatchMode yes RequestTTY no Compression yes DynamicForward localhost:9118
然後我創建了一個systemd-user服務單元定義文件:
$ cat ~/.config/systemd/user/SocksProxy.service [Unit] Description=SocksProxy Over Bridge Host [Service] ExecStart=/usr/bin/ssh -Nk socks-proxy [Install] WantedBy=default.target
我讓守護程序重新載入新的服務定義,啟用新服務,啟動它,檢查它的狀態,並驗證它正在監聽:
$ systemctl --user daemon-reload $ systemctl --user list-unit-files | grep SocksP SocksProxy.service disabled $ systemctl --user enable SocksProxy.service Created symlink from ~/.config/systemd/user/default.target.wants/SocksProxy.service to ~/.config/systemd/user/SocksProxy.service. $ systemctl --user start SocksProxy.service $ systemctl --user status SocksProxy.service ● SocksProxy.service - SocksProxy Over Bridge Host Loaded: loaded (/home/alex/.config/systemd/user/SocksProxy.service; enabled) Active: active (running) since Thu 2017-08-03 10:45:29 CEST; 2s ago Main PID: 26490 (ssh) CGroup: /user.slice/user-1000.slice/user@1000.service/SocksProxy.service └─26490 /usr/bin/ssh -Nk socks-proxy $ netstat -tnlp | grep 118 tcp 0 0 127.0.0.1:9118 0.0.0.0:* LISTEN tcp6 0 0 ::1:9118 :::* LISTEN
這按預期工作。然後我想通過使用systemd socket-activation按需(重新)生成來避免手動啟動服務或使用autossh永久執行它。那沒用,我認為(我的版本)無法接收套接字文件描述符。
ssh
我找到了文件(1、2),以及使用-tool 創建 2 個“包裝器”服務、一個“服務”和一個“套接字”的範例:
systemd-socket-proxyd
$ cat ~/.config/systemd/user/SocksProxyHelper.socket [Unit] Description=On Demand Socks proxy into Work [Socket] ListenStream=8118 #BindToDevice=lo #Accept=yes [Install] WantedBy=sockets.target $ cat ~/.config/systemd/user/SocksProxyHelper.service [Unit] Description=On demand Work Socks tunnel After=network.target SocksProxyHelper.socket Requires=SocksProxyHelper.socket SocksProxy.service After=SocksProxy.service [Service] #Type=simple #Accept=false ExecStart=/lib/systemd/systemd-socket-proxyd 127.0.0.1:9118 TimeoutStopSec=5 [Install] WantedBy=multi-user.target $ systemctl --user daemon-reload
這似乎有效,直到
ssh
死亡或被殺。然後它不會在下一次連接嘗試時重新生成。問題:
- /usr/bin/ssh 真的不能接受 systemd 傳遞的套接字嗎?還是只有較新的版本?我的是來自 up2date Debian 8.9的那個。
- 只有根單位可以使用該
BindTodevice
選項嗎?- 為什麼在舊隧道死亡後,我的代理服務在第一個新連接上沒有正確重生?
- 這是設置“按需 ssh socks 代理”的正確方法嗎?如果不是,你是怎麼做到的?
- /usr/bin/ssh 真的不能接受 systemd 傳遞的套接字嗎?
我認為這並不奇怪,考慮到:
- OpenSSH 是一個 OpenBSD 項目
- systemd 僅支持 Linux 核心
- systemd 支持需要顯式添加到 OpenSSH,作為可選/建構時依賴項,因此它可能很難賣。
- 只有根單位可以使用該
BindTodevice
選項嗎?使用者 systemd 實例通常非常孤立,例如無法與主 pid-0 實例通信。諸如依賴使用者單元文件中的系統單元之類的事情是不可能的。
BindToDevice
提及的文件:請注意,設置此參數可能會導致向單元添加額外的依賴項(見上文)。
由於上述限制,我們可以暗示該選項不適用於使用者 systemd 實例。
- 為什麼在舊隧道死亡後,我的代理服務在第一個新連接上沒有正確重生?
據我了解,事件鏈如下:
SocksProxyHelper.socket
已啟動。- SOCKS 客戶端連接到 localhost:8118。
- systemd 啟動
SocksProxyHelper.service
。- 作為 的依賴
SocksProxyHelper.service
,systemd 也會啟動SocksProxy.service
。systemd-socket-proxyd
接受 systemd 套接字,並將其數據轉發到ssh
.ssh
死或被殺。- systemd 通知並
SocksProxy.service
進入非活動狀態,但什麼也不做。SocksProxyHelper.service
繼續執行並接受連接,但無法連接到ssh
,因為它不再執行。解決方法是添加
BindsTo=SocksProxy.service
到SocksProxyHelper.service
. 引用其文件(強調添加):配置需求依賴,在風格上與
Requires=
. 但是,這種依賴類型更強:它除了Requires=
聲明如果綁定到的單元停止,該單元也將停止的效果之外。這意味著綁定到另一個單元的單元突然進入非活動狀態也將被停止。單元可能由於不同的原因突然、意外地進入非活動狀態:服務單元的主程序可能自行終止,設備單元的備份設備可能已拔出,或者掛載單元的掛載點可能在沒有涉及的情況下被解除安裝系統和服務管理器。當與
After=
同一個單元一起使用時, 的行為BindsTo=
甚至更強。在這種情況下,嚴格綁定到的單元必須處於活動狀態才能使該單元也處於活動狀態。這不僅意味著綁定到另一個單元的單元突然進入非活動狀態,而且綁定到另一個單元的單元由於條件檢查失敗(例如ConditionPathExists=
,ConditionPathIsSymbolicLink=
, … - 見下文)將被停止,如果它正在執行。因此,在許多情況下,最好與 結合BindsTo=
使用After=
。
- 這是設置“按需 ssh socks 代理”的正確方法嗎?如果不是,你是怎麼做到的?
可能沒有“正確的方法”。這種方法有它的優點(一切都是“按需”)和缺點(依賴於 systemd,第一個連接沒有通過,因為 ssh 還沒有開始監聽)。也許在 autossh 中實現 systemd 套接字啟動支持會是一個更好的解決方案。