强曰为道

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

第11章 跳板机与堡垒机

第11章 跳板机与堡垒机

11.1 跳板机与堡垒机概念

跳板机(Jump Host)

通过中间服务器间接访问目标服务器,常用于网络隔离环境。

[运维人员]  →  [跳板机]  →  [内网服务器]
  外网           DMZ/公网        内网

堡垒机(Bastion Host / PAM)

堡垒机在跳板机基础上增加了审计访问控制功能:

[运维人员]  →  [堡垒机]  →  [内网服务器]
                │
                ├── 身份认证
                ├── 权限控制
                ├── 会话录制
                ├── 命令审计
                └── 告警通知
功能跳板机堡垒机
网络跳转
访问控制基础细粒度
会话审计
命令录制
工单集成

11.2 ProxyJump(推荐方式)

基本语法

# 方式一:-J 参数
ssh -J user@jumphost user@target

# 方式二:ProxyJump 配置
# ~/.ssh/config
Host target
    ProxyJump user@jumphost

# 多跳
ssh -J user@jump1,user@jump2,user@jump3 user@target

工作原理

[本地]  →  [跳板机]  →  [目标服务器]
           代理连接       最终目标
           
ProxyJump 在本地完成密钥认证,
只在跳板机上建立 TCP 转发。

配置文件示例

# ~/.ssh/config

# 跳板机
Host bastion
    HostName bastion.example.com
    User jumpuser
    Port 22
    IdentityFile ~/.ssh/id_bastion

# 内网服务器(通过跳板机)
Host internal-*
    User admin
    ProxyJump bastion
    IdentityFile ~/.ssh/id_internal

# 具体内网服务器
Host internal-web
    HostName 10.0.0.10

Host internal-db
    HostName 10.0.0.20
    Port 5432

Host internal-cache
    HostName 10.0.0.30

使用:

# 直接使用别名,自动通过跳板机
ssh internal-web
ssh internal-db
ssh internal-cache

多层跳板

# ~/.ssh/config

Host dmz-jump
    HostName dmz.example.com
    User dmz-user

Host app-jump
    HostName 10.0.1.100
    User app-user
    ProxyJump dmz-jump

Host db-server
    HostName 10.0.2.50
    User dba
    ProxyJump dmz-jump,app-jump

文件传输通过跳板机

# scp 通过跳板机
scp -o ProxyJump=bastion localfile.txt user@internal:/tmp/

# rsync 通过跳板机
rsync -avz -e "ssh -J bastion" ./src/ user@internal:/dst/

# sftp 通过跳板机
sftp -o ProxyJump=bastion user@internal

11.3 ProxyCommand(旧方式)

ProxyCommand 是 ProxyJump 之前的方式,更灵活但配置更复杂:

# ~/.ssh/config

# 使用 netcat(推荐简单场景)
Host internal
    HostName 10.0.0.10
    ProxyCommand ssh bastion -W %h:%p

# 使用 netcat(如果跳板机没有 -W 支持)
Host internal
    ProxyCommand ssh bastion nc %h %p 2>/dev/null

# 使用 socat
Host internal
    ProxyCommand ssh bastion socat - TCP:%h:%p

ProxyCommand 变量

变量说明
%h目标主机名
%p目标端口
%r远程用户名
%l本地主机名

ProxyCommand vs ProxyJump

特性ProxyJumpProxyCommand
语法简单复杂
性能更好(原生支持)稍差
灵活性跳转场景任意代理命令
推荐⭐⭐⭐⭐⭐(兼容旧版本)

11.4 堡垒机配置

搭建简易堡垒机

步骤一:配置堡垒机 sshd

# /etc/ssh/sshd_config (堡垒机)

# 基本设置
Port 22
Protocol 2
UsePAM yes

# 认证
PubkeyAuthentication yes
PasswordAuthentication no
MaxAuthTries 3
LoginGraceTime 60

# 会话记录
ForceCommand /usr/local/bin/bastion-shell.sh

# 功能限制
AllowTcpForwarding local
X11Forwarding no
PermitTunnel no
GatewayPorts no

# 日志
LogLevel VERBOSE
SyslogFacility AUTH

步骤二:创建堡垒机 Shell

#!/bin/bash
# /usr/local/bin/bastion-shell.sh

# 记录会话信息
LOG_DIR="/var/log/bastion"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/session-$(whoami)-$(date +%Y%m%d-%H%M%S).log"

# 记录连接信息
echo "=== Session Start ===" >> "$LOG_FILE"
echo "User: $(whoami)" >> "$LOG_FILE"
echo "From: $SSH_CLIENT" >> "$LOG_FILE"
echo "Date: $(date)" >> "$LOG_FILE"
echo "Command: $SSH_ORIGINAL_COMMAND" >> "$LOG_FILE"
echo "========================" >> "$LOG_FILE"

if [ -n "$SSH_ORIGINAL_COMMAND" ]; then
    # 执行转发的命令
    echo "Executing: $SSH_ORIGINAL_COMMAND" >> "$LOG_FILE"
    eval "$SSH_ORIGINAL_COMMAND" 2>&1 | tee -a "$LOG_FILE"
else
    # 交互式会话
    exec script -q -f -a "$LOG_FILE"
fi

echo "=== Session End ===" >> "$LOG_FILE"
# 设置权限
sudo chmod 755 /usr/local/bin/bastion-shell.sh

步骤三:会话录制

# 使用 script 命令录制终端会话
# 已在 bastion-shell.sh 中实现

# 使用 ttyrec 录制(支持回放)
sudo apt install ttyrec

# 录制
ttyrec /var/log/bastion/session-$USER-$(date +%s).ttyrec

# 回放
ttyplay /var/log/bastion/session-xxx.ttyrec

11.5 集中化访问控制

使用 authorized_keys 限制跳板机用户

# 堡垒机上 ~/.ssh/authorized_keys

# 只允许通过跳板机连接内网服务器
from="10.0.0.0/8",command="/usr/local/bin/bastion-shell.sh",permitopen="10.0.0.0/8:22",no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3... admin@workstation

使用 SSH 证书 + 堡垒机

# 1. 所有用户向 CA 申请证书
# 2. 堡垒机信任 CA
# 3. 内网服务器也信任 CA

# 用户证书
ssh-keygen -s ca_user_key -I "admin-bastion" -n "admin" -V "+8h" id_work.pub

# 堡垒机配置
TrustedUserCAKeys /etc/ssh/ca_user_key.pub

# 内网服务器配置
TrustedUserCAKeys /etc/ssh/ca_user_key.pub

11.6 自动化堡垒机部署

Ansible 部署

# deploy-bastion.yml
---
- name: Deploy bastion host
  hosts: bastion
  become: true
  vars:
    bastion_users:
      - admin
      - operator1
      - operator2
  
  tasks:
    - name: Install required packages
      apt:
        name:
          - openssh-server
          - auditd
          - script
        state: present

    - name: Configure sshd
      template:
        src: sshd_config.j2
        dest: /etc/ssh/sshd_config
        validate: "sshd -t -f %s"
      notify: restart sshd

    - name: Create bastion shell script
      template:
        src: bastion-shell.sh.j2
        dest: /usr/local/bin/bastion-shell.sh
        mode: '0755'

    - name: Create bastion users
      user:
        name: "{{ item }}"
        shell: /usr/local/bin/bastion-shell.sh
        groups: bastion
        append: yes
      loop: "{{ bastion_users }}"

    - name: Deploy CA public key
      copy:
        src: ca_user_key.pub
        dest: /etc/ssh/ca_user_key.pub
        mode: '0644'

    - name: Configure auditd rules
      copy:
        content: |
          -w /usr/local/bin/bastion-shell.sh -p x -k bastion-shell
          -w /var/log/bastion -p wa -k bastion-log
        dest: /etc/audit/rules.d/bastion.rules
      notify: restart auditd

  handlers:
    - name: restart sshd
      systemd:
        name: sshd
        state: restarted

    - name: restart auditd
      systemd:
        name: auditd
        state: restarted

11.7 多人共享跳板机管理

问题分析

多人共享跳板机时的常见问题:

问题风险
共享 root 密码无法追踪个人操作
共享 SSH 密钥密钥泄露影响所有人
无操作审计出了问题无法追溯
无权限隔离所有人权限相同

解决方案

# 1. 每个人使用独立账户
# 2. 使用 sudo 限制权限
# 3. 使用 SSH 证书控制访问
# 4. 启用会话录制和审计

# /etc/sudoers.d/bastion-operators
# 运维人员可以 sudo 到目标用户
operator1 ALL=(appuser) NOPASSWD: /usr/bin/ssh internal-*
operator2 ALL=(appuser) NOPASSWD: /usr/bin/ssh internal-*

11.8 场景实战

场景一:通过跳板机连接数据库

# ~/.ssh/config
Host db-jump
    HostName bastion.example.com
    User dba

Host internal-mysql
    HostName 10.0.0.50
    User dba
    ProxyJump db-jump
    LocalForward 3306 localhost:3306
    RequestTTY no

# 使用
ssh internal-mysql -fN
mysql -h 127.0.0.1 -P 3306 -u root -p

场景二:Ansible 通过跳板机管理内网

# ansible.cfg
[ssh_connection]
ssh_args = -o ProxyJump=[email protected] -o ControlMaster=auto -o ControlPersist=60s
pipelining = True

# inventory
[internal]
web01 ansible_host=10.0.0.10
web02 ansible_host=10.0.0.11
db01 ansible_host=10.0.0.20

场景三:Git 通过跳板机

# ~/.ssh/config
Host github.com
    HostName github.com
    User git
    ProxyCommand ssh bastion -W %h:%p
    IdentityFile ~/.ssh/id_github

扩展阅读


下一章: 第12章 自动化运维集成 → 学习 sshpass、expect、Ansible 等自动化工具。