基于 ContainerSSH 的 SSH 代理服务,允许用户通过 SSH 直接连接到 Kubernetes 集群中的 Pod 容器。
- 🔐 灵活的认证方式:支持密码认证和 SSH 公钥认证
- 🎯 精确的容器映射:每个用户可以映射到特定的 Kubernetes Pod 和容器
- 🌐 多集群支持:支持配置和连接多个 Kubernetes 集群,用户可以访问不同集群的 Pod
- 🚀 零侵入性:使用 ContainerSSH 的 persistent 模式,连接到已存在的 Pod,无需创建新容器
- 📝 详细的日志:完整的认证和连接日志,便于调试和审计
- 🔧 易于配置:简单的 YAML 配置文件,支持热重载
- 🧪 完整的测试:包含单元测试和集成测试
- Go 1.21 或更高版本
- Kubernetes 集群访问权限
- kubectl 配置正确(用于本地开发测试)
git clone <repository-url>
cd sshproxy首次使用需要生成 SSH host key:
ssh-keygen -t rsa -b 2048 -f ssh_host_rsa_key -N "" -C "containerssh@sshproxy"
chmod 600 ssh_host_rsa_key编辑 webhook.yaml,配置 Kubernetes 集群连接信息和用户映射:
listen: ":8080"
# Kubernetes 集群配置
clusters:
# 生产集群
- name: "prod-cluster"
host: "https://prod-k8s-api.example.com:6443"
cacertFile: "/path/to/prod-ca.crt"
certFile: "/path/to/prod-client.crt"
keyFile: "/path/to/prod-client.key"
# 开发集群
- name: "dev-cluster"
host: "https://dev-k8s-api.example.com:6443"
cacertFile: "/path/to/dev-ca.crt"
certFile: "/path/to/dev-client.crt"
keyFile: "/path/to/dev-client.key"
# 用户配置
users:
# 生产环境用户
- username: "prod-user"
password: "prod_password"
metadata:
KUBERNETES_CLUSTER: "prod-cluster" # 指定集群
KUBERNETES_POD_NAMESPACE: "production"
KUBERNETES_POD_NAME: "my-app-pod"
KUBERNETES_CONTAINER_NAME: "app"
# 开发环境用户
- username: "dev-user"
password: "dev_password"
metadata:
KUBERNETES_CLUSTER: "dev-cluster" # 指定集群
KUBERNETES_POD_NAMESPACE: "development"
KUBERNETES_POD_NAME: "dev-pod"
KUBERNETES_CONTAINER_NAME: "app"提示:
- 可以从
~/.kube/config中提取集群连接信息 - 每个用户通过
KUBERNETES_CLUSTER字段指定要连接的集群 - 支持配置多个集群,用户可以访问不同集群的 Pod
编辑 config.yaml,移除静态的 Kubernetes 连接配置(现在由 webhook 动态提供):
backend: kubernetes
kubernetes:
# 注意:集群连接配置已移至 webhook.yaml 的 clusters 配置段
# 这样可以支持多个 Kubernetes 集群,并由 webhook 根据用户动态返回对应的集群连接信息
pod:
mode: "persistent"
createMissingPods: falsemake build或手动构建:
go build -o bin/containerssh ./cmd/containerssh
go build -o bin/sshhook ./cmd/sshhook方式 1:使用 Makefile(推荐)
# 启动所有服务
make run
# 或分别启动
make run-hook # 启动 Webhook 服务
make run-ssh # 启动 SSH 服务方式 2:手动启动
在两个终端中分别运行:
# 终端 1:启动 Webhook 服务
./bin/sshhook --config webhook.yaml
# 终端 2:启动 ContainerSSH
./bin/containerssh --config config.yaml使用 SSH 客户端连接:
# 密码认证
ssh developer@localhost -p 2222
# 公钥认证
ssh -i ~/.ssh/your_private_key developer@localhost -p 2222sshproxy/
├── cmd/
│ ├── containerssh/ # ContainerSSH 主程序入口
│ └── sshhook/ # Webhook 服务入口
├── pkg/
│ └── webhook/ # Webhook 实现
│ ├── config.go # 配置加载
│ ├── server.go # HTTP 服务器和认证逻辑
│ ├── config_test.go # 配置测试
│ └── server_test.go # 服务器测试
├── config.yaml # ContainerSSH 配置文件
├── webhook.yaml # Webhook 服务配置文件
├── .gitignore # Git 忽略文件
├── Makefile # 构建脚本
├── go.mod # Go 模块定义
├── go.sum # Go 依赖锁定
└── README.md # 项目文档
详细的配置说明请参考 config.yaml 文件中的注释。主要配置项:
- ssh: SSH 服务配置(监听地址、host key、banner 等)
- auth: 认证配置(webhook URL、认证方式等)
- configserver: 配置服务器(用于动态配置后端)
- backend: 后端类型(kubernetes)
- kubernetes: Kubernetes 连接和 Pod 配置
- log: 日志配置
详细的配置说明请参考 webhook.yaml 文件中的注释。主要配置项:
- listen: Webhook 服务监听地址
- clusters: Kubernetes 集群配置列表(支持多集群)
- name: 集群名称(唯一标识)
- host: Kubernetes API Server 地址
- cacertFile: CA 证书文件路径
- certFile: 客户端证书文件路径
- keyFile: 客户端密钥文件路径
- bearerTokenFile: Bearer Token 文件路径(可选)
- serverName: TLS 服务器名称(可选)
- qps: QPS 限制(可选)
- burst: Burst 限制(可选)
- users: 用户列表
- username: SSH 用户名
- password: 密码(可选)
- publicKey: SSH 公钥(可选)
- metadata: Pod 映射信息
- KUBERNETES_CLUSTER: 集群名称(必须,对应 clusters 中的 name)
- KUBERNETES_POD_NAMESPACE: Pod 所在的 namespace
- KUBERNETES_POD_NAME: Pod 名称
- KUBERNETES_CONTAINER_NAME: 容器名称(可选)
在 webhook.yaml 中配置用户密码:
users:
- username: "user1"
password: "secure_password"
metadata:
KUBERNETES_CLUSTER: "prod-cluster" # 指定集群
KUBERNETES_POD_NAMESPACE: "default"
KUBERNETES_POD_NAME: "my-pod"连接:
ssh user1@your-server -p 2222
# 输入密码:secure_password- 生成 SSH 密钥对:
ssh-keygen -t rsa -b 2048 -f ~/.ssh/sshproxy_key- 在
webhook.yaml中配置公钥:
users:
- username: "user1"
publicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ... user@host"
metadata:
KUBERNETES_CLUSTER: "prod-cluster" # 指定集群
KUBERNETES_POD_NAMESPACE: "default"
KUBERNETES_POD_NAME: "my-pod"- 连接:
ssh -i ~/.ssh/sshproxy_key user1@your-server -p 2222在 webhook.yaml 中配置多个 Kubernetes 集群:
clusters:
- name: "prod-cluster"
host: "https://prod-api.example.com:6443"
cacertFile: "/path/to/prod-ca.crt"
certFile: "/path/to/prod-client.crt"
keyFile: "/path/to/prod-client.key"
- name: "staging-cluster"
host: "https://staging-api.example.com:6443"
cacertFile: "/path/to/staging-ca.crt"
certFile: "/path/to/staging-client.crt"
keyFile: "/path/to/staging-client.key"
- name: "dev-cluster"
host: "https://dev-api.example.com:6443"
cacertFile: "/path/to/dev-ca.crt"
certFile: "/path/to/dev-client.crt"
keyFile: "/path/to/dev-client.key"每个用户通过 KUBERNETES_CLUSTER 元数据字段指定要连接的集群:
users:
# 生产环境运维人员
- username: "ops-prod"
password: "ops_password"
metadata:
KUBERNETES_CLUSTER: "prod-cluster"
KUBERNETES_POD_NAMESPACE: "kube-system"
KUBERNETES_POD_NAME: "monitoring-pod"
# 测试环境开发人员
- username: "dev-staging"
password: "dev_password"
metadata:
KUBERNETES_CLUSTER: "staging-cluster"
KUBERNETES_POD_NAMESPACE: "testing"
KUBERNETES_POD_NAME: "test-pod"
# 开发环境开发人员
- username: "dev-local"
password: "dev_password"
metadata:
KUBERNETES_CLUSTER: "dev-cluster"
KUBERNETES_POD_NAMESPACE: "development"
KUBERNETES_POD_NAME: "dev-pod"- 用户通过 SSH 连接到服务
- Webhook 服务根据用户名查找配置
- 从用户的 metadata 中获取
KUBERNETES_CLUSTER字段 - 在 clusters 配置中查找对应集群的连接信息
- 动态返回该集群的连接配置给 ContainerSSH
- ContainerSSH 使用返回的配置连接到指定集群的 Pod
可以使用以下命令从 kubeconfig 文件中提取集群连接信息:
# 查看 kubeconfig 内容
kubectl config view --flatten --minify
# 提取 CA 证书
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
# 提取客户端证书
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}' | base64 -d > client.crt
# 提取客户端密钥
kubectl config view --raw -o jsonpath='{.users[0].user.client-key-data}' | base64 -d > client.key
# 获取 API Server 地址
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.server}'# 运行所有测试
make test
# 或手动运行
go test ./...
# 运行特定包的测试
go test -v ./pkg/webhook/...
# 运行测试并显示覆盖率
go test -cover ./...# 代码格式化
go fmt ./...
# 代码检查
go vet ./...
# 使用 golangci-lint(如果已安装)
golangci-lint run项目支持本地开发配置文件(不会提交到 Git):
.local-ssh.yaml: 本地 ContainerSSH 配置.local-hook.yaml: 本地 Webhook 配置
使用本地配置启动:
./bin/sshhook --config .local-hook.yaml
./bin/containerssh --config .local-ssh.yamlContainerSSH 和 Webhook 服务都会输出详细的日志:
# ContainerSSH 日志
[SSH] Connection from 127.0.0.1:xxxxx
[Auth] Password authentication request for user: developer
[Auth] ✓ Password authentication successful for user: developer
[Config] Configuration returned - namespace=default, pod=my-app-pod
# Webhook 日志
[Password] Request received - username=developer
[Password] ✓ Authentication successful - username=developer
[Config] Request received - username=developer
[Config] ✓ Configuration returned - namespace=default, pod=my-app-pod修改 config.yaml 中的日志级别:
log:
level: debug # 可选:debug, info, warning, error问题:ssh: connect to host localhost port 2222: Connection refused
解决:
- 确保 ContainerSSH 服务正在运行
- 检查端口是否被占用:
lsof -i :2222 - 检查防火墙设置
问题:Permission denied (publickey,password)
解决:
- 检查
webhook.yaml中的用户配置 - 确保 Webhook 服务正在运行
- 查看 Webhook 日志确认认证请求
问题:Failed to connect to pod
解决:
- 确保 Pod 存在且正在运行:
kubectl get pods -n <namespace> - 检查 Kubernetes 连接配置
- 确保有足够的 RBAC 权限
- 验证 Pod 名称和 namespace 配置正确
问题:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
解决:
- 删除旧的 host key:
ssh-keygen -R [localhost]:2222 - 或编辑
~/.ssh/known_hosts删除对应行
- 使用公钥认证:禁用密码认证,只使用 SSH 公钥
- 配置 TLS:为 Webhook 服务配置 HTTPS
- 限制访问:使用防火墙限制 SSH 端口访问
- 日志审计:配置日志收集和审计
- 监控告警:配置 Prometheus 监控和告警
- 定期更新:保持 ContainerSSH 和依赖库更新
- RBAC 权限:为 ContainerSSH 配置最小权限的 Kubernetes RBAC
欢迎提交 Issue 和 Pull Request!
MIT License - 详见 LICENSE 文件