强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 连接问题、权限问题的诊断和修复。