autossh SSH 反向隧道:从 Docker 到 Kubernetes 部署
背景
在内网环境中,经常需要将本地服务暴露到远程服务器上,常见场景如:
- 本地开发环境需要被外部访问
- 内网服务需要通过跳板机访问
- Kubernetes 集群服务需要临时暴露到外部网络
使用 SSH 反向隧道(-R 参数)可以将本地端口映射到远程服务器端口,而 autossh 则能自动保持 SSH 连接稳定,在断线时自动重连。
本文将从 Docker 容器部署开始,逐步过渡到 Kubernetes 集群部署方案。
什么是 SSH 反向隧道
SSH 反向隧道(Remote Port Forwarding)允许你将本地机器的端口暴露到远程机器上。当远程服务器上的某个端口收到流量时,它会通过 SSH 连接将其转发到你的本地服务。
远程服务器:6001 → SSH隧道 → 本地服务:3067
方案一:Docker 容器部署
准备 SSH 密钥
首先创建专用的 SSH 密钥用于隧道认证:
1ssh-keygen -t rsa -b 4096 -f ~/.ssh/autossh_id_rsa -N ""
将公钥添加到远程服务器的 ~/.ssh/authorized_keys 中:
1ssh-copy-id -i ~/.ssh/autossh_id_rsa.pub user@10.0.0.1
Docker 启动命令
1docker run -d \
2 --name=ssh-tunnel \
3 --restart=always \
4 --network host \
5 -v ~/.ssh/autossh_id_rsa:/app/autossh_id_rsa:ro \
6 -v ~/.ssh/known_hosts:/app/known_hosts:ro \
7 -e SSH_REMOTE_USER=user \
8 -e SSH_REMOTE_HOST=10.0.0.1 \
9 -e SSH_REMOTE_PORT=22 \
10 -e SSH_MODE=-R \
11 -e SSH_TUNNEL_PORT=6001 \
12 -e SSH_TARGET_HOST=127.0.0.1 \
13 -e SSH_TARGET_PORT=3067 \
14 -e SSH_BIND_IP=127.0.0.1 \
15 -e SSH_KEY_FILE=/app/autossh_id_rsa \
16 -e SSH_KNOWN_HOSTS_FILE=/app/known_hosts \
17 jnovack/autossh
环境变量说明
| 变量 | 说明 | 示例 |
|---|---|---|
SSH_REMOTE_USER |
远程服务器用户名 | user |
SSH_REMOTE_HOST |
远程服务器地址 | 10.0.0.1 |
SSH_REMOTE_PORT |
SSH 端口 | 22 |
SSH_MODE |
隧道模式,-R 为反向隧道 |
-R |
SSH_TUNNEL_PORT |
远程服务器监听端口 | 6001 |
SSH_TARGET_HOST |
本地目标地址 | 127.0.0.1 |
SSH_TARGET_PORT |
本地目标端口 | 3067 |
SSH_BIND_IP |
远程绑定地址 | 127.0.0.1 |
SSH_KEY_FILE |
SSH 私钥路径(容器内) | /app/autossh_id_rsa |
SSH_KNOWN_HOSTS_FILE |
known_hosts 路径(容器内) | /app/known_hosts |
映射关系:远程服务器 127.0.0.1:6001 ↔ 本地 127.0.0.1:3067
为什么使用 --network host
使用宿主机网络模式的原因:
- SSH 隧道本质是网络层的连接,不需要 Docker 的网络隔离
- 避免端口映射的额外开销
- 确保
127.0.0.1绑定正确指向宿主机回环地址
常见问题:密钥路径错误
jnovack/autossh 镜像默认查找密钥路径为 /id_rsa(容器根目录),如果挂载到其他路径,需要通过 SSH_KEY_FILE 环境变量显式指定。如果使用 SSH_EXTRA_ARGS="-i /app/autossh_id_rsa" 会报错,因为镜像的密钥检测逻辑先于 SSH 参数生效:
1[FATAL] No SSH Key file found
正确做法是使用 SSH_KEY_FILE=/app/autossh_id_rsa 环境变量指定路径。
验证隧道
1# 查看容器日志
2docker logs ssh-tunnel
3
4# 在远程服务器上测试连接
5ssh user@10.0.0.1
6curl http://127.0.0.1:6001
方案二:Kubernetes 集群部署
当需要将 SSH 隧道纳入 Kubernetes 集群管理时,可以使用 Deployment + Secret + ConfigMap 的方式部署。
架构说明
在 K8s 中部署 autossh 需要使用 hostNetwork: true 模式(对应 Docker 的 --network host),因为 SSH 隧道需要在宿主机网络层面工作。
完整 YAML
1---
2apiVersion: v1
3kind: Namespace
4metadata:
5 name: ssh-tunnel
6---
7apiVersion: v1
8kind: Secret
9metadata:
10 name: ssh-key
11 namespace: ssh-tunnel
12type: Opaque
13data:
14 # 使用 base64 编码的 SSH 私钥
15 # 生成方式: base64 -w0 ~/.ssh/autossh_id_rsa
16 autossh_id_rsa: <BASE64_ENCODED_KEY>
17---
18apiVersion: v1
19kind: ConfigMap
20metadata:
21 name: ssh-known-hosts
22 namespace: ssh-tunnel
23data:
24 known_hosts: |
25 # 此处填写远程服务器的 SSH 公钥指纹
26 # 从本地 ~/.ssh/known_hosts 中提取对应条目
27---
28apiVersion: apps/v1
29kind: Deployment
30metadata:
31 name: ssh-tunnel
32 namespace: ssh-tunnel
33 labels:
34 app: ssh-tunnel
35spec:
36 replicas: 1
37 selector:
38 matchLabels:
39 app: ssh-tunnel
40 template:
41 metadata:
42 labels:
43 app: ssh-tunnel
44 spec:
45 hostNetwork: true
46 restartPolicy: Always
47 volumes:
48 - name: ssh-key
49 secret:
50 secretName: ssh-key
51 defaultMode: 0400
52 - name: known-hosts
53 configMap:
54 name: ssh-known-hosts
55 defaultMode: 0444
56 containers:
57 - name: autossh
58 image: jnovack/autossh
59 imagePullPolicy: IfNotPresent
60 env:
61 - name: SSH_REMOTE_USER
62 value: "user"
63 - name: SSH_REMOTE_HOST
64 value: "10.0.0.1"
65 - name: SSH_REMOTE_PORT
66 value: "22"
67 - name: SSH_MODE
68 value: "-R"
69 - name: SSH_TUNNEL_PORT
70 value: "6001"
71 - name: SSH_TARGET_HOST
72 value: "127.0.0.1"
73 - name: SSH_TARGET_PORT
74 value: "3067"
75 - name: SSH_BIND_IP
76 value: "127.0.0.1"
77 - name: SSH_KEY_FILE
78 value: "/etc/ssh-key/autossh_id_rsa"
79 - name: SSH_KNOWN_HOSTS_FILE
80 value: "/etc/ssh-known-hosts/known_hosts"
81 volumeMounts:
82 - name: ssh-key
83 mountPath: /etc/ssh-key
84 readOnly: true
85 - name: known-hosts
86 mountPath: /etc/ssh-known-hosts
87 readOnly: true
K8s 部署步骤
1# 1. 生成 SSH 私钥 Secret
2kubectl create secret generic ssh-key \
3 --namespace=ssh-tunnel \
4 --from-file=autossh_id_rsa=~/.ssh/autossh_id_rsa \
5 --dry-run=client -o yaml > ssh-key-secret.yaml
6
7# 2. 生成 known_hosts ConfigMap
8kubectl create configmap ssh-known-hosts \
9 --namespace=ssh-tunnel \
10 --from-file=known_hosts=~/.ssh/known_hosts \
11 --dry-run=client -o yaml > ssh-known-hosts-cm.yaml
12
13# 3. 部署
14kubectl apply -f ssh-tunnel.yaml
Kuboard 一键部署
在 Kuboard 中,可以直接将完整 YAML 粘贴到"导入 YAML"中一键部署。需要注意:
- 将 SSH 私钥通过
base64 -w0 ~/.ssh/autossh_id_rsa编码后填入 Secret 的data.autossh_id_rsa字段 - 从本地
~/.ssh/known_hosts中提取远程服务器的公钥指纹,填入 ConfigMap - 确保目标节点能直接访问远程服务器的 SSH 端口(22)
K8s 部署注意事项
- hostNetwork 要求:Pod 使用宿主机网络,确保能正常发起 SSH 连接
- 节点选择:建议使用
nodeSelector将 Pod 调度到特定节点,方便网络策略管理 - 密钥权限:Secret 挂载时设置
defaultMode: 0400,确保私钥文件权限正确(SSH 要求私钥权限为 600 或 400) - 高可用:反向隧道是点对点连接,建议
replicas: 1,避免多个 Pod 同时绑定同一端口导致冲突
Docker vs K8s 部署对比
| 维度 | Docker | Kubernetes |
|---|---|---|
| 部署复杂度 | 低,一条命令 | 中,需要 YAML + 密钥管理 |
| 密钥管理 | 宿主机直接挂载 | Secret 资源管理 |
| 自愈能力 | Docker restart policy | K8s 自动重启 + 健康检查 |
| 网络模式 | --network host |
hostNetwork: true |
| 适用场景 | 单机、临时隧道 | 集群化、需要统一管理 |
| 日志管理 | docker logs |
kubectl logs + 日志聚合 |
总结
SSH 反向隧道是解决内网服务暴露问题的经典方案。通过 autossh 可以保证隧道连接的稳定性,而 Docker 和 Kubernetes 则提供了不同的部署选择:
- Docker 适合快速启动和临时隧道场景,一条命令即可完成部署
- Kubernetes 适合生产环境,通过声明式配置和密钥管理提升安全性和可维护性
选择哪种方式取决于你的基础设施和管理需求。
