當我們需要對遠端機器進行操作時,經常會被防火牆卡住,這時候就需要透過代理伺服器來解決問題。在眾多手段中 SSH tunnel 又特別順手,作為一個歷史悠久且可靠的管道,為數不少的服務、軟體都與它都有相當程度的整合,進而帶來極高的靈活性。
然後我遇到的問題是該環境裡面沒有建置公用的 SSH 跳板機(jump server / bastion machine)。山不轉路轉,沒有公用的機器就手動開一個!
手邊能夠迅速開一個機器起來的手段變是 Kubernetes,所以本文的目標就是利用該 Kubernete 叢集作為我的跳板機;又因為我的目標不是接觸叢集上的資源,所以 kubectl port-forward
指令並不滿足我的需求。
設置 SSH 伺服器
我打算直接開一個新的 Pod 來當伺服器,容器則直接拉網路上的資源而非自建,然後我選擇了 linuxserver/openssh-server。
設置公鑰
我們需要把自己的公鑰放進伺服器端的 authorized_keys
才能登入。
上面選用的容器提供了以下幾個環境變數可用於設置公鑰:
-e PUBLIC_KEY=yourpublickey `#optional` \
-e PUBLIC_KEY_FILE=/path/to/file `#optional` \
-e PUBLIC_KEY_DIR=/path/to/directory/containing/_only_/pubkeys `#optional` \
-e PUBLIC_KEY_URL=https://github.com/username.keys `#optional` \
我想偷懶,所以直接走 PUBLIC_KEY
,等等直接丟在 Pod 環境變數上。
開放 SSH tunnel
根據 sshd_config (5) 說明,sshd
預設是沒有開放 SSH tunnel 功能的,故我們需要去修改 /etc/ssh/sshd_config
。
上述選用的容器提供了 Custom Scripts 功能,當我們將腳本放到指定路徑下、容器會在啟動時自動執行,這大幅地減少設置與重新載入 sshd
的麻煩。
開個 ConfigMap 來存腳本:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: bastion
data:
ssh-init: |
#!/bin/bash
sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding yes/g' /etc/ssh/sshd_config
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
sed -i 's/GatewayPorts no/GatewayPorts yes/g' /etc/ssh/sshd_config
sed -i 's/PermitTunnel no/PermitTunnel yes/g' /etc/ssh/sshd_config
sed -i 's/X11Forwarding no/X11Forwarding yes/g' /etc/ssh/sshd_config
理論上這裡的唯一一項必須是 PermitTunnel
要打開,但上面的腳本多開了些權限方便做事。
設定 Deployment
最後就是開個 Deployment 來上架 SSH 伺服器:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bastion
spec:
selector:
matchLabels:
app.kubernetes.io/name: bastion
app.kubernetes.io/instance: bastion
template:
metadata:
labels:
app.kubernetes.io/name: bastion
app.kubernetes.io/instance: bastion
spec:
containers:
- name: openssh-server
image: linuxserver/openssh-server:latest
ports:
- containerPort: 2222
env:
- name: PUBLIC_KEY
value: ssh-ed25519 REDACTED
volumeMounts:
- name: custom-cont-init
mountPath: /custom-cont-init.d
volumes:
- name: custom-cont-init
configMap:
name: bastion
介紹一下這裡的幾個寫死的參數:
containerPort
設定為 2222,是為linuxserver/openssh-server
的預設 SSH 接口- 環境變數中的
PUBLIC_KEY
就是稍後要連線進去用的公鑰 volumeMounts
的路徑(/custom-cont-init.d
)是此容器放置 Custom Scripts 的位置,我們把 ConfigMap 接到這個位子上、在容器在初始化階段時裡面放置的腳本會自動被執行
再順便開個 Service 等等連線可以更順手:
---
apiVersion: v1
kind: Service
metadata:
name: bastion
spec:
type: ClusterIP
ports:
- port: 2222
targetPort: 2222
protocol: TCP
name: ssh
selector:
app.kubernetes.io/name: bastion
app.kubernetes.io/instance: bastion
連線
這還是免不了需要使用到 port-forward 指令:
$ kube port-forward svc/bastion 2222:2222
這樣就會把本地端的 2222 阜轉到容器的 2222 阜上去,接下來嘗試登入看看:
$ ssh linuxserver.io@localhost -p 2222
ED25519 key fingerprint is SHA256:REDACTED.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[localhost]:2222' (ED25519) to the list of known hosts.
Welcome to OpenSSH Server
bastion-544d566fdc-pzs2k:~$
連線參數的部分,所使用的容器的預設使用者名稱為 linuxserver.io
,然後連到本機端 2222 阜應該很好理解。
成功!然後就可以開始讓各種工具走 localhost:2222
來建 SSH 隧道了。