强曰为道

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

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