🔑 第15课:SSH服务

阶段三:网络配置第3/6课

📚 课程目标

一、SSH协议

1.1 SSH工作流程

客户端                          服务端
  │── 版本协商 ──────────────────→│
  │←── 版本协商 ──────────────────│
  │── 算法协商 ──────────────────→│
  │←── 算法协商 ──────────────────│
  │←── 服务端主机密钥 ───────────│
  │── DH密钥交换 ───────────────→│
  │←── DH密钥交换 ───────────────│
  │  (会话密钥建立)               │
  │── 认证请求 ─────────────────→│
  │←── 认证结果 ─────────────────│
  │── 加密数据传输 ─────────────→│

1.2 SSH版本

版本状态说明
SSH-1已弃用存在安全漏洞
SSH-2当前标准DH密钥交换,强加密

二、SSH服务端配置

$ grep -v '^#' /etc/ssh/sshd_config | grep -v '^$' | head -20
Include /etc/ssh/sshd_config.d/*.conf
AddressFamily any
PermitRootLogin yes
KbdInteractiveAuthentication no
UsePAM yes
X11Forwarding yes
PrintMotd no
UseDNS no
AcceptEnv LANG LC_*
Subsystem sftp	/usr/lib/openssh/sftp-server
PasswordAuthentication no
PermitEmptyPasswords no
$ systemctl status sshd 2>/dev/null | head -10 || systemctl status ssh | head -10
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-05-19 02:15:58 CST; 19h ago
TriggeredBy: ● ssh.socket
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 972 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 1000 (sshd)
      Tasks: 3 (limit: 9127)
     Memory: 5.2M (peak: 15.5M swap: 516.0K swap peak: 516.0K)

2.1 安全加固配置

# /etc/ssh/sshd_config 安全加固
Port 2222                              # 修改默认端口
PermitRootLogin no                     # 禁止root登录
PasswordAuthentication no              # 禁用密码认证
PubkeyAuthentication yes               # 启用密钥认证
MaxAuthTries 3                         # 最多尝试3次
LoginGraceTime 30                      # 30秒内必须认证
AllowUsers deploy admin@10.0.0.*       # 限制用户和来源IP
X11Forwarding no                       # 禁用X转发
PermitEmptyPasswords no                # 禁止空密码
ClientAliveInterval 300                # 5分钟空闲检测
ClientAliveCountMax 2                  # 2次无响应断开

# 重启SSH服务
sudo systemctl restart sshd

三、SSH密钥认证

$ ls -la ~/.ssh/
total 8
drwx------  2 root root 4096 Apr 26  2024 .
drwx------ 17 root root 4096 May 19 21:32 ..
-rw-------  1 root root    0 May 14 12:15 authorized_keys
# 生成密钥对
ssh-keygen -t ed25519 -C "user@host"       # 推荐(Ed25519)
ssh-keygen -t rsa -b 4096 -C "user@host"   # RSA备选

# 复制公钥到服务器
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# 或手动复制
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

3.1 SSH Agent

# 启动ssh-agent
eval $(ssh-agent -s)

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

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

# 使用Agent转发(在~/.ssh/config中)
Host server
    ForwardAgent yes

四、SSH配置文件

# ~/.ssh/config 客户端配置
Host myserver
    HostName 192.168.1.100
    Port 2222
    User deploy
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes

Host jump
    HostName jump.example.com
    User admin

Host internal
    HostName 10.0.0.50
    ProxyJump jump              # 通过跳板机连接
    User deploy

# 使用
ssh myserver    # 自动使用配置
ssh internal    # 通过跳板机连接

五、SSH隧道与端口转发

5.1 本地端口转发

# 将本地8080端口转发到远程的localhost:80
ssh -L 8080:localhost:80 user@server

# 访问内网数据库
ssh -L 3306:db.internal:3306 user@bastion
# 然后本地连接 localhost:3306 即可访问内网数据库

5.2 远程端口转发

# 将远程的9090端口转发到本地的3000
ssh -R 9090:localhost:3000 user@server
# 他人访问server:9090 即可访问你本地的3000端口

5.3 动态端口转发(SOCKS代理)

# 创建SOCKS5代理
ssh -D 1080 user@server
# 浏览器配置SOCKS5代理为 localhost:1080

六、故障排查

❌ SSH连接失败

# 详细输出
ssh -vvv user@server

# 检查服务端
sudo systemctl status sshd
sudo journalctl -u sshd -n 20

# 检查端口
ss -tlnp | grep 22

# 检查防火墙
sudo ufw status
sudo iptables -L -n | grep 22

# 检查hosts.deny
cat /etc/hosts.deny

# 检查fail2ban
sudo fail2ban-client status sshd

七、SSH安全最佳实践

📋 SSH安全检查清单

  1. 仅使用SSH-2协议
  2. 禁用密码认证,仅用密钥
  3. 禁止root直接登录
  4. 使用非标准端口
  5. 限制允许登录的用户
  6. 安装fail2ban防暴力破解
  7. 使用Ed25519密钥
  8. 定期轮换密钥
  9. 记录登录日志
# 安装fail2ban
sudo apt install fail2ban
sudo systemctl enable --now fail2ban

# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

八、练习

📝 SSH安全加固

# 1. 生成Ed25519密钥对
ssh-keygen -t ed25519 -C "$(whoami)@$(hostname)"

# 2. 查看当前SSH配置
grep -v '^#' /etc/ssh/sshd_config | grep -v '^$'

# 3. 检查密钥权限
ls -la ~/.ssh/

# 4. 查看SSH登录日志
sudo journalctl -u sshd | tail -10
sudo last | head -5
🏆

成就解锁:SSH安全专家

✅ 理解SSH协议与认证流程

✅ 掌握SSH服务端安全加固

✅ 学会密钥认证配置

✅ 掌握SSH隧道与端口转发

✅ 了解fail2ban防暴力破解

九、SSH多路复用

# ~/.ssh/config
Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600   # 10分钟后关闭

# 创建socket目录
mkdir -p ~/.ssh/sockets

# 效果:第一个SSH连接建立后,后续连接复用同一通道
# 大幅减少连接建立时间

十、SSH安全审计脚本

#!/bin/bash
# ssh-audit.sh - SSH安全审计
echo "===== SSH安全审计 ====="

# 检查SSH版本
echo "--- SSH版本 ---"
ssh -V 2>&1

# 检查关键配置
echo "\n--- 安全配置检查 ---"
for key in PermitRootLogin PasswordAuthentication PubkeyAuthentication MaxAuthTries Port; do
    val=$(grep "^$key" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')
    echo "$key: ${val:-未设置(使用默认值)}"
done

# 检查密钥类型
echo "\n--- 主机密钥 ---"
ls -la /etc/ssh/ssh_host_*_key 2>/dev/null

# 检查授权密钥
echo "\n--- 授权密钥 ---"
for user_home in /home/* /root; do
    if [ -f "$user_home/.ssh/authorized_keys" ]; then
        count=$(wc -l < "$user_home/.ssh/authorized_keys")
        echo "$user_home: $count keys"
    fi
done

# 检查fail2ban
echo "\n--- fail2ban状态 ---"
sudo fail2ban-client status sshd 2>/dev/null || echo "fail2ban未安装"

十一、SSH证书认证(CA)

# 创建SSH CA
# 1. 生成CA密钥
ssh-keygen -t ed25519 -f ssh_ca -C "SSH CA"

# 2. 签发用户证书
ssh-keygen -s ssh_ca -I user_zhangsan -n zhangsan -V +52w ~/.ssh/id_ed25519.pub
# -I: 证书标识
# -n: 允许登录的用户名
# -V: 有效期(52周)

# 3. 服务端信任CA
echo "TrustedUserCAKeys /etc/ssh/ca.pub" | sudo tee -a /etc/ssh/sshd_config
sudo cp ssh_ca.pub /etc/ssh/ca.pub
sudo systemctl restart sshd

# 优势:无需在每个服务器上部署公钥,仅信任CA即可

十二、SSH跳板机架构

# 生产环境SSH架构
用户 → 跳板机(bastion) → 内网服务器
         │
         └── 仅跳板机有外网IP
             内网服务器仅允许跳板机IP连接SSH

# ~/.ssh/config
Host bastion
    HostName bastion.example.com
    User admin
    IdentityFile ~/.ssh/id_ed25519

Host internal-*
    ProxyJump bastion
    User deploy
    IdentityFile ~/.ssh/id_ed25519_internal

十三、SSH与Ansible集成

# Ansible通过SSH管理服务器
# ansible.cfg
[defaults]
inventory = hosts
host_key_checking = False
private_key_file = ~/.ssh/id_ed25519
ssh_args = -F ~/.ssh/ansible_ssh_config -o ControlMaster=auto -o ControlPersist=60s

# hosts文件
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11

[dbservers]
db1 ansible_host=192.168.1.20

[all:vars]
ansible_user=deploy
ansible_ssh_port=2222

十四、SSH隧道应用场景

# 场景1:安全访问内网Web服务
ssh -L 8080:internal-web:80 user@bastion
# 浏览器访问 localhost:8080

# 场景2:安全访问远程数据库
ssh -L 3306:db.internal:3306 user@bastion
# 本地MySQL客户端连接 localhost:3306

# 场景3:远程桌面(VNC)
ssh -L 5901:vnc-server:5901 user@bastion

# 场景4:临时反向代理
# 从家里访问办公室内网
ssh -R 2222:localhost:22 office-server
# 在办公室服务器上: ssh -p 2222 localhost 即可回家里的机器

# 场景5:SOCKS代理浏览
ssh -D 1080 user@server
# 配置浏览器SOCKS5代理 localhost:1080

十五、SSH配置完整示例

# /etc/ssh/sshd_config - 生产级安全配置
# 基础设置
Port 2222
AddressFamily inet
ListenAddress 0.0.0.0

# 协议与加密
Protocol 2
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org

# 认证
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes

# 访问控制
AllowUsers deploy admin@10.0.0.*
DenyUsers guest

# 会话
X11Forwarding no
PrintMotd no
AcceptEnv LANG LC_*
ClientAliveInterval 300
ClientAliveCountMax 2
MaxSessions 10
MaxStartups 10:30:100

# 日志
SyslogFacility AUTH
LogLevel VERBOSE

# SFTP(可选)
Subsystem sftp /usr/lib/openssh/sftp-server