SSH 服务器完全指南 / 第13章 Docker 中的 SSH
第13章 Docker 中的 SSH
13.1 Docker 与 SSH 的关系
Docker 容器通常通过 docker exec 管理,但某些场景下仍然需要 SSH:
| 场景 | 方式 | 推荐 |
|---|---|---|
| 日常管理 | docker exec | ✅ |
| CI/CD 部署 | SSH / docker exec | 都可以 |
| 生产环境多容器 | SSH 编排工具 | 视情况 |
| 模拟多节点集群 | 容器内 SSH | ✅ |
| 遗留应用迁移 | 容器内 SSH | 必要时 |
| 开发测试 | SSH 到容器 | 方便 |
最佳实践: 生产环境中,容器不应该运行 SSH 服务。使用
docker exec或编排工具(Kubernetes、Docker Compose)管理容器。
13.2 在 Docker 容器中安装 SSH
Dockerfile 示例
# Dockerfile.ssh
FROM ubuntu:22.04
# 安装 OpenSSH
RUN apt-get update && \
apt-get install -y openssh-server && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 创建 SSH 运行目录
RUN mkdir -p /var/run/sshd
# 配置 SSH
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
# 设置 root 密码
RUN echo 'root:password' | chpasswd
# 暴露 SSH 端口
EXPOSE 22
# 启动 SSH 服务
CMD ["/usr/sbin/sshd", "-D"]
# 构建镜像
docker build -t ssh-server -f Dockerfile.ssh .
# 运行容器
docker run -d --name ssh-container -p 2222:22 ssh-server
# 连接到容器
ssh -p 2222 root@localhost
使用密钥认证的 Dockerfile
# Dockerfile.ssh-keys
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y openssh-server && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /var/run/sshd /root/.ssh
# 从构建上下文复制公钥
COPY authorized_keys /root/.ssh/authorized_keys
RUN chmod 600 /root/.ssh/authorized_keys
# 禁用密码登录
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
# 生成主机密钥
RUN ssh-keygen -A
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
# 使用
docker build -t ssh-server-keys \
--build-arg SSH_PUB_KEY="$(cat ~/.ssh/id_ed25519.pub)" \
-f Dockerfile.ssh-keys .
docker run -d -p 2222:22 ssh-server-keys
ssh -p 2222 root@localhost
使用环境变量传递密钥
# Dockerfile.ssh-env
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y openssh-server && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /var/run/sshd /root/.ssh && \
chmod 700 /root/.ssh
# 使用 entrypoint 脚本处理环境变量
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 22
ENTRYPOINT ["/entrypoint.sh"]
#!/bin/bash
# entrypoint.sh
# 从环境变量设置 SSH 公钥
if [ -n "$SSH_PUBLIC_KEY" ]; then
echo "$SSH_PUBLIC_KEY" > /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
fi
# 从文件挂载设置 SSH 公钥
if [ -f /root/.ssh/authorized_keys ]; then
chmod 600 /root/.ssh/authorized_keys
fi
# 生成主机密钥(如果不存在)
ssh-keygen -A
# 启动 SSH 服务
exec /usr/sbin/sshd -D "$@"
# 使用环境变量
docker run -d -p 2222:22 \
-e SSH_PUBLIC_KEY="$(cat ~/.ssh/id_ed25519.pub)" \
ssh-server-env
# 或使用 volume 挂载
docker run -d -p 2222:22 \
-v ~/.ssh/id_ed25519.pub:/root/.ssh/authorized_keys:ro \
ssh-server-env
13.3 Docker Compose 示例
# docker-compose.yml
version: '3.8'
services:
ssh-server:
build:
context: .
dockerfile: Dockerfile.ssh
container_name: ssh-server
ports:
- "2222:22"
volumes:
- ssh-keys:/root/.ssh
- ssh-host-keys:/etc/ssh/ssh_host_keys
environment:
- SSH_PUBLIC_KEY=${SSH_PUBLIC_KEY}
restart: unless-stopped
app:
image: my-app:latest
depends_on:
- ssh-server
networks:
- internal
volumes:
ssh-keys:
ssh-host-keys:
networks:
internal:
driver: bridge
13.4 SSH 到 Docker 容器
直接 SSH 到容器
# 获取容器 IP
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container-name
# SSH 到容器
ssh [email protected]
# 或使用端口映射
ssh -p 2222 root@localhost
使用 Docker 网络
# 创建自定义网络
docker network create ssh-network
# 运行容器在自定义网络中
docker run -d --name ssh-server --network ssh-network ssh-server
# 从另一个容器连接
docker run -it --network ssh-network ubuntu ssh root@ssh-server
配置 ~/.ssh/config
# ~/.ssh/config
# Docker 容器
Host docker-*
User root
Port 22
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Host docker-app
HostName localhost
Port 2222
Host docker-db
HostName localhost
Port 2223
13.5 Docker 安全 SSH 配置
安全加固的 Dockerfile
# Dockerfile.ssh-hardened
FROM ubuntu:22.04
# 创建非 root 用户
RUN useradd -m -s /bin/bash appuser && \
echo 'appuser:password' | chpasswd
RUN apt-get update && \
apt-get install -y openssh-server && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# SSH 安全配置
RUN mkdir -p /var/run/sshd && \
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
echo 'AllowUsers appuser' >> /etc/ssh/sshd_config && \
echo 'X11Forwarding no' >> /etc/ssh/sshd_config && \
echo 'AllowTcpForwarding no' >> /etc/ssh/sshd_config
# 设置用户公钥
RUN mkdir -p /home/appuser/.ssh && \
chmod 700 /home/appuser/.ssh
COPY authorized_keys /home/appuser/.ssh/authorized_keys
RUN chown -R appuser:appuser /home/appuser/.ssh && \
chmod 600 /home/appuser/.ssh/authorized_keys
# 生成主机密钥
RUN ssh-keygen -A
# 使用非 root 用户运行
USER appuser
WORKDIR /home/appuser
# 不直接运行 sshd,使用 entrypoint
COPY entrypoint.sh /entrypoint.sh
USER root
ENTRYPOINT ["/entrypoint.sh"]
13.6 SSH Agent 转发到容器
# 将本地 SSH Agent 转发到容器
docker run -it \
-v $SSH_AUTH_SOCK:/ssh-agent \
-e SSH_AUTH_SOCK=/ssh-agent \
ubuntu bash
# 在容器内使用本地 SSH 密钥
ssh -T [email protected]
Docker Compose 中使用 SSH Agent
# docker-compose.yml
version: '3.8'
services:
build:
image: node:18
volumes:
- ${SSH_AUTH_SOCK}:/ssh-agent
environment:
- SSH_AUTH_SOCK=/ssh-agent
command: |
bash -c "
apt-get update && apt-get install -y openssh-client git
ssh -T [email protected] || true
npm install
"
13.7 Dockerfile 中的 Git 克隆(SSH 密钥)
# 使用 BuildKit 的 SSH 转发
# syntax=docker/dockerfile:1
FROM node:18
# 使用 SSH Agent 转发(BuildKit)
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh git clone [email protected]:company/private-repo.git /app
WORKDIR /app
RUN npm install
# 构建时使用 SSH Agent
DOCKER_BUILDKIT=1 docker build --ssh default -t myapp .
13.8 批量管理多个容器
使用 docker exec 代替 SSH
# 批量执行命令
for container in $(docker ps --format "{{.Names}}"); do
echo "--- $container ---"
docker exec "$container" uname -a
done
# 并行执行
docker ps --format "{{.Names}}" | xargs -P 10 -I {} docker exec {} uptime
SSH 方式批量管理
#!/bin/bash
# docker-batch-exec.sh
# 从 Docker Compose 获取容器列表
CONTAINERS=$(docker-compose ps -q)
for container in $CONTAINERS; do
NAME=$(docker inspect -f '{{.Name}}' $container | sed 's/^\///')
IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $container)
echo "--- $NAME ($IP) ---"
ssh -o StrictHostKeyChecking=no root@$IP "$1"
done
13.9 Kubernetes 中的 SSH
SSH 调试 Pod
# debug-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: ssh-debug
spec:
containers:
- name: ssh
image: linuxserver/openssh-server
env:
- name: PASSWORD_ACCESS
value: "true"
- name: USER_PASSWORD
value: "debugpass"
- name: USER_NAME
value: "debug"
ports:
- containerPort: 2222
---
apiVersion: v1
kind: Service
metadata:
name: ssh-debug
spec:
selector:
app: ssh-debug
ports:
- port: 2222
targetPort: 2222
type: LoadBalancer
# 部署
kubectl apply -f debug-pod.yaml
# 连接
ssh -p 2222 debug@<service-ip>
# 使用 kubectl 端口转发
kubectl port-forward svc/ssh-debug 2222:2222
ssh -p 2222 debug@localhost
# 清理(使用完后删除)
kubectl delete -f debug-pod.yaml
kubectl exec vs SSH
# 推荐:使用 kubectl exec
kubectl exec -it pod-name -- /bin/bash
# SSH 方式(需要 Pod 内运行 sshd)
ssh -p 2222 user@pod-ip
| 方式 | 优点 | 缺点 |
|---|---|---|
| kubectl exec | 无需额外服务、原生支持 | 需要 kubectl 权限 |
| SSH | 熟悉的工作流、端口转发 | 需要额外服务、安全风险 |
13.10 安全考量
| 风险 | 说明 | 缓解措施 |
|---|---|---|
| 容器内 SSH 攻击面 | 增加了攻击入口 | 不在生产容器内运行 SSH |
| 密钥泄露 | 容器镜像可能包含密钥 | 使用多阶段构建、secrets |
| 密码暴力破解 | 弱密码风险 | 禁用密码认证、使用 Fail2Ban |
| 主机密钥固定 | 容器重建后密钥变化 | 挂载持久化存储 |
| 网络暴露 | SSH 端口暴露到公网 | 使用内部网络、防火墙 |
| root 访问 | 容器内 root 权限过大 | 使用非 root 用户、限制能力 |
生产环境建议
# 1. 不在生产容器内运行 SSH
# 使用 docker exec 或 kubectl exec
# 2. 如果必须运行 SSH,使用以下安全措施:
# - 禁用密码认证
# - 使用密钥或证书
# - 限制来源 IP
# - 使用非 root 用户
# - 启用审计日志
# - 设置 Fail2Ban
# 3. 使用 Docker secrets 管理敏感信息
docker secret create ssh_key ~/.ssh/id_deploy
扩展阅读
下一章: 第14章 故障排查 → 学习 SSH 连接问题、权限问题的诊断和修复。