Dropbear SSH 完全指南 / 09 - Docker 与容器化
第九章:Docker 与容器化
9.1 为什么在容器中使用 SSH
在容器化环境中使用 SSH 服务的需求通常源于以下场景:
| 场景 | 说明 | 推荐方案 |
|---|---|---|
| CI/CD 构建环境 | Jenkins 等工具需要 SSH 访问构建节点 | Dropbear |
| 遗留系统迁移 | 老系统依赖 SSH 执行维护脚本 | Dropbear |
| 嵌入式仿真 | 模拟嵌入式设备的 SSH 管理接口 | Dropbear |
| 远程调试 | 容器内部调试和文件传输 | Dropbear 或 SSH |
| 多租户访问控制 | 不同团队通过 SSH 访问共享容器 | OpenSSH |
| Git 操作 | Git over SSH | OpenSSH 或 Git HTTP |
注意: 在容器中运行 SSH 服务并不符合容器化最佳实践(每个容器只运行一个服务)。但在某些实际场景中,这是必要的折衷方案。
Dropbear vs OpenSSH 在容器中的对比
| 对比项 | Dropbear | OpenSSH |
|---|---|---|
| 镜像大小 | ~1-5 MB | ~20-50 MB |
| 内存占用 | ~1-2 MB | ~5-15 MB |
| 启动速度 | 极快 | 快 |
| 依赖项 | 极少 | zlib, openssl, pam… |
| 功能完整度 | 基础 | 完整 |
| 安全更新 | 随镜像更新 | 随系统更新 |
9.2 基础 Docker 镜像
Alpine Linux 方案(推荐)
# Dockerfile.dropbear - 基于 Alpine 的 Dropbear 镜像
FROM alpine:3.19
# 安装 Dropbear
RUN apk add --no-cache dropbear \
&& mkdir -p /etc/dropbear /var/run/dropbear
# 生成主机密钥(构建时)
RUN dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key \
&& dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
# 设置 root 密码(或使用 authorized_keys)
RUN echo 'root:changeme' | chpasswd
# 暴露 SSH 端口
EXPOSE 22
# 启动 Dropbear
CMD ["dropbear", "-F", "-E", "-R", "-w", "-s", "-p", "22"]
# 构建镜像
docker build -t dropbear-alpine -f Dockerfile.dropbear .
# 运行容器
docker run -d \
--name dropbear-test \
-p 2222:22 \
dropbear-alpine
# 连接测试
dbclient -p 2222 root@localhost
Debian Slim 方案
# Dockerfile.dropbear-debian - 基于 Debian 的 Dropbear 镜像
FROM debian:bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
dropbear-bin \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /etc/dropbear /var/run/dropbear \
&& dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key \
&& dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
RUN echo 'root:changeme' | chpasswd
EXPOSE 22
CMD ["/usr/sbin/dropbear", "-F", "-E", "-R", "-w", "-s", "-p", "22"]
极简多阶段构建
# Dockerfile.dropbear-multi - 多阶段构建最小镜像
# 阶段一:编译 Dropbear
FROM alpine:3.19 AS builder
RUN apk add --no-cache build-base zlib-dev linux-headers
ARG DROPBEAR_VERSION=2024.86
RUN wget -q https://matt.ucc.asn.au/dropbear/releases/dropbear-${DROPBEAR_VERSION}.tar.bz2 \
&& tar xjf dropbear-${DROPBEAR_VERSION}.tar.bz2 \
&& cd dropbear-${DROPBEAR_VERSION} \
&& echo '#define DROPBEAR_X11FWD 0' > localoptions.h \
&& echo '#define DROPBEAR_CLI_LOCALTCPFWD 0' >> localoptions.h \
&& echo '#define DROPBEAR_CLI_REMOTETCPFWD 0' >> localoptions.h \
&& echo '#define DROPBEAR_AGENTFWD 0' >> localoptions.h \
&& echo '#define DROPBEAR_SFTP_SERVER 0' >> localoptions.h \
&& CFLAGS="-Os -ffunction-sections -fdata-sections" \
LDFLAGS="-Wl,--gc-sections" \
./configure \
--prefix=/usr \
--disable-zlib \
--disable-pam \
--enable-bundled-libtom \
--disable-syslog \
--enable-static \
&& make -j$(nproc) MULTI=1 \
&& strip dropbearmulti
# 阶段二:最终镜像
FROM scratch
COPY --from=builder /dropbear-*/dropbearmulti /usr/bin/dropbearmulti
# 创建符号链接
RUN ["/usr/bin/dropbearmulti", "dropbearmulti"]
# 注意:scratch 镜像中需要手动创建链接
# 实际使用时推荐从 builder 复制多个符号链接
# 最终镜像仅包含 Dropbear 二进制
9.3 安全 Docker 配置
使用公钥认证
# Dockerfile.dropbear-pubkey - 公钥认证专用
FROM alpine:3.19
RUN apk add --no-cache dropbear \
&& mkdir -p /etc/dropbear /root/.ssh /var/run/dropbear \
&& chmod 700 /root/.ssh
RUN dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
# authorized_keys 将在运行时挂载
EXPOSE 22
CMD ["dropbear", "-F", "-E", "-R", "-s", "-w", "-p", "22"]
# 运行时挂载公钥
docker run -d \
--name dropbear-secure \
-p 2222:22 \
-v /path/to/authorized_keys:/root/.ssh/authorized_keys:ro \
-v /path/to/host-keys:/etc/dropbear:ro \
dropbear-pubkey
docker-compose 配置
# docker-compose.yml
version: '3.8'
services:
dropbear:
build:
context: .
dockerfile: Dockerfile.dropbear-pubkey
container_name: dropbear-ssh
ports:
- "2222:22"
volumes:
# 挂载公钥(只读)
- ./ssh/authorized_keys:/root/.ssh/authorized_keys:ro
# 持久化主机密钥
- dropbear-host-keys:/etc/dropbear
environment:
- DROPBEAR_PORT=22
restart: unless-stopped
# 安全限制
read_only: false # Dropbear 需要写某些文件
tmpfs:
- /var/run
- /tmp
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # 绑定 <1024 端口需要
- SETUID
- SETGID
security_opt:
- no-new-privileges:true
volumes:
dropbear-host-keys:
安全加固选项
# 安全加固的 Dropbear 容器
FROM alpine:3.19
RUN apk add --no-cache dropbear \
&& mkdir -p /etc/dropbear /var/run/dropbear /home/admin/.ssh \
&& chmod 700 /home/admin/.ssh \
&& adduser -D -s /bin/sh admin \
&& chown admin:admin /home/admin/.ssh
RUN dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
# 非 root 用户运行
USER admin
EXPOSE 2022
# 使用非特权端口,禁止 root 登录
CMD ["dropbear", "-F", "-E", "-R", "-s", "-p", "2022", "-U", "admin", "-G", "admin"]
9.4 嵌入式容器场景
OpenWrt Docker 镜像
# Dockerfile.openwrt-dropbear - 模拟 OpenWrt Dropbear 环境
FROM alpine:3.19
# 模拟 OpenWrt 目录结构
RUN apk add --no-cache dropbear \
&& mkdir -p /etc/dropbear /etc/config /etc/init.d \
/var/run/dropbear
# OpenWrt 风格配置文件
RUN echo 'config dropbear' > /etc/config/dropbear \
&& echo ' option PasswordAuth "off"' >> /etc/config/dropbear \
&& echo ' option Port "22"' >> /etc/config/dropbear \
&& echo ' option Interface "lan"' >> /etc/config/dropbear
# 生成密钥
RUN dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
EXPOSE 22
CMD ["dropbear", "-F", "-E", "-R", "-s", "-p", "22"]
Buildroot 容器构建
# Dockerfile.buildroot-dropbear - 使用 Buildroot 构建最小化系统
FROM ubuntu:22.04 AS buildroot
RUN apt-get update && apt-get install -y \
build-essential git wget cpio python unzip \
bc rsync file libssl-dev \
&& rm -rf /var/lib/apt/lists/*
# 获取 Buildroot
ARG BUILDROOT_VERSION=2024.02
RUN git clone --depth 1 --branch ${BUILDROOT_VERSION} \
https://github.com/buildroot/buildroot.git /buildroot
WORKDIR /buildroot
# 使用最小化配置 + Dropbear
RUN make qemu_aarch64_virt_defconfig \
&& sed -i 's/BR2_PACKAGE_OPENSSH=y/# BR2_PACKAGE_OPENSSH is not set/' .config \
&& echo 'BR2_PACKAGE_DROPBEAR=y' >> .config \
&& echo 'BR2_PACKAGE_DROPBEAR_CLIENT=y' >> .config \
&& make olddefconfig \
&& make -j$(nproc)
# 产物在 output/images/ 下
9.5 远程管理容器
通过 SSH 管理 Docker 容器
#!/bin/sh
# manage-container-via-ssh.sh - 通过 SSH 管理容器中的服务
CONTAINER_NAME="myapp"
SSH_PORT=2222
SSH_KEY="~/.ssh/id_ed25519"
# 在容器中执行命令
ssh_exec() {
dbclient -i "$SSH_KEY" -p "$SSH_PORT" \
-o "StrictHostKeyChecking=no" \
root@localhost "$@"
}
# 部署配置文件
deploy_config() {
local config_file="$1"
local remote_path="$2"
dbclient -i "$SSH_KEY" -p "$SSH_PORT" \
"$config_file" "root@localhost:$remote_path"
}
# 日志查看
view_logs() {
ssh_exec "tail -f /var/log/app.log"
}
# 服务重启
restart_service() {
ssh_exec "kill -HUP \$(cat /var/run/app.pid)"
}
case "$1" in
exec) shift; ssh_exec "$@" ;;
deploy) deploy_config "$2" "$3" ;;
logs) view_logs ;;
restart) restart_service ;;
*) echo "用法: $0 {exec|deploy|logs|restart} [args...]" ;;
esac
Docker + Dropbear + 端口转发
# 启动带端口转发的 Dropbear 容器
docker run -d \
--name dropbear-gateway \
-p 2222:22 \
-p 8080:80 \
-p 3306:3306 \
dropbear-alpine
# 通过 SSH 隧道访问容器内部服务
dbclient -L 8080:localhost:80 -L 3306:localhost:3306 \
-p 2222 root@localhost
# 或直接通过 Docker 端口映射
curl http://localhost:8080
mysql -h 127.0.0.1 -P 3306
9.6 容器安全最佳实践
安全检查清单
容器 SSH 安全检查:
┌──────────────────────────────────────────────────────┐
│ ✅ 使用公钥认证,禁用密码认证 (-s) │
│ ✅ 禁止 root 登录 (-w) │
│ ✅ 使用非 root 用户运行 │
│ ✅ 限制端口映射范围 │
│ ✅ 使用 read-only 文件系统(尽可能) │
│ ✅ 删除不必要的 capabilities │
│ ✅ 设置 no-new-privileges │
│ ✅ 使用 secrets 管理敏感数据 │
│ ✅ 定期更新镜像 │
│ ✅ 使用安全扫描工具检查镜像 │
│ ❌ 不要在生产环境使用密码认证 │
│ ❌ 不要暴露 SSH 端口到公网(除非必要) │
│ ❌ 不要在镜像中硬编码密钥或密码 │
└──────────────────────────────────────────────────────┘
使用 Docker Secrets
# docker-compose.yml - 使用 Docker Secrets
version: '3.8'
services:
dropbear:
image: dropbear-alpine
ports:
- "2222:22"
secrets:
- ssh_authorized_keys
- host_key_ed25519
- host_key_rsa
volumes:
- dropbear-keys:/etc/dropbear
command: >
sh -c "
cp /run/secrets/host_key_ed25519 /etc/dropbear/dropbear_ed25519_host_key &&
cp /run/secrets/host_key_rsa /etc/dropbear/dropbear_rsa_host_key &&
cp /run/secrets/ssh_authorized_keys /root/.ssh/authorized_keys &&
chmod 600 /etc/dropbear/* /root/.ssh/authorized_keys &&
dropbear -F -E -R -s -p 22
"
secrets:
ssh_authorized_keys:
file: ./secrets/authorized_keys
host_key_ed25519:
file: ./secrets/dropbear_ed25519_host_key
host_key_rsa:
file: ./secrets/dropbear_rsa_host_key
volumes:
dropbear-keys:
网络隔离
# docker-compose.yml - 网络隔离
version: '3.8'
services:
dropbear:
image: dropbear-alpine
ports:
- "127.0.0.1:2222:22" # 仅绑定到 localhost
networks:
- internal
- management
app:
image: myapp:latest
networks:
- internal
# 不暴露端口到宿主机
networks:
internal:
internal: true # 内部网络,无法访问外网
management:
# 管理网络,可访问外网
9.7 CI/CD 集成
Jenkins Agent SSH 容器
# Dockerfile.jenkins-dropbear - Jenkins SSH Agent
FROM alpine:3.19
RUN apk add --no-cache \
dropbear \
git \
openssh-client \
bash \
&& mkdir -p /etc/dropbear /home/jenkins/.ssh /var/run/dropbear
# 创建 Jenkins 用户
RUN adduser -D -s /bin/bash jenkins
# 生成主机密钥
RUN dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
# Jenkins 公钥将在运行时注入
RUN chown -R jenkins:jenkins /home/jenkins
EXPOSE 22
CMD dropbear -F -E -R -p 22 -w -U jenkins -G jenkins
# Jenkins 配置 SSH Agent
# Manage Jenkins → Manage Credentials → SSH Username with private key
# Username: jenkins
# Private Key: <Jenkins 用户的私钥>
# Host: dropbear-agent:22
自动化测试中的 SSH
# test-with-ssh.sh - 使用 Dropbear 容器进行 SSH 测试
#!/bin/bash
set -e
CONTAINER_NAME="test-ssh-$(date +%s)"
SSH_PORT=$(shuf -i 20000-30000 -n 1)
# 启动 Dropbear 容器
docker run -d \
--name "$CONTAINER_NAME" \
-p "$SSH_PORT:22" \
-v "$PWD/test-data:/data:ro" \
dropbear-alpine
# 等待 SSH 就绪
echo "等待 SSH 服务就绪..."
for i in $(seq 1 30); do
if dbclient -p "$SSH_PORT" -o "StrictHostKeyChecking=no" \
-o "UserKnownHostsFile=/dev/null" \
root@localhost "echo ready" 2>/dev/null; then
echo "SSH 已就绪"
break
fi
sleep 1
done
# 执行测试
echo "执行测试..."
dbclient -p "$SSH_PORT" root@localhost "ls /data"
dbclient -p "$SSH_PORT" root@localhost "cat /data/test.txt"
# 清理
docker rm -f "$CONTAINER_NAME"
echo "测试完成"
9.8 业务场景:IoT 设备仿真
# Dockerfile.iot-simulator - IoT 设备仿真器
FROM alpine:3.19
RUN apk add --no-cache \
dropbear \
busybox-extras \
curl \
jq \
&& mkdir -p /etc/dropbear /var/run/dropbear \
/opt/iot /var/log/iot
# 生成设备密钥
RUN dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
# 模拟设备信息
RUN echo '#!/bin/sh' > /usr/local/bin/device-info \
&& echo 'echo "=== IoT 设备信息 ==="' >> /usr/local/bin/device-info \
&& echo 'echo "型号: IoT-Sensor-2000"' >> /usr/local/bin/device-info \
&& echo 'echo "固件: v2.1.0"' >> /usr/local/bin/device-info \
&& echo 'echo "序列号: $(hostname)"' >> /usr/local/bin/device-info \
&& echo 'echo "运行时间: $(uptime -p)"' >> /usr/local/bin/device-info \
&& echo 'echo "内存: $(free -h | awk "/Mem/{print \\$3\\\"/\\\"\\$2}")"' >> /usr/local/bin/device-info \
&& chmod +x /usr/local/bin/device-info
# 模拟传感器数据
RUN echo '#!/bin/sh' > /usr/local/bin/read-sensor \
&& echo 'echo "温度: $(awk "BEGIN{srand(); printf \"%.1f\", 20+rand()*15}")°C"' >> /usr/local/bin/read-sensor \
&& echo 'echo "湿度: $(awk "BEGIN{srand(); printf \"%.0f\", 40+rand()*40}")%"' >> /usr/local/bin/read-sensor \
&& echo 'echo "压力: $(awk "BEGIN{srand(); printf \"%.0f\", 1000+rand()*30}")hPa"' >> /usr/local/bin/read-sensor \
&& chmod +x /usr/local/bin/read-sensor
EXPOSE 22
CMD ["dropbear", "-F", "-E", "-R", "-s", "-p", "22", "-m"]
# 运行 IoT 仿真器集群
for i in $(seq 1 5); do
docker run -d \
--name "iot-sensor-$i" \
-p "220$i:22" \
iot-simulator
done
# 从仿真器读取数据
for i in $(seq 1 5); do
echo "=== Sensor $i ==="
dbclient -p "220$i" root@localhost "read-sensor"
done
9.9 容器健康检查
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD dbclient -o "StrictHostKeyChecking=no" \
-o "UserKnownHostsFile=/dev/null" \
-p 22 root@localhost "echo ok" || exit 1
# docker-compose.yml 健康检查
services:
dropbear:
image: dropbear-alpine
healthcheck:
test: ["CMD", "dbclient", "-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-p", "22", "root@localhost", "echo", "ok"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
9.10 本章小结
| 要点 | 说明 |
|---|---|
| 镜像选择 | Alpine Linux 最小,Debian Slim 兼容 |
| 安全配置 | 公钥认证 + 非 root + 最小权限 |
| 网络隔离 | 绑定 localhost + 内部网络 |
| 密钥管理 | 挂载 secrets,不硬编码在镜像 |
| CI/CD | 适用于 Jenkins Agent、自动化测试 |
| IoT 仿真 | Dropbear 非常适合模拟嵌入式设备 SSH 接口 |
扩展阅读
上一章:dropbearkey 工具 | 下一章:最佳实践 →