SSH 加固、免密登录与端口转发

服务器的第一道门就是 SSH。密码登录 + 22 端口 + root 允许登录,等于把门牌号和钥匙一起挂在门口。本章给出密钥登录的完整落地步骤、~/.ssh/config 的实用写法、三种端口转发的区别与用途,以及最基础的登录审计手段。

密码登录 vs 密钥登录

维度密码登录密钥登录
暴力破解难度低,公网每天成千上万次尝试极高,几乎不可能穷举 ed25519 私钥
凭据泄露风险密码会被复用、被撞库私钥只在本机,公钥泄露也无害
自动化友好度需 sshpass 等不安全方案CI/CD、Ansible 原生支持
运维成本改密码即生效换机器要重新分发公钥
结论仅用于应急救援生产环境唯一选择

生成密钥并上传公钥

# 生成 ed25519 密钥(比 RSA 更短更安全,-C 是注释便于识别)
ssh-keygen -t ed25519 -C "deploy@laptop"
# 口令可留空(便于自动化),也可设置后交给 ssh-agent 缓存
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519

# 上传公钥,会自动创建 ~/.ssh 与 authorized_keys 并设好权限
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@47.98.x.x
# 非默认端口:ssh-copy-id -p 2222 -i ~/.ssh/id_ed25519.pub deploy@47.98.x.x

# 先验证免密登录成功,再继续加固
ssh -i ~/.ssh/id_ed25519 deploy@47.98.x.x 'hostname && whoami'

务必先验证密钥能登录,再禁用密码登录,否则只能去控制台走 VNC 救援。

加固 sshd_config

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
sudo vim /etc/ssh/sshd_config
# /etc/ssh/sshd_config 关键片段
Port 2222                          # 改端口挡扫描(降噪,不是安全屏障)
PermitRootLogin no                 # 禁止 root 直接登录,用普通用户 + sudo
PasswordAuthentication no          # 关闭密码登录,只认密钥
PubkeyAuthentication yes           # 打开公钥认证
KbdInteractiveAuthentication no    # 关闭键盘交互,防绕过密码策略
PermitEmptyPasswords no
MaxAuthTries 3                     # 认证失败 3 次即断开
LoginGraceTime 30                  # 30 秒内必须完成认证
ClientAliveInterval 300            # 5 分钟无响应发保活包
AllowUsers deploy ops              # 白名单,只有这两个用户能登录
UseDNS no                          # 关闭反向解析,加快登录速度

正确顺序是:建普通用户 → 传公钥 → 验证免密登录 → 改 sshd_configsshd -t 校验 → reload。

应用配置并校验

sudo sshd -t && echo config-ok      # 语法校验
sudo systemctl reload sshd          # 平滑重载,不断开现有连接
# 部分 Debian 版本服务名是 ssh:sudo systemctl reload ssh

# 另开一个终端验证新配置可登录,两件事都通过后再关掉旧会话
ssh -p 2222 deploy@47.98.x.x 'echo ok'

# 确认端口真的换了,且三处已同步(安全组、防火墙、sshd)
sudo ss -lntp | grep sshd
sudo ufw allow 2222/tcp && sudo ufw delete allow 22/tcp

~/.ssh/config:把长命令变成别名

# ~/.ssh/config(本机文件,权限 600,否则 ssh 会拒绝使用)
Host prod
    HostName 47.98.x.x
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 60

Host db
    HostName 10.0.0.8       # 内网地址,只能经跳板机到达
    User deploy
    ProxyJump prod

配置完成后 ssh prodscp file prod:/srv/www/ssh db 都能直接用;ProxyJump 替代了早年的 -o ProxyCommand="ssh -W %h:%p" 写法。

三种端口转发

类型命令形式方向典型用途
本地转发ssh -L 13306:127.0.0.1:3306 prod本地端口 → 远端服务本地客户端连生产库、访问内网面板
远程转发ssh -R 9000:127.0.0.1:3000 prod远端端口 → 本地服务把本地开发服务临时给别人调试
动态转发ssh -D 1080 prod本地 SOCKS5 代理浏览器走服务器出口访问内网资源
# 本地转发:生产 MySQL 全程不暴露,本地用 13306 连接
ssh -N -L 13306:127.0.0.1:3306 deploy@47.98.x.x
mysql -h 127.0.0.1 -P 13306 -u appuser -p

# 远程转发:默认只绑远端 127.0.0.1,需 GatewayPorts yes 才能对外
ssh -N -R 9000:127.0.0.1:3000 deploy@47.98.x.x

# -f 转后台、-N 不执行远程命令、-C 压缩
ssh -fNC -L 13306:127.0.0.1:3306 prod

fail2ban 与登录审计

# fail2ban:连续认证失败即封禁来源 IP,务必配 ignoreip 白名单
sudo apt -y install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# 在 jail.local 的 [sshd] 段改:enabled=true, maxretry=3, bantime=1h, port=2222
sudo systemctl enable --now fail2ban && sudo fail2ban-client status sshd

# 登录审计
last -a | head -20                                  # 最近成功登录
sudo lastb | head -20                               # 最近失败登录
sudo tail -f /var/log/auth.log                      # RHEL 系为 /var/log/secure
sudo grep 'Failed password' /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head

常见坑

现象做法
先关密码登录,公钥还没传彻底失联,只能控制台救援顺序:建用户 → 传公钥 → 验证 → 再关密码
改端口后忘了改安全组新端口连不上,旧端口已关三处同步:sshd_config、防火墙、安全组
私钥权限过松UNPROTECTED PRIVATE KEY FILEchmod 600 私钥、chmod 700 ~/.ssh
私钥提交到 Git密钥永久泄露私钥永不入库,.gitignore.ssh/*.pem
fail2ban 白名单没设自己的出口 IP 被封ignoreip 写死固定的运维出口 IP
改完直接 restart sshd现有会话被断,无法验证reload,并另开会话验证

小结:SSH 加固的顺序是建普通用户、上传 ed25519 公钥、验证免密、再关 PasswordAuthenticationPermitRootLogin no 并用 AllowUsers 收窄范围;~/.ssh/config 用别名与 ProxyJump 统一入口;端口转发让数据库永远不用对外暴露;最后用 fail2ban 与 lastb 把爆破挡在门外并定期审计。

笔记加载中…