
gnzsnz/bastionDocker化的SSH堡垒机🏯,具有强化的默认配置。SSH堡垒机是一种可从互联网访问的跳板服务器,用于访问私有网络中的服务。部署堡垒机后,您可以通过它访问私有网络服务。
更多详情请查看GitHub仓库
按照以下步骤部署运行SSH堡垒机:
data文件夹中复制authorized_keys文件。我们将创建两个用户,并假设他们的/home/user_name/.ssh/authorized_keys已存在bash# 创建主目录 export USERS=devops,bastion mkdir $PWD/data/home/{$USERS}/.ssh # 复制authorized_keys文件示例 cp /home/{$USERS}/.ssh/authorized_keys $PWD/data/{$USERS}/.ssh
data文件夹。这是创建SSH堡垒机所需目录结构的必要步骤,详见准备工作bashdocker run -it --rm --env-file .env \ -v $PWD/data:/data \ gnzsnz/bastion /provision.sh
bashdocker compose up
bashssh -J devops@bastion:22222 devops@remote_host
上述命令指示ssh创建到-J参数指定服务器(此处为devops@bastion:22222)的连接,连接成功后从堡垒机创建到remote_host的另一个连接。从客户端角度看,这就像直接连接到remote_host。
克隆Git仓库作为模板并设置偏好:
bashgit clone [***] cp .env-dist .env nano .env # 编辑环境变量 cp docker-compose.yml-dist docker-compose.yml nano docker-compose.yml # 编辑docker compose文件 docker compose config # 验证compose文件 # 设置authorized keys(假设使用bastion用户) mkdir -p $PWD/data/home/bastion/.ssh cp authorized_keys $PWD/data/home/bastion/.ssh # 运行准备脚本 docker run -it --rm -v $PWD/data:/data --env-file .env \ gnzsnz/bastion /provision.sh # 启动SSH堡垒机 docker compose up -d && docker compose logs -ft
下文将介绍可用的环境变量、如何构建镜像、准备工作的更多细节、如何运行容器、用户访问管理、如何设置SSH客户端、SSH堡垒机的多种使用场景、多因素认证以及证书机构。
.env文件中可用的变量如下:
| 变量 | 默认值 | 描述 |
|---|---|---|
| APT_PROXY | 空 | 定义可选的APT代理以加速镜像构建,格式为[***] |
| SSH_LISTEN_PORT | 22222 | 主机对外发布端口 |
| USERS | bastion | 逗号分隔的用户列表,例如USERS=bastion,devops,准备脚本将创建此变量中定义的用户 |
| USER_SHELL | /usr/sbin/nologin | 必需,用于设置用户shell |
| BANNER_ENABLED | no | 启用SSH横幅,默认显示bastion_banner.txt。要更改横幅需添加挂载点-v path/to/new_banner.txt:/bastion_banner.txt |
| TOTP_ENABLED | no | 启用TOTP,支持Google Authenticator或Microsoft Authenticator |
| TOTP_ISSUER | Bastion | TOTP应用的描述 |
| TOTP_QR_ENCODE | UTF8 | TOTP URI QR的编码方式,使用qrencoder |
| CA_ENABLED | 'no' | 设置为'yes'启用SSH CA模式 |
| SSHD_HOST_CERT | '/etc/ssh/ssh_host_ed25519_key-cert.pub' | CA签名的主机证书,需复制到./data/etc/ssh目录 |
| SSHD_USER_CA | '/etc/ssh/user_ca.pub' | 公共CA密钥,需复制到./data/etc/ssh目录 |
| IMAGE_VERSION | 构建时用于标记镜像的版本 | |
| BASE_VERSION | jammy | Ubuntu基础镜像版本,用于构建 |
设置.env文件后,检查配置是否正确:
bashdocker compose config
确保USERS变量设置了将使用SSH堡垒机的用户。
除环境变量外,还可通过传递命令行参数或设置配置文件修改SSH堡垒机行为,详见运行容器部分。
您可以选择按照以下步骤构建镜像:
bashdocker compose build
如果定义了APT_PROXY,构建过程将使用它加速构建。
可在Docker Hub和GitHub容器 registry获取现成的堡垒机镜像。示例docker-compose文件将从Docker Hub拉取镜像。
使用容器前,需准备./data主机目录,包含必要数据。可通过运行准备脚本来完成。/data目录包含SSH所需的所有配置、主机和用户密钥以及用户访问权限。准备脚本将执行以下任务:
USERS环境变量创建用户/usr/sbin/nologin(除非您明确了解需求,否则不建议更改)./data绑定挂载已准备,将使用现有文件容器将以只读模式挂载所有这些文件(除非使用TOTP,此时/home需要写权限)
设置authorized keys:
bash# 创建主目录 export USERS=devops,bastion mkdir $PWD/data/home/{$USERS}/.ssh # 复制authorized_keys文件示例 cp /home/{$USERS}/.ssh/authorized_keys $PWD/data/{$USERS}/.ssh
这将复制devops和bastion用户的公钥。
运行准备脚本:
bashdocker run -it --rm --env-file .env \ -v $PWD/data:/data \ gnzsnz/bastion /provision.sh
运行准备脚本后,数据目录将包含运行容器所需的所有数据。注意数据目录的所有者和权限将反映data/etc/passwd中的UID和GID,可能需要sudo进行修改。
如果手动修改data/etc内容,可能需要重新运行准备脚本以更新签名。
编辑docker-compose.yml文件,默认值通常可正常工作。可定义DNS或'extra_hosts',使SSH客户端能够使用服务器名称而非IP地址。
yamlversion: "3.6" services: bastion: build: context: . platforms: - "linux/amd64" - "linux/arm64" - "linux/arm/v7" args: APT_PROXY: ${APT_PROXY} BASE_VERSION: ${BASE_VERSION} IMAGE_VERSION: ${IMAGE_VERSION} image: gnzsnz/bastion:${IMAGE_VERSION}-${BASE_VERSION} restart: unless-stopped ports: - ${SSH_LISTEN_PORT}:22 # 可选 # dns: ${DNS} # extra_hosts: # - host 10.10.0.5 # command: ["-o ForwardX11=yes "] environment: - USERS=${USERS} - USER_SHELL=${USER_SHELL} - TOTP_ENABLED=${TOTP_ENABLED} - TOTP_ISSUER=${TOTP_ISSUER} - TOTP_QR_ENCODE=${TOTP_QR_ENCODE} - CA_ENABLED=${CA_ENABLED} - SSHD_HOST_CERT=${SSHD_HOST_CERT} - SSHD_USER_CA=${SSHD_USER_CA} - BANNER_ENABLED=${BANNER_ENABLED} volumes: - $PWD/data/etc/passwd:/etc/passwd:ro - $PWD/data/etc/shadow:/etc/shadow:ro - $PWD/data/etc/group:/etc/group:ro - $PWD/data/etc/ssh:/etc/ssh:ro - $PWD/data/home:/home:ro
验证所有设置是否正确(是否已设置.env文件):
bashdocker compose config
容器启动时将:
运行容器:
bashdocker compose up -d; docker-compose logs -f
如果手动修改数据目录,可能需要重新运行准备脚本以生成更新的校验和,确保启动时验证通过。
可通过在命令行或docker-compose.yml的command元素中设置参数更改堡垒机行为,任何有效的sshd选项均可使用。上述示例docker文件包含允许X转发的行:command: ["-o ForwardX11=yes "]。
另一种选择是在/data/etc/ssh/sshd_config.d/中添加额外配置,堡垒机将读取这些文件。
堡垒机遵循OpenSSH的认证机制。通常需要为每个用户在authorized_keys文件中设置公钥。从管理authorized_keys文件的角度,更简单的方法是设置证书机构(CA)。这需要额外步骤生成和管理证书,但无需在authorized_keys文件中添加行,也无需为每个主机设置known_host记录,详见证书机构部分。
添加更多用户的最简单方法是编辑.env文件,设置USERS并重新运行准备模式,它将添加到现有/etc/passwd文件并设置authorized keys:
bashdocker run -it --rm -e USERS=new_user,another_user \ -v $PWD/data:/data \ gnzsnz/bastion /provision.sh
禁用现有用户:
bashdocker run -it --rm -v $PWD/data:/data \ gnzsnz/bastion adduser --disable-login user_name
可按照准备工作部分所述添加authorized_keys。
如果已按照本文档操作,现在应该已启动并运行SSH堡垒机容器。您可以通过堡垒机访问SSH服务器。
从简单场景开始,使用-J选项建立连接,或设置ssh配置指定通过ProxyJump连接到server:
bashssh -J devops@bastion_host:22222 devops@server # 如果在~/.ssh/config中设置了ProxyJump ssh devops@server
还可使用scp、rsync、sftp、端口转发或socks代理:
bash# scp scp -J devops@bastion_host:22222 file.gz devops@server:/tmp # 如果在配置文件中设置了ProxyJump,无需使用-J scp file.gz devops@server:/tmp # rsync rsync -rtva devops@server:/tmp/file.gz /tmp # sftp sftp -J devops@bastion_host:22222 file.gz devops@server:/tmp sftp file.gz devops@server:/tmp sftp devops@server # 端口转发(转发发生在服务器上,堡垒机仅作为跳板) ssh -N -L 8888:localhost:80 -J devops@bastion_host:22222 pgsql.example.com # 不使用-J ssh -N -L 8888:localhost:80 *** # 远程转发(将本地:80转发到远程的localhost:8888) ssh -N -R 80:localhost:8888 *** # 如果在配置中设置了本地或远程转发,只需执行 ssh rf_app ssh lf_app # socks代理 ssh -J devops@bastion_host:22222 -D 1337 -f -N *** ssh myproxy
详见下一节客户端设置示例。
一个值得特别注意的场景是作为端口转发的sidecar容器:
>|< _____________ __________ | | 堡垒机容器 | | 客户端 | ---|--- | | ----\ ---------- | ------------- | | _____________ | | | 应用容器 | ---/ | | | | ------------- | >|< 应用到堡垒机:ssh -R 8888:localhost:8888 bastion 客户端到堡垒机:ssh -L 8888:localhost:8888 bastion
在上述场景中,应用需要暴露8888端口但直接暴露不安全(如VNC)。在应用容器中,可安装SSH客户端在SSH堡垒机上创建远程转发,而客户端创建本地转发。注意此时我们直接连接到堡垒机,而非作为ProxyJump使用,这是允许的,因为未打开shell会话。
此场景中,应用容器无需安装sshd服务器,只需SSH客户端。只需将堡垒机端口暴露到互联网。通过适当的SSH客户端配置,两种转发连接都易于设置。应用容器可专注于核心功能,堡垒机容器负责创建安全连接。
可配置~/.ssh/config文件简化客户端命令:
### 堡垒机主机 Host bastion-host-nickname HostName bastion-hostname AddKeysToAgent yes ForwardAgent yes ### 远程主机 Host remote-host-nickname HostName remote-hostname ProxyJump bastion-host-nickname AddKeysToAgent yes ForwardAgent yes # 远程转发示例 Host rf_app Hostname app.example.com ProxyJump bastion-host-nickname # local_host:local_port:remote_host:remote_port # local是SSH客户端视角,remote是SSH服务器可访问的任何主机 RemoteForward localhost:5432 localhost:5432 SessionType none ForkAfterAuthentication yes ExitOnForwardFailure yes IdentitiesOnly yes CertificateFile ~/.ssh/id_ed25519-cert.pub IdentityFile ~/.ssh/id_ed25519 # 本地转发示例 Host lf_pgsql Hostname pgsql.example.com ProxyJump jump_host_nickname # local_host:local_port:remote_host:remote_port # local是SSH客户端视角,remote是SSH服务器可访问的任何主机 LocalForward localhost:5432 localhost:5432 SessionType none ForkAfterAuthentication yes ExitOnForwardFailure yes IdentitiesOnly yes CertificateFile ~/.ssh/id_ed25519-cert.pub IdentityFile ~/.ssh/id_ed25519 # socks动态代理示例 Host myproxy Hostname server.example.com Port 2222 ProxyJump bastion-host-nickname DynamicForward 1337 SessionType none ForkAfterAuthentication yes ExitOnForwardFailure yes IdentitiesOnly yes CertificateFile ~/.ssh/id_ed25519-cert.pub IdentityFile




manifest unknown 错误
TLS 证书验证失败
DNS 解析超时
410 错误:版本过低
402 错误:流量耗尽
身份认证失败错误
429 限流错误
凭证保存错误
来自真实用户的反馈,见证轩辕镜像的优质服务