SSH(Secure Shell)是系统管理员最常用的远程访问协议,也是攻击者的首要目标。本文详细介绍 SSH 安全加固的各个层面,从基础配置到高级技巧,帮助你构建坚不可摧的 SSH 防线。

一、SSH 基础安全配置

sshd_config 核心配置

配置文件路径:/etc/ssh/sshd_config

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 禁用 root 登录
PermitRootLogin no

# 禁用密码认证(强制密钥认证)
PasswordAuthentication no
PubkeyAuthentication yes

# 禁用空密码
PermitEmptyPasswords no

# 禁用 PAM(可选,提高安全性但可能影响某些认证方式)
UsePAM no

# 限制登录用户
AllowUsers admin deploy@192.168.1.0/24
# 或使用组
AllowGroups ssh-users

# 设置登录超时(秒)
LoginGraceTime 60

# 最大认证尝试次数
MaxAuthTries 3

# 限制会话数
MaxSessions 10

# 限制启动会话数(防止资源耗尽)
MaxStartups 10:30:100
# 格式: start:rate:full
# start: 开始拒绝前的未认证连接数
# rate: 拒绝概率增长速率
# full: 完全拒绝的连接数

修改默认端口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 不要使用默认的 22 端口
Port 2222

# 或使用多个端口(便于过渡)
Port 22
Port 2222

# 记得更新防火墙规则
ufw allow 2222/tcp
# 或
firewall-cmd --add-port=2222/tcp --permanent
firewall-cmd --reload

完整的安全配置示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# /etc/ssh/sshd_config 安全加固版

# 网络设置
Port 2222
AddressFamily inet
ListenAddress 0.0.0.0

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

# 加密算法(只允许强加密)
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256

# 认证设置
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 5
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no

# 禁用不安全功能
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PermitTunnel no

# 连接设置
LoginGraceTime 60
TCPKeepAlive yes
ClientAliveInterval 300
ClientAliveCountMax 2

# 日志
SyslogFacility AUTH
LogLevel VERBOSE

# 访问控制
AllowUsers admin@192.168.1.0/24 deploy

# 重启服务
# systemctl restart sshd

验证配置

1
2
3
4
5
6
7
8
9
# 测试配置语法
sshd -t

# 测试配置并显示有效配置
sshd -T

# 查看特定参数
sshd -T | grep -i "passwordauthentication"
sshd -T | grep -i "permitrootlogin"

二、SSH 密钥管理

生成安全的 SSH 密钥

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 推荐使用 Ed25519(性能好、安全性高)
ssh-keygen -t ed25519 -C "your_email@example.com"

# 使用 RSA(兼容性好)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# 带密码保护的密钥
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_ed25519_secure

# 指定密钥注释
ssh-keygen -t ed25519 -C "server-admin-$(date +%Y%m%d)"

# 使用硬件密钥(如 YubiKey)
ssh-keygen -t ecdsa-sk -C "hardware-key"

# 显示密钥指纹
ssh-keygen -lf ~/.ssh/id_ed25519.pub

密钥部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 手动复制公钥
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

# 指定端口
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@server

# 手动安装(当 ssh-copy-id 不可用时)
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# 设置正确权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

管理 known_hosts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 查看已知主机
cat ~/.ssh/known_hosts

# 删除特定主机
ssh-keygen -R hostname
ssh-keygen -R 192.168.1.100

# 删除特定行的主机
ssh-keygen -R hostname -f ~/.ssh/known_hosts

# 显示主机密钥指纹
ssh-keygen -l -f ~/.ssh/known_hosts

# 严格主机密钥检查
# 在 ~/.ssh/config 中设置
Host *
    StrictHostKeyChecking yes
    UserKnownHostsFile ~/.ssh/known_hosts

SSH Agent 管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 启动 ssh-agent
eval "$(ssh-agent -s)"

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

# 查看已添加的密钥
ssh-add -l

# 删除所有密钥
ssh-add -D

# 锁定 agent
ssh-add -x

# 解锁 agent
ssh-add -X

# 设置密钥超时(秒)
ssh-add -t 3600 ~/.ssh/id_ed25519

# 在 ~/.ssh/config 中自动添加密钥
Host *
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

三、SSH 客户端配置

~/.ssh/config 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 全局默认设置
Host *
    # 使用 Ed25519 密钥
    IdentityFile ~/.ssh/id_ed25519
    # 自动添加密钥到 agent
    AddKeysToAgent yes
    # 保持连接
    ServerAliveInterval 60
    ServerAliveCountMax 3
    # 连接复用
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600
    # 压缩传输
    Compression yes

# 生产服务器
Host prod-*
    User admin
    Port 2222
    ForwardAgent no
    ForwardX11 no

# 特定服务器配置
Host myserver
    HostName 192.168.1.100
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519_server
    # 跳板机
    ProxyJump bastion.example.com

# 通过跳板机连接
Host internal-server
    HostName 10.0.0.50
    User admin
    ProxyJump jumpuser@jumpserver.example.com:2222

# 多跳连接
Host final-destination
    HostName 192.168.2.100
    ProxyJump jump1.example.com,jump2.example.com

# Git 服务器
Host github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_github

Host gitlab.com
    User git
    IdentityFile ~/.ssh/id_ed25519_gitlab

创建目录结构

1
2
3
4
5
6
7
# 创建 socket 目录用于连接复用
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets

# 创建配置文件
touch ~/.ssh/config
chmod 600 ~/.ssh/config

四、高级安全措施

双因素认证 (2FA/MFA)

使用 Google Authenticator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装
apt install libpam-google-authenticator

# 为用户配置
google-authenticator

# 回答问题:
# Do you want authentication tokens to be time-based? y
# Do you want me to update your "~/.google_authenticator" file? y
# Do you want to disallow multiple uses? y
# Do you want to do so? n (默认限制到30秒)
# Do you want to enable rate-limiting? y

# 编辑 /etc/pam.d/sshd
# 添加:
auth required pam_google_authenticator.so

# 编辑 /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive

# 重启服务
systemctl restart sshd

限制 IP 访问

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 方法1:在 sshd_config 中使用 AllowUsers
AllowUsers admin@192.168.1.0/24 deploy@10.0.0.100

# 方法2:使用 AllowGroups
AllowGroups ssh-admins ssh-developers

# 方法3:使用 /etc/hosts.allow 和 /etc/hosts.deny
# /etc/hosts.allow
sshd: 192.168.1.0/255.255.255.0
sshd: 10.0.0.100

# /etc/hosts.deny
sshd: ALL

# 方法4:使用防火墙(推荐)
# UFW
ufw allow from 192.168.1.0/24 to any port 2222 proto tcp

# iptables
iptables -A INPUT -p tcp --dport 2222 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 2222 -j DROP

# firewalld
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="2222" accept'
firewall-cmd --reload

Fail2Ban 防暴力破解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 安装
apt install fail2ban

# 创建本地配置
cat > /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 3600
ignoreip = 127.0.0.1/8 192.168.1.0/24

[sshd-aggressive]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 2
findtime = 86400
bantime = 604800
EOF

# 启动服务
systemctl enable fail2ban
systemctl start fail2ban

# 查看状态
fail2ban-client status sshd

# 手动解封 IP
fail2ban-client set sshd unbanip 192.168.1.100

# 查看日志
tail -f /var/log/fail2ban.log

端口敲门 (Port Knocking)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 安装 knockd
apt install knockd

# 编辑 /etc/knockd.conf
[openSSH]
    sequence    = 7000,8000,9000
    seq_timeout = 5
    command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
    tcpflags    = syn

[closeSSH]
    sequence    = 9000,8000,7000
    seq_timeout = 5
    command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
    tcpflags    = syn

# 客户端使用
knock server.example.com 7000:tcp 8000:tcp 9000:tcp
ssh -p 2222 user@server.example.com
knock server.example.com 9000:tcp 8000:tcp 7000:tcp

五、SSH 日志与监控

日志配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 在 sshd_config 中设置日志级别
LogLevel VERBOSE

# 查看 SSH 登录日志
# Debian/Ubuntu
tail -f /var/log/auth.log

# RHEL/CentOS
tail -f /var/log/secure

# 查看成功登录
grep "Accepted password\|Accepted publickey" /var/log/auth.log

# 查看失败登录
grep "Failed password\|Connection closed by" /var/log/auth.log

# 查看特定用户的登录
grep "user" /var/log/auth.log | grep "session opened"

# 统计失败登录 IP
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head

登录监控脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# ssh_monitor.sh - SSH 登录监控

LOG_FILE="/var/log/auth.log"
ALERT_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

# 监控实时登录
tail -fn0 $LOG_FILE | while read line; do
    if echo "$line" | grep -q "Accepted"; then
        user=$(echo "$line" | grep -oP '(?<=for )[^ ]+')
        ip=$(echo "$line" | grep -oP '\d+\.\d+\.\d+\.\d+' | tail -1)
        method=$(echo "$line" | grep -oP 'publickey|password')
        
        message="SSH 登录告警: 用户 $user$ip 通过 $method 方式登录成功"
        echo "$(date): $message"
        
        # 发送告警
        # curl -X POST -H 'Content-type: application/json' \
        #   --data "{\"text\":\"$message\"}" $ALERT_WEBHOOK
    fi
done

使用 last 和 w 命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 查看登录历史
last -n 20

# 查看 SSH 登录历史
last -f /var/log/wtmp | grep ssh

# 查看当前登录用户
w

# 查看登录失败记录
lastb -n 20

# 清除登录记录(需要 root)
echo > /var/log/wtmp
echo > /var/log/btmp

六、SSH 高级用法

SSH 隧道

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 本地端口转发(访问远程内网服务)
ssh -L 8080:localhost:80 user@server
# 本地访问 localhost:8080 -> 远程 localhost:80

# 远程端口转发(让远程访问本地服务)
ssh -R 8080:localhost:3000 user@server
# 远程访问 server:8080 -> 本地 localhost:3000

# 动态端口转发(SOCKS 代理)
ssh -D 1080 user@server
# 浏览器设置 SOCKS 代理: localhost:1080

# 后台运行隧道
ssh -fNL 8080:localhost:80 user@server

# 保持隧道存活
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -fNL 8080:localhost:80 user@server

SSH 跳转

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 通过跳板机连接
ssh -J user@jumpserver user@targetserver

# 多跳转
ssh -J user1@jump1,user2@jump2 user@target

# 使用 ProxyCommand(兼容旧版本)
ssh -o ProxyCommand="ssh -W %h:%p user@jumpserver" user@targetserver

# 在 ~/.ssh/config 中配置
Host target
    HostName 10.0.0.50
    ProxyJump jumpserver

SFTP 和 SCP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# SCP 上传
scp -P 2222 local_file user@server:/remote/path/

# SCP 下载
scp -P 2222 user@server:/remote/file ./local/path/

# SCP 目录
scp -P 2222 -r local_dir user@server:/remote/path/

# SFTP 命令行
sftp -P 2222 user@server

# SFTP 命令
sftp> ls
sftp> cd remote_dir
sftp> get remote_file
sftp> put local_file
sftp> mkdir new_dir
sftp> exit

# 使用 rsync(更高效)
rsync -avz -e "ssh -p 2222" local_dir/ user@server:/remote/dir/

SSH 证书认证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建 CA 密钥
ssh-keygen -t ed25519 -f /etc/ssh/ca -N ""

# 在 sshd_config 中配置 CA
TrustedUserCAKeys /etc/ssh/ca.pub

# 签发用户证书
ssh-keygen -s /etc/ssh/ca -I user_admin -n admin,deploy -V +52w ~/.ssh/id_ed25519.pub

# 查看证书
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub

# 证书内容示例:
# Type: ssh-ed25519-cert-v01@openssh.com user certificate
# Public key: ED25519-CERT SHA256:xxx
# Signing CA: ED25519 SHA256:xxx
# Key ID: "user_admin"
# Serial: 0
# Valid: from 2024-03-24T00:00:00 to 2025-03-24T00:00:00
# Principals: admin deploy
# Critical Options: (none)
# Extensions: permit-X11-forwarding permit-agent-forwarding permit-port-forwarding

七、SSH 故障排查

连接问题诊断

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 详细输出模式
ssh -vvv user@server

# 检查服务状态
systemctl status sshd

# 检查端口监听
netstat -tlnp | grep :22
ss -tlnp | grep :22

# 检查防火墙
iptables -L -n
ufw status
firewall-cmd --list-all

# 测试端口连通性
nc -zv server 22
telnet server 22

# 检查 DNS 解析
nslookup server
dig server

# 检查 SELinux(RHEL/CentOS)
getenforce
# 如果是 Enforcing,检查 SSH 相关策略
ausearch -c sshd --raw | audit2allow -M mypolicy
semodule -i mypolicy.pp

权限问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 正确的权限设置
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/config

# 检查文件所有权
ls -la ~/.ssh/
# 应该都是当前用户

# 修复所有权
chown -R $USER:$USER ~/.ssh/

# 服务器端检查
ls -la /home/user/.ssh/
# 确保用户目录权限正确
chmod 755 /home/user

常见错误解决

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 错误: Permission denied (publickey)
# 解决: 检查密钥、权限、sshd_config

# 错误: Host key verification failed
# 解决: 删除旧的主机密钥
ssh-keygen -R server

# 错误: Connection refused
# 解决: 检查服务是否运行、端口是否正确、防火墙

# 错误: Connection timed out
# 解决: 检查网络、防火墙、IP是否正确

# 错误: Too many authentication failures
# 解决: 指定密钥文件
ssh -i ~/.ssh/specific_key user@server
# 或在 config 中设置 IdentitiesOnly yes

# 错误: no matching key exchange found
# 解决: 指定算法
ssh -o KexAlgorithms=diffie-hellman-group-exchange-sha256 user@server

参考来源