详解SSH服务器的安全配置方法

描述

背景与问题

SSH(Secure Shell)是 Linux 系统远程管理的首选协议,几乎所有服务器的远程管理都依赖 SSH。从简单的命令行登录到文件传输(SFTP)、端口转发、远程命令执行,SSH 是运维工程师每天都要使用的核心工具。

然而,SSH 默认配置存在安全隐患:默认端口 22 面临大量暴力破解攻击;允许密码认证容易被暴力破解或密码泄露;允许 root 直接登录风险极高;SSH 密钥默认没有设置过期机制。 另一方面,很多运维人员在配置 SSH 安全策略时过于激进,禁用了所有登录方式或删除了所有备份方法,结果在网络波动或配置失误时把自己锁在系统外面。

本文详细讲解 SSH 的安全配置方法,同时提供防止把自己锁在系统外面的保障措施。内容包括 SSH 协议原理、认证方式配置、密钥管理、网络层防护、配置文件详解、常见错误分析和应急恢复方法。

1 SSH 协议基础

1.1 SSH 协议版本

SSH 有两个主要版本:SSHv1 和 SSHv2。目前 SSHv1 已完全废弃,所有现代系统都使用 SSHv2。SSHv2 相比 SSHv1 有显著的安全改进,包括更强的加密算法(AES、ChaCha20)、完整的密钥交换协议(Diffie-Hellman)、支持公钥认证和证书。

查看 SSH 版本:

 

# 查看 SSH 客户端版本
ssh -V

# 查看 SSH 服务器版本
sshd -V

# 测试 SSH 连接使用的协议版本
ssh -1 user@host  # 应该失败
ssh -2 user@host  # 使用 SSHv2

 

1.2 SSH 连接过程

SSH 连接建立分为几个阶段:首先是 TCP 握手建立连接;然后是协议版本交换;接着是密钥交换阶段(Diffie-Hellman 或 ECDH);然后是服务器认证(服务器证明自己的身份);然后是用户认证(密码、公钥或其他方式);最后是会话建立,可以执行命令。

理解这个过程有助于排查 SSH 连接问题:

 

# 详细查看 SSH 连接过程
ssh -vvv user@host

# 输出分为三段:debug1(连接和密钥交换)、debug2(认证过程)、debug3(会话建立)

 

1.3 SSH 密钥类型

现代 SSH 支持多种密钥类型:RSA 是最广泛支持的类型,推荐使用 4096 位;Ed25519 是现代算法,安全性和性能都很好,推荐使用;ECDSA 依赖曲线参数,不同实现可能存在兼容性问题。

生成 SSH 密钥:

 

# 生成 RSA 密钥(2048 位,兼容性好)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# 生成 Ed25519 密钥(推荐,性能好,密钥短)
ssh-keygen -t ed25519 -C "your_email@example.com"

# 生成 ECDSA 密钥
ssh-keygen -t ecdsa -b 521 -C "your_email@example.com"

# 指定密钥存放位置
ssh-keygen -t ed25519 -f ~/.ssh/my_server_key -C "my_server"

# 添加密码保护
ssh-keygen -t ed25519 -o -a 100 -C "your_email@example.com"
# -o 参数使用 OpenSSH 新格式(更安全)
# -a 参数指定密钥推导次数,越高越安全但越慢

 

2 SSH 服务器配置

2.1 配置文件位置

SSH 服务器配置文件位于 /etc/ssh/sshd_config,客户端配置位于 ~/.ssh/config。本文重点讲解服务器端配置。

配置文件的语法规则:使用空格分隔的键值对;以 # 开头的是注释;空行被忽略;每个配置项一行。

 

# 查看默认配置
man sshd_config

# 查看当前生效的配置
sshd -T

# 检查配置语法(不实际重启服务)
sshd -t

# 测试配置文件并显示会生效的配置
sshd -T -C user=root,host=localhost,addr=127.0.0.1

 

2.2 核心安全配置项

以下配置项直接影响 SSH 的安全性:

 

# 编辑 SSH 服务器配置
vim /etc/ssh/sshd_config

# 建议的配置:

# 1. 更改默认端口(减少自动化攻击)
Port 2222

# 2. 禁用 SSHv1(已废弃,不安全)
Protocol 2

# 3. 监听特定地址(如果有多个网卡,指定只监听内网)
ListenAddress 0.0.0.0
# 或只监听特定 IP
ListenAddress 192.168.1.1

# 4. 禁用空密码
PermitEmptyPasswords no

# 5. 禁用密码认证(强烈推荐,使用密钥认证)
PasswordAuthentication no
ChallengeResponseAuthentication no

# 6. 禁用 root 直接登录
PermitRootLogin no

# 7. 限制允许登录的用户
AllowUsers admin deploy@192.168.1.0/24
AllowGroups sshusers

# 8. 禁用 hosts.equiv 和 .rhosts
IgnoreRhosts yes
HostbasedAuthentication no

# 9. 禁用 GSSAPI 认证(减少攻击面)
GSSAPIAuthentication no

# 10. 启用公钥认证
PubkeyAuthentication yes

# 11. 设置空闲超时
ClientAliveInterval 300
ClientAliveCountMax 2

# 12. 设置登录尝试限制(配合 Fail2Ban 使用)
MaxAuthTries 3
MaxSessions 10

# 13. 设置空闲连接断开时间
LoginGraceTime 30

# 14. 禁用用户环境
PermitUserEnvironment no

# 15. 禁用 .rhosts 文件
IgnoreUserKnownHosts yes

# 16. 打印 motd(可选)
PrintMotd no

# 17. 禁用 TCP 转发
DisableForwarding yes

# 18. 禁用 X11 转发(如果不是必要)
X11Forwarding no

# 19. 限制子系统(如 SFTP)
Subsystem sftp internal-sftp -l INFO

# 20. 配置 SFTP 用户的 chroot
Match Group sftpusers
    ChrootDirectory /var/sftp
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no

 

2.3 编辑完配置后验证

修改 SSH 配置后,必须验证配置正确才能重启服务:

 

# 1. 检查配置语法
sshd -t

# 2. 如果没有错误输出,说明语法正确

# 3. 查看会生效的配置(详细)
sshd -T | grep -E "^passwordauthentication|^permitrootlogin|^pubkeyauthentication"

# 4. 重启 SSH 服务
systemctl restart sshd
systemctl restart ssh

# 5. 确认服务状态
systemctl status sshd

 

3 公钥认证配置

3.1 公钥认证原理

公钥认证基于非对称加密:客户端持有私钥,公钥放在服务器上;登录时,客户端用自己的私钥签名一段随机数据;服务器用对应的公钥验证签名;验证通过则允许登录。

优点:不会被暴力破解(私钥无法通过猜测获得);无需在网络中传输密码;可以禁用密码认证,消除密码泄露风险;私钥可以设置密码保护。

3.2 配置服务器接受公钥认证

 

# 服务器端配置
# /etc/ssh/sshd_config

PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# 限制 authorized_keys 文件的权限
# 服务器端应确保
# .ssh 目录权限 700
# authorized_keys 文件权限 600

 

3.3 用户端配置和管理

 

# 生成密钥对
ssh-keygen -t ed25519

# 将公钥复制到服务器(最简单的方式)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

# 手动复制公钥
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# 服务器端设置权限
ssh user@host "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"

# 验证权限是否正确
ssh user@host "ls -la ~/.ssh/"

 

3.4 管理 authorized_keys

 

# 查看用户的 authorized_keys
ssh user@host "cat ~/.ssh/authorized_keys"

# 添加多个公钥(可以是不同用户、不同机器的)
ssh user@host "cat >> ~/.ssh/authorized_keys" < ~/.ssh/new_key.pub

# 从 authorized_keys 中删除某个公钥
ssh user@host "grep -v 'ssh-rsa AAAAB3...' ~/.ssh/authorized_keys > /tmp/auth_keys && mv /tmp/auth_keys ~/.ssh/authorized_keys"

# 批量管理脚本
#!/bin/bash
# 添加多个用户的公钥到同一个账号

AUTH_KEYS_FILE="$HOME/.ssh/authorized_keys"
USER_KEYS_DIR="$HOME/.ssh/user_keys"

mkdir -p "$USER_KEYS_DIR"

# 为每个用户创建子目录
for user in alice bob charlie; do
    mkdir -p "$USER_KEYS_DIR/$user"
done

# 合并所有公钥(包含注释区分来源)
{
    echo "# Alice's keys"
    cat "$USER_KEYS_DIR/alice/"*.pub 2>/dev/null
    echo "# Bob's keys"
    cat "$USER_KEYS_DIR/bob/"*.pub 2>/dev/null
} > "$AUTH_KEYS_FILE"

 

4 防止把自己锁在系统外面

4.1 修改 SSH 配置前的准备工作

修改 SSH 配置前,必须确保不会把自己锁在外面。正确的操作顺序:

第一步,创建一个备份会话。在修改配置前,先开一个 SSH 会话并保持不退出。这个会话作为紧急恢复通道。

第二步,备份原配置文件:

 

cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup.$(date +%Y%m%d)
cp /etc/ssh/sshd_config /root/sshd_config.backup

 

第三步,使用 sshd -t 验证配置语法:

 

sshd -t
# 如果没有输出,说明语法正确

 

第四步,准备远程恢复方法。如果有控制台访问(云平台的控制台、IPMI、iLO、DRAC),确保可以远程登录。记录下控制台登录信息。

4.2 分步修改配置

不要一次性做多个修改,每次只改一个,确认生效后再改下一个:

 

# 步骤一:只修改端口,保持其他配置不变
# 编辑 /etc/ssh/sshd_config
Port 2222

# 验证并重启
sshd -t && systemctl restart sshd

# 用新端口测试连接(保持旧会话不退出)
ssh -p 2222 user@host

# 确认新端口工作正常后,再进行下一步

 

4.3 使用 ansible 或脚本批量修改

批量修改多台服务器的 SSH 配置时,使用配置管理工具可以减少出错概率:

 

# ansible 批量修改 SSH 配置
ansible all -i inventory -m lineinfile 
    -a "path=/etc/ssh/sshd_config 
        regexp='^Port' 
        line='Port 2222'"

# 验证配置
ansible all -i inventory -m command -a "sshd -t"

# 重启服务
ansible all -i inventory -m systemd -a "name=sshd state=restarted"

 

4.4 设置 PermitRootLogin 的正确顺序

禁用 root 登录是高风险操作,必须确保有替代方案:

 

# 第一步:创建替代的 sudo 用户
useradd -m -s /bin/bash admin
usermod -aG sudo admin

# 第二步:为该用户配置 SSH 公钥登录
ssh-copy-id admin@host

# 第三步:测试该用户能否 sudo 到 root
ssh admin@host
sudo -i

# 第四步:确认可以 sudo 后,再禁用 root 登录
# 编辑 /etc/ssh/sshd_config
PermitRootLogin no

# 第五步:保留一个可用的 root 登录方式(物理控制台或带外管理)

 

4.5 使用 Fail2Ban 防护暴力破解

Fail2Ban 可以自动封禁多次登录失败的 IP:

 

# 安装 Fail2Ban
apt-get install fail2ban  # Debian/Ubuntu
yum install fail2ban      # RHEL/CentOS

# 配置 Fail2Ban
cat > /etc/fail2ban/jail.local <

 

4.6 紧急恢复方法

如果不幸被锁在系统外面,按以下步骤恢复:

方法一:通过控制台登录(云服务器)

大多数云平台提供网页版控制台,可以直接用 root 或其他用户登录,然后修复 SSH 配置。

方法二:通过云平台的用户数据脚本

某些云平台支持在实例启动时执行用户数据脚本,可以用来修复 SSH 配置。

方法三:通过修改启动参数进入单用户模式

重启服务器,在 GRUB 菜单按 e 编辑启动参数,在 linux 行末尾添加 single 或 init=/bin/bash,以单用户模式启动,然后挂载根分区为可写,修复 SSH 配置。

方法四:通过云平台的"救援模式"

大多数云平台提供救援模式,可以从 ISO 启动,挂载原系统磁盘,修改 SSH 配置。

 

# 以 DigitalOcean 为例
# 1. 在控制台创建快照
# 2. 从快照创建新 droplet,选救援模式
# 3. 挂载原磁盘
mount /dev/vda1 /mnt
# 4. 编辑 SSH 配置
vim /mnt/etc/ssh/sshd_config
# 5. 卸载磁盘
umount /mnt
# 6. 从原快照创建新 droplet

 

5 密钥管理最佳实践

5.1 密钥存储和权限

 

# 密钥文件权限(非常重要)
# 私钥权限必须是 600
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_rsa

# 公钥可以是 644
chmod 644 ~/.ssh/id_ed25519.pub

# .ssh 目录权限必须是 700
chmod 700 ~/.ssh

# 服务器端 authorized_keys 权限必须是 600
chmod 600 ~/.ssh/authorized_keys

# 用户目录不能让其他人有写权限
chmod go-w ~/

 

5.2 密钥轮换策略

定期更换密钥是良好的安全习惯:

 

# 创建新密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_2024 -C "rotation 2024"

# 在服务器端添加新公钥
ssh user@host "cat >> ~/.ssh/authorized_keys" < ~/.ssh/id_ed25519_2024.pub

# 测试新密钥可以登录
ssh -i ~/.ssh/id_ed25519_2024 user@host

# 确认新密钥工作后,从 authorized_keys 中移除旧公钥
ssh user@host "grep -v 'old_key_comment' ~/.ssh/authorized_keys > /tmp/auth && mv /tmp/auth ~/.ssh/authorized_keys"

# 本地删除旧私钥
mv ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.old

 

5.3 使用 SSH Agent 转发

SSH Agent 可以缓存解密后的私钥,避免重复输入密码:

 

# 启动 SSH Agent
eval "$(ssh-agent -s)"

# 添加密钥到 Agent
ssh-add ~/.ssh/id_ed25519

# 查看 Agent 中的密钥
ssh-add -l

# 密钥转发(从跳板机跳转到内网机器,保留密钥访问能力)
# 在 .ssh/config 中配置
Host jump-server
    HostName jumphost.example.com
    User admin
    Port 2222
    ForwardAgent yes

# 或者使用 -A 参数
ssh -A admin@jumphost.example.com

 

5.4 使用 config 文件管理多服务器

 

# ~/.ssh/config 示例
# 可以为每个服务器定义独立的配置

# 默认配置
Host *
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 300
    ServerAliveCountMax 2
    StrictHostKeyChecking ask

# 生产服务器
Host prod-*
    HostName %h.example.com
    User admin
    ForwardAgent no
    LogLevel INFO

# 跳板机
Host jump
    HostName jumphost.example.com
    User admin
    Port 2222
    ForwardAgent yes

# 数据库服务器(只能从跳板机访问)
Host db-1
    HostName 192.168.1.100
    User dbadmin
    ProxyJump jump
    LocalForward 3306 127.0.0.1:3306

# 免密码快捷登录
Host github
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_ed25519

 

6 连接问题排查

6.1 常见连接错误及解决

错误一:Connection refused

 

# 检查 SSH 服务是否运行
systemctl status sshd

# 检查端口是否监听
ss -tlnp | grep sshd
netstat -tlnp | grep sshd

# 检查防火墙规则
iptables -L -n | grep 22
ufw status
firewall-cmd --list-all

 

错误二:Permission denied

 

# 检查用户名是否正确
ssh user@host

# 检查密钥是否正确配置
ssh -vvv user@host  # 查看详细认证过程

# 检查服务器日志
tail -f /var/log/auth.log
journalctl -u sshd -f

# 客户端指定密钥
ssh -i ~/.ssh/specific_key user@host

 

错误三:Connection timeout

 

# 检查网络连通性
ping -c 4 host
traceroute host  # Linux
tracert host     # Windows

# 检查端口是否可达
nc -zv host 22
telnet host 22

# 检查是否有 ACL 限制
ssh -b bind_ip user@host

 

6.2 查看详细连接日志

 

# 客户端查看详细日志
ssh -vvv user@host

# 服务器端查看实时日志
tail -f /var/log/auth.log
journalctl -u sshd -f

# 调试 SSH 握手
ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 user@host

 

6.3 检查 SSH 服务状态

 

# 查看服务状态
systemctl status sshd

# 查看监听端口
ss -tlnp | grep sshd

# 测试本地 SSH 连接
ssh localhost

# 检查 SELinux 上下文(RHEL/CentOS)
getsebool -a | grep ssh
setsebool -P ssh_sysadm_login on

 

7 完整的 SSH 安全加固脚本

 

#!/bin/bash
# ssh_hardening.sh - SSH 安全加固脚本

set -euo pipefail

BACKUP_DIR="/root/ssh_backups"
mkdir -p "$BACKUP_DIR"

backup_config() {
    cp /etc/ssh/sshd_config "${BACKUP_DIR}/sshd_config.$(date +%Y%m%d_%H%M%S)"
    echo "Backup created in $BACKUP_DIR"
}

verify_sudo() {
    if [ "$(id -u)" -ne 0 ]; then
        echo "This script must be run as root"
        exit 1
    fi
}

verify_admin_user() {
    if ! id admin &>/dev/null; then
        echo "Creating admin user..."
        useradd -m -s /bin/bash admin
        usermod -aG sudo admin
    fi

    if [ ! -f "/home/admin/.ssh/authorized_keys" ]; then
        echo "WARNING: admin user has no SSH keys configured!"
        echo "Please add your public key to /home/admin/.ssh/authorized_keys before continuing"
        read -p "Press Enter to continue anyway..."
    fi
}

apply_config() {
    cat > /etc/ssh/sshd_config <<'EOF'
# SSH Server Configuration
Port 2222
Protocol 2

ListenAddress 0.0.0.0

# Authentication
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
LoginGraceTime 30

# Security
IgnoreRhosts yes
HostbasedAuthentication no
 PermitUserEnvironment no
PrintMotd no
TCPKeepAlive yes

# Idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2

# Disable unused features
X11Forwarding no
AllowTcpForwarding no
PermitTunnel no
AllowAgentForwarding no

# SFTP configuration
Subsystem sftp internal-sftp -l INFO

# Override for SFTP users
Match Group sftpusers
    ChrootDirectory /var/sftp
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
EOF
    echo "Configuration applied"
}

set_permissions() {
    chmod 644 /etc/ssh/sshd_config

    # 确保 admin 用户目录权限正确
    chown -R admin:admin /home/admin/.ssh
    chmod 700 /home/admin/.ssh
    chmod 600 /home/admin/.ssh/authorized_keys
}

verify_config() {
    echo "Verifying configuration..."
    sshd -t && echo "Configuration syntax OK"
}

restart_service() {
    echo "Restarting SSH service..."
    systemctl restart sshd
    systemctl status sshd --no-pager
}

main() {
    echo "SSH Security Hardening Script"
    echo "============================"

    verify_sudo
    verify_admin_user
    backup_config
    apply_config
    set_permissions
    verify_config

    echo ""
    echo "IMPORTANT: Keep your current SSH session open!"
    echo "Test a new connection before closing this session!"
    echo ""

    read -p "Restart SSH service now? (yes/no): " confirm
    if [ "$confirm" = "yes" ]; then
        restart_service
        echo "SSH service restarted. Please test a new connection."
    else
        echo "Service restart skipped. Run 'systemctl restart sshd' manually when ready."
    fi
}

main "$@"

 

8 结论

SSH 安全配置需要在安全性和可用性之间取得平衡。过度限制的配置可能导致自己无法登录,合理的安全配置应该既能防止攻击,又能保证管理员正常访问。

核心安全配置建议:更改默认端口减少自动化攻击;禁用密码认证,使用公钥认证;禁用 root 直接登录,使用 sudo;配置 Fail2Ban 防止暴力破解;定期轮换 SSH 密钥。

防止把自己锁在外面的关键:在修改配置前先备份;保持一个不退出 SSH 会话作为恢复通道;分步骤修改配置,每步确认后再进行下一步;使用控制台或带外管理作为最终恢复手段。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分