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

SSH 服务器完全指南 / 第15章 最佳实践与规范

第15章 最佳实践与规范

15.1 生产环境 SSH 安全清单

部署 SSH 服务器到生产环境前,请逐项检查:

高优先级(必须)

#检查项标准
1禁用密码认证PasswordAuthentication no
2禁止 root 密码登录PermitRootLogin prohibit-passwordno
3使用强密钥类型Ed25519 或 RSA 4096
4禁用空密码PermitEmptyPasswords no
5文件权限正确私钥 600,.ssh 目录 700
6强加密算法只允许现代算法
7限制认证尝试MaxAuthTries 3
8服务端配置语法验证sshd -t 通过

中优先级(强烈推荐)

#检查项标准
9安装 Fail2Ban配置自动封禁
10限制用户/组访问AllowGroups 白名单
11禁用不需要的功能X11/TCP 转发
12启用心跳保活ClientAliveInterval 300
13关闭 DNS 反向解析UseDNS no
14关闭 GSSAPIGSSAPIAuthentication no
15日志级别 INFOLogLevel INFO
16配置 Include 拆分模块化管理

低优先级(推荐)

#检查项标准
17使用 SSH 证书集中化密钥管理
18配置登录横幅Banner /etc/ssh/banner.txt
19审计日志auditd 集成
20定期密钥轮换自动化密钥更换

自动化检查脚本

#!/bin/bash
# ssh-security-audit.sh

CONFIG="/etc/ssh/sshd_config"
PASS=0
FAIL=0
WARN=0

check() {
    local desc="$1"
    local expected="$2"
    local actual="$3"
    
    if [ "$actual" = "$expected" ]; then
        echo "✅ $desc"
        ((PASS++))
    else
        echo "❌ $desc (期望: $expected, 实际: $actual)"
        ((FAIL++))
    fi
}

warn_check() {
    local desc="$1"
    local expected="$2"
    local actual="$3"
    
    if [ "$actual" = "$expected" ]; then
        echo "✅ $desc"
        ((PASS++))
    else
        echo "⚠️  $desc (建议: $expected, 实际: $actual)"
        ((WARN++))
    fi
}

echo "=============================="
echo " SSH 安全审计报告"
echo " 日期: $(date)"
echo " 服务器: $(hostname)"
echo "=============================="
echo ""

# 配置语法
echo "--- 配置检查 ---"
if sshd -t 2>/dev/null; then
    check "配置语法" "正确" "正确"
else
    check "配置语法" "正确" "错误"
fi

# 获取生效配置
get_conf() {
    sshd -T 2>/dev/null | grep -i "^$1 " | awk '{print $2}'
}

# 认证检查
echo ""
echo "--- 认证安全 ---"
check "密码认证" "no" "$(get_conf passwordauthentication)"
check "空密码" "no" "$(get_conf permitemptypasswords)"
check "Root 密码登录" "prohibit-password|no" "$(get_conf permitrootlogin)"
check "公钥认证" "yes" "$(get_conf pubkeyauthentication)"
warn_check "最大认证尝试" "3" "$(get_conf maxauthtries)"

# 功能限制
echo ""
echo "--- 功能限制 ---"
warn_check "X11 转发" "no" "$(get_conf x11forwarding)"
warn_check "TCP 转发" "no" "$(get_conf allowtcpforwarding)"

# 网络配置
echo ""
echo "--- 网络配置 ---"
warn_check "DNS 反向解析" "no" "$(get_conf usedns)"
warn_check "GSSAPI" "no" "$(get_conf gssapiauthentication)"
warn_check "心跳保活" "300" "$(get_conf clientaliveinterval)"

# 日志
echo ""
echo "--- 日志配置 ---"
warn_check "日志级别" "INFO" "$(get_conf loglevel)"

# 密钥检查
echo ""
echo "--- 主机密钥 ---"
for key in /etc/ssh/ssh_host_*_key; do
    [ -f "$key" ] || continue
    PERM=$(stat -c %a "$key")
    if [ "$PERM" = "600" ]; then
        echo "✅ $key 权限正确 (600)"
        ((PASS++))
    else
        echo "❌ $key 权限错误 ($PERM)"
        ((FAIL++))
    fi
done

# 检查弱密钥
if [ -f /etc/ssh/ssh_host_dsa_key ]; then
    echo "❌ 发现 DSA 主机密钥(已不安全)"
    ((FAIL++))
fi

# 检查 Fail2Ban
echo ""
echo "--- Fail2Ban ---"
if systemctl is-active fail2ban &>/dev/null; then
    check "Fail2Ban 状态" "active" "active"
else
    warn_check "Fail2Ban 状态" "active" "inactive"
fi

# 总结
echo ""
echo "=============================="
echo " 审计结果"
echo " 通过: $PASS"
echo " 失败: $FAIL"
echo " 警告: $WARN"
echo "=============================="

if [ $FAIL -gt 0 ]; then
    echo "⛔ 存在安全问题,请立即修复!"
    exit 1
elif [ $WARN -gt 0 ]; then
    echo "⚠️  存在建议改进项"
    exit 0
else
    echo "🎉 所有检查通过!"
    exit 0
fi

15.2 密钥生命周期管理

密钥生成规范

密钥用途类型位数密码保护存储位置
个人管理Ed25519256✅ 强密码~/.ssh/
CI/CD 部署Ed25519256❌ (自动化)密钥管理系统
服务账户Ed25519256受限目录
证书 CAEd25519256✅ 强密码离线/HSM
兼容旧系统RSA4096✅ 强密码~/.ssh/

密钥轮换策略

# 密钥轮换周期建议
# ┌─────────────────┬──────────┐
# │ 密钥类型         │ 轮换周期  │
# ├─────────────────┼──────────┤
# │ 个人用户密钥     │ 每年     │
# │ 服务账户密钥     │ 每季度   │
# │ CI/CD 密钥       │ 每月     │
# │ 临时访问密钥     │ 每次     │
# │ SSH CA 密钥      │ 每 2 年  │
# └─────────────────┴──────────┘

自动化密钥轮换

#!/bin/bash
# rotate-ssh-keys.sh

SERVER="$1"
USER="${2:-deploy}"
KEY_DIR="$HOME/.ssh"
NEW_KEY="$KEY_DIR/id_ed25519_$(date +%Y%m%d)"

if [ -z "$SERVER" ]; then
    echo "Usage: $0 <server> [user]"
    exit 1
fi

echo "=== SSH 密钥轮换 ==="
echo "服务器: $SERVER"
echo "用户: $USER"
echo ""

# 1. 生成新密钥
echo "[1/5] 生成新密钥..."
ssh-keygen -t ed25519 -f "$NEW_KEY" -N "" -C "$USER@$(hostname)-$(date +%Y%m%d)" -q

# 2. 部署新密钥
echo "[2/5] 部署新密钥..."
ssh-copy-id -i "$NEW_KEY.pub" "$USER@$SERVER" 2>/dev/null
if [ $? -ne 0 ]; then
    echo "❌ 部署失败"
    rm -f "$NEW_KEY" "$NEW_KEY.pub"
    exit 1
fi

# 3. 验证新密钥
echo "[3/5] 验证新密钥..."
ssh -i "$NEW_KEY" -o BatchMode=yes "$USER@$SERVER" "echo OK" 2>/dev/null
if [ $? -ne 0 ]; then
    echo "❌ 新密钥验证失败"
    exit 1
fi

# 4. 更新配置
echo "[4/5] 更新本地配置..."
if [ -f "$KEY_DIR/id_ed25519" ]; then
    mv "$KEY_DIR/id_ed25519" "$KEY_DIR/id_ed25519.old.$(date +%Y%m%d)"
    mv "$KEY_DIR/id_ed25519.pub" "$KEY_DIR/id_ed25519.pub.old.$(date +%Y%m%d)"
fi
mv "$NEW_KEY" "$KEY_DIR/id_ed25519"
mv "$NEW_KEY.pub" "$KEY_DIR/id_ed25519.pub"

# 5. 清理服务器上的旧密钥
echo "[5/5] 清理服务器旧密钥..."
echo "(保留旧密钥 7 天后删除)"
# 可以添加自动化清理逻辑

echo ""
echo "✅ 密钥轮换完成"
echo "旧密钥已备份为 id_ed25519.old.$(date +%Y%m%d)"

密钥清理

#!/bin/bash
# cleanup-old-keys.sh

KEY_DIR="$HOME/.ssh"
DAYS=30

echo "=== 清理 $DAYS 天前的旧密钥 ==="

find "$KEY_DIR" -name "*.old.*" -mtime +$DAYS -type f | while read file; do
    echo "删除: $file"
    rm -f "$file"
done

echo "清理完成"

15.3 审计日志规范

日志收集架构

[SSH 服务器]  →  [rsyslog/Filebeat]  →  [集中日志平台]
  /var/log/auth.log                    ELK/Splunk/Graylog

配置集中日志

# /etc/rsyslog.d/ssh.conf
# 将 SSH 日志发送到远程服务器

auth,authpriv.*  @logserver.example.com:514

# 或使用 TCP(更可靠)
auth,authpriv.*  @@logserver.example.com:514

auditd SSH 审计规则

# /etc/audit/rules.d/ssh.rules

# 监控 SSH 配置文件变更
-w /etc/ssh/sshd_config -p wa -k ssh_config_change
-w /etc/ssh/sshd_config.d/ -p wa -k ssh_config_change

# 监控主机密钥
-w /etc/ssh/ssh_host_ -p wa -k ssh_hostkey_change

# 监控 authorized_keys
-w /home/ -p wa -k ssh_key_change -F dir=/home
-w /root/.ssh/authorized_keys -p wa -k ssh_key_change

# 监控 SSH 登录事件
-w /usr/sbin/sshd -p x -k ssh_daemon
# 查看审计日志
sudo ausearch -k ssh_config_change
sudo ausearch -k ssh_key_change

# 生成审计报告
sudo aureport --auth --summary

关键日志事件

事件日志模式优先级
认证成功Accepted publickey/passwordINFO
认证失败Failed password/publickeyWARNING
无效用户Invalid userWARNING
Root 登录Accepted publickey for rootNOTICE
配置变更sshd_config 修改ALERT
主机密钥变更主机密钥修改ALERT
服务启动/停止sshd 启动/停止INFO

日志告警

#!/bin/bash
# ssh-alert.sh - SSH 安全告警

LOG="/var/log/auth.alert.log"
THRESHOLD=10  # 10 分钟内失败次数
WINDOW=600

# 检查失败登录
FAILURES=$(journalctl -u sshd --since "-${WINDOW} seconds" | \
    grep "Failed password" | wc -l)

if [ $FAILURES -ge $THRESHOLD ]; then
    TOP_IP=$(journalctl -u sshd --since "-${WINDOW} seconds" | \
        grep "Failed password" | \
        awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -1)
    
    echo "⚠️ SSH 暴力破解告警" | tee -a "$LOG"
    echo "时间: $(date)" | tee -a "$LOG"
    echo "失败次数: $FAILURES (最近 ${WINDOW}秒)" | tee -a "$LOG"
    echo "主要攻击源: $TOP_IP" | tee -a "$LOG"
    
    # 发送告警(邮件/Slack/企业微信)
    # mail -s "SSH Brute Force Alert" [email protected] < "$LOG"
fi

15.4 运维规范

SSH 操作规范

规范说明
禁止共享密钥每人使用独立密钥
禁止密码认证生产环境必须使用密钥
禁止 root 直登使用普通用户 + sudo
保持备用连接修改配置时保持一个连接
记录操作日志重要操作需要记录
使用跳板机所有访问通过跳板机
密钥定期轮换至少每年一次
离职立即撤销员工离职当天撤销所有访问

SSH 配置管理规范

# 1. 版本控制
# 将 sshd_config 纳入 Git 管理
cd /etc/ssh
git init
git add sshd_config sshd_config.d/
git commit -m "Initial SSH config"

# 2. 变更流程
# a. 在测试环境验证
# b. 代码审查
# c. 灰度发布(先部分服务器)
# d. 全量发布
# e. 验证生效

# 3. 回滚准备
# 始终保留备份
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%Y%m%d)

运维工具链

┌──────────────────────────────────────────────┐
│               SSH 运维工具链                  │
├──────────────────────────────────────────────┤
│                                              │
│  密钥管理        访问控制        审计日志    │
│  ├─ ssh-keygen   ├─ 堡垒机      ├─ auditd    │
│  ├─ SSH CA       ├─ AllowGroups ├─ rsyslog   │
│  ├─ Vault        ├─ Match 块    ├─ ELK       │
│  └─ 自动轮换     └─ 证书认证    └─ 告警      │
│                                              │
│  自动化          安全防护        监控        │
│  ├─ Ansible      ├─ Fail2Ban    ├─ 端口监控  │
│  ├─ 批量脚本     ├─ iptables    ├─ 连接数    │
│  └─ CI/CD        └─ 端口敲门    └─ 认证日志  │
│                                              │
└──────────────────────────────────────────────┘

15.5 合规性要求

常见安全标准对 SSH 的要求

标准SSH 要求
等保 2.0密码复杂度、登录失败锁定、访问控制、审计日志
PCI DSS加密传输、密钥管理、访问日志、定期轮换
ISO 27001访问控制、密钥生命周期、安全配置
CIS Benchmark具体的安全配置参数

CIS Benchmark 核心检查项

# CIS SSH 检查项(摘要)

# 1. 加密配置
KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected]

# 2. 认证配置
MaxAuthTries 4
PermitEmptyPasswords no
PermitUserEnvironment no

# 3. 访问控制
PermitRootLogin no
AllowGroups ssh-users
Protocol 2

# 4. 日志
LogLevel INFO

15.6 生产环境推荐配置模板

# /etc/ssh/sshd_config
# ================================
# 生产环境 SSH 配置模板
# 版本: 2.0
# 更新日期: 2025-01-01
# ================================

# ===== 网络设置 =====
Port 22
AddressFamily inet
ListenAddress 0.0.0.0

# ===== 主机密钥 =====
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

# ===== 加密算法 =====
KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers [email protected],[email protected],[email protected],aes256-ctr
MACs [email protected],[email protected],[email protected]
HostKeyAlgorithms ssh-ed25519,[email protected],rsa-sha2-512,rsa-sha2-256

# ===== 认证设置 =====
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
MaxAuthTries 3
LoginGraceTime 60
AuthenticationMethods publickey

# ===== 用户访问控制 =====
PermitRootLogin no
AllowGroups ssh-users
DenyUsers root
MaxSessions 10
MaxStartups 10:30:60

# ===== 会话管理 =====
ClientAliveInterval 300
ClientAliveCountMax 2
TCPKeepAlive yes

# ===== 功能限制 =====
X11Forwarding no
AllowTcpForwarding no
PermitTunnel no
GatewayPorts no
AllowStreamLocalForwarding no
PermitUserEnvironment no

# ===== 日志 =====
SyslogFacility AUTH
LogLevel INFO

# ===== PAM =====
UsePAM yes

# ===== 性能 =====
UseDNS no
GSSAPIAuthentication no

# ===== 杂项 =====
PrintMotd no
PrintLastLog yes
Banner /etc/ssh/banner.txt

# ===== 子配置 =====
Include /etc/ssh/sshd_config.d/*.conf

# ===== 条件配置 =====

# 部署用户(CI/CD 专用)
Match User deploy
    AllowGroups deploy
    ForceCommand /opt/deploy/entry.sh
    AllowTcpForwarding no
    X11Forwarding no
    PermitTTY no
    PermitRootLogin no

# SFTP 用户
Match Group sftp-only
    ChrootDirectory /home/%u
    ForceCommand internal-sftp -l INFO
    AllowTcpForwarding no
    X11Forwarding no
    PermitTTY no
    PermitRootLogin no

# 管理员(仅从管理网段)
Match Group admins Hostaddress 10.0.100.0/24
    AllowTcpForwarding local
    X11Forwarding yes
    MaxSessions 20
    PermitRootLogin prohibit-password

15.7 持续改进

定期审计计划

频率检查内容
每日查看登录日志、Fail2Ban 状态
每周检查活跃用户和密钥
每月运行安全审计脚本、更新配置
每季度密钥轮换、清理过期账户
每年全面安全评估、更新安全策略

自动化监控

#!/bin/bash
# ssh-monitor-cron

# 放入 crontab
# */5 * * * * /opt/scripts/ssh-monitor-cron.sh

LOG="/var/log/ssh-monitor.log"

# 检查 SSH 服务状态
if ! systemctl is-active sshd &>/dev/null; then
    echo "[CRITICAL] $(date) SSH 服务停止!" >> "$LOG"
    systemctl restart sshd
fi

# 检查 Fail2Ban 状态
if ! systemctl is-active fail2ban &>/dev/null; then
    echo "[WARNING] $(date) Fail2Ban 服务停止!" >> "$LOG"
    systemctl restart fail2ban
fi

# 检查当前连接数
CONN=$(ss -tnp | grep :22 | wc -l)
if [ $CONN -gt 100 ]; then
    echo "[WARNING] $(date) SSH 连接数过多: $CONN" >> "$LOG"
fi

# 检查磁盘空间(日志文件)
LOG_SIZE=$(du -sm /var/log/auth.log 2>/dev/null | awk '{print $1}')
if [ "${LOG_SIZE:-0}" -gt 1000 ]; then
    echo "[WARNING] $(date) auth.log 文件过大: ${LOG_SIZE}MB" >> "$LOG"
fi

15.8 本章总结

SSH 安全加固核心原则

1. 最小权限原则
   - 只开放必需的访问
   - 使用 AllowGroups 限制用户
   - 禁用不需要的功能

2. 纵深防御
   - 防火墙 + SSH 配置 + Fail2Ban
   - 密钥认证 + 多因素认证
   - 跳板机 + 审计日志

3. 默认拒绝
   - PasswordAuthentication no
   - PermitRootLogin no
   - AllowTcpForwarding no

4. 可审计性
   - 集中日志收集
   - 操作记录
   - 定期审计

5. 自动化
   - 配置管理(Ansible)
   - 密钥轮换(脚本)
   - 安全检查(CI/CD)

扩展阅读


🎉 恭喜你完成了 SSH 服务器完全指南的全部 15 章内容!

建议将本教程作为参考手册,在实际工作中按需查阅相关章节。 SSH 安全是一个持续改进的过程,定期审计和更新配置是保持安全的关键。