Linux sshd 实战指南:从配置到白名单、跳板机代理
本文面向有运维/SRE 基础、想把 SSH 真正用"稳"和用"安全"的读者。不只讲
sshd_config的表面选项,重点放在两个高频实战场景:sshd 白名单 (只允许受信任来源登录)和 ssh proxy / 跳板机 (如何无公网 IP 地穿透访问内网机器)。所有示例均可在生产环境直接套用。文中 IP / 端口 / 域名均为文档示例 (RFC 5737 保留地址、RFC 1918 私有段、example.com),端口、网段按你自己的环境替换即可。
目录
- [sshd 是什么](#sshd 是什么)
- [sshd_config 核心配置](#sshd_config 核心配置)
- [实战一:sshd 白名单(4 种方案)](#实战一:sshd 白名单(4 种方案))
- [实战二:SSH 代理 / 跳板机(ssh_proxy)](#实战二:SSH 代理 / 跳板机(ssh_proxy))
- 安全加固清单
- 排障速查
- 总结
1. sshd 是什么
sshd 是 OpenSSH 的服务端守护进程 ,监听 TCP 22(或你改的端口),负责处理客户端的 SSH 连接、身份认证、加密通道建立。客户端工具是 ssh/scp/sftp。
bash
# 看版本(客户端和服务端一起装)
ssh -V
# systemd 系统:状态 / 启动 / 开机自启
systemctl status sshd
systemctl enable --now sshd
# 配置文件路径
# 主配置:/etc/ssh/sshd_config
# 片段目录(部分发行版):/etc/ssh/sshd_config.d/*.conf (优先级高于主文件)
# 主机密钥:/etc/ssh/ 下 ssh_host_*_key
# 服务端日志:/var/log/secure (RHEL) 或 /var/log/auth.log (Debian)
⚠️ 改完配置务必校验语法再重启,避免把自己锁外面:
bashsshd -t # 校验配置文件语法,无误则无输出
2. sshd_config 核心配置
贴一份生产可用的基线配置 (先放主文件,或在 sshd_config.d/99-hardening.conf):
bash
# /etc/ssh/sshd_config.d/99-hardening.conf
Port 2222 # 改非标端口,挡掉 99% 自动化扫描
AddressFamily inet # 仅 IPv4,按需
ListenAddress 0.0.0.0
Protocol 2 # 强制 SSHv2,杜绝 v1
# ------ 认证 ------
PermitRootLogin no # 禁止 root 直接登录,用普通用户 + sudo
PubkeyAuthentication yes # 开启密钥
PasswordAuthentication no # 密钥就绪后关密码(先确认密钥能登再关!)
AuthenticationMethods publickey # 强制仅公钥(等价上一条但更硬)
PermitEmptyPasswords no
# ------ 会话 ------
LoginGraceTime 30 # 认证宽限时间
MaxAuthTries 3 # 最多试 3 次
MaxSessions 10
ClientAliveInterval 300 # 5 分钟无活动发探测
ClientAliveCountMax 2 # 连续 2 次无响应断连
# ------ 其他 ------
X11Forwarding no # 不需要图形就关
AllowTcpForwarding yes # 跳板机场景需要(下面实战二要用)
PermitTunnel no
UseDNS no # 关反向解析,登录更快、避免 DNS 卡住
应用:
bash
sshd -t && systemctl restart sshd
3. 实战一:sshd 白名单(4 种方案)
"白名单" = 只允许某些来源(IP/网段)登录,其余一律拒绝。下面 4 种按需组合,推荐方案 3(防火墙)做第一道闸 + 方案 1(sshd 原生)做第二道闸。
方案 1:sshd 原生 ------ AllowUsers user@host + Match Address
OpenSSH 5.6+ 支持在 user@host 里写 CIDR,这是最干净的服务端白名单:
bash
# 只允许:
# - 运维 alice 从 203.0.113.0/24 登录
# - 内网 10.10.0.0/16 任意用户登录
# - 其余来源全部拒绝
AllowUsers alice@203.0.113.0/24 *@10.10.0.0/16
注意:
AllowUsers是白名单语义 ------写了它之后,没列出的用户/来源一律拒登 。配合DenyUsers可叠加黑名单。
更精细地用 Match Address 按来源控制认证方式:
bash
# 信任网段内允许密码(方便应急),公网只允许密钥
Match Address 10.10.0.0/16
PasswordAuthentication yes
Match Address *,!10.10.0.0/16
PasswordAuthentication no
方案 2:TCP Wrappers(hosts.allow / hosts.deny)
老派做法,很多内网环境还在用:
bash
# /etc/hosts.deny
sshd: ALL
# /etc/hosts.allow
sshd: 203.0.113.0/24 10.10.0.0/16
⚠️ 大坑 :Debian 8 / RHEL 7 之后很多发行版编译 OpenSSH 时带了
--without-tcp-wrappers,hosts.allow直接不生效。先验证:
bashldd $(which sshd) | grep -i wrap # 有输出才支持,无输出此法失效不确定就别依赖它,用下面的防火墙方案。
方案 3(推荐):防火墙限制 22/端口来源
白名单放防火墙最稳,连接在最底层就被 drop:
bash
# ------ firewalld(RHEL/CentOS 7+, Rocky, Alma)------
firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port protocol="tcp" port="2222" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.10.0.0/16" port protocol="tcp" port="2222" accept'
# 默认区域里没被 accept 的 2222 流量保持默认(建议默认 zone 设为 drop 或仅放行白名单)
firewall-cmd --reload
# ------ ufw(Ubuntu/Debian)------
ufw allow from 203.0.113.0/24 to any port 2222
ufw allow from 10.10.0.0/16 to any port 2222
ufw deny 2222 # 兜底拒绝其余来源(或 ufw default deny incoming)
# ------ iptables(裸机/老系统)------
iptables -A INPUT -p tcp -s 203.0.113.0/24 --dport 2222 -j ACCEPT
iptables -A INPUT -p tcp -s 10.10.0.0/16 --dport 2222 -j ACCEPT
iptables -A INPUT -p tcp --dport 2222 -j DROP
方案 4:fail2ban(动态黑名单,防爆破)
白名单是"允许谁",fail2ban 是"谁乱试就封谁",两者互补:
bash
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/secure # Debian 改成 /var/log/auth.log
maxretry = 3
bantime = 1h
findtime = 10m
bash
systemctl enable --now fail2ban
fail2ban-client status sshd # 看被封 IP
组合建议:防火墙白名单(方案3)拦来源 + sshd 原生限制(方案1)兜底 + fail2ban(方案4)防白名单内也有肉鸡乱试。
4. 实战二:SSH 代理 / 跳板机(ssh_proxy)
真实场景:内网机器 10.10.20.141:2222 没有公网 IP ,你只能先登录一台有公网的跳板机 jump.example.com:2222,再跳进去。这就是 ssh proxy。
4.1 ProxyJump(最推荐,OpenSSH 7.3+)
-J 一行搞定多级跳板:
bash
# 单级跳板
ssh -J jumpuser@jump.example.com:2222 inneruser@10.10.20.141 -p 2222
# 多级跳板(A -> B -> C)
ssh -J user@A,user@B user@C
4.2 ProxyCommand + nc(老版本兼容)
没有 -J 时用 ProxyCommand 调 nc/ncat/socat:
bash
ssh -o ProxyCommand="ssh -p 2222 jumpuser@jump.example.com -W %h:%p" \
-p 2222 inneruser@10.10.20.141
%h:%p 会被替换成最终目标主机和端口,-W 在跳板机上建立到目标的明文隧道(加密在两端 ssh 之间,跳板机看不到内容)。
4.3 ~/.ssh/config 完整示例(一劳永逸)
把跳板逻辑写进客户端配置,之后直接 ssh inner:
bash
# ~/.ssh/config
# 跳板机(有公网)
Host jump
HostName jump.example.com
Port 2222
User jumpuser
IdentityFile ~/.ssh/id_ed25519
# 内网目标机(无公网,经 jump 跳)
Host inner
HostName 10.10.20.141
Port 2222
User inneruser
IdentityFile ~/.ssh/id_ed25519
ProxyJump jump
# 老版本无 ProxyJump 时改用:
# ProxyCommand ssh -p 2222 jumpuser@jump.example.com -W %h:%p
# 通配:所有 10.10.* 内网机器统一走跳板
Host 10.10.*
ProxyJump jump
Port 2222
bash
chmod 600 ~/.ssh/config
ssh inner # 自动走跳板,无需手动 -J
scp file.txt inner:/tmp/ # scp 同样走跳板
4.4 动态 SOCKS 代理(-D,浏览器翻内网)
把跳板机当 SOCKS5 出口,浏览器配 127.0.0.1:1080 即可访问内网 Web:
bash
ssh -D 1080 -N -f jump # -N 不执行命令 -f 后台
4.5 端口转发(-L / -R)
bash
# 本地转发:把内网 10.10.20.141:3306 映射到本机 13306,经跳板穿透
ssh -L 13306:10.10.20.141:3306 -N -f jump
# 远程转发:把本机 8080 暴露到跳板机 18080(临时对外暴露内网服务时用)
ssh -R 18080:localhost:8080 -N -f jump
跳板机要支持转发,记得把第 2 节的
AllowTcpForwarding yes打开。
5. 安全加固清单
| 项 | 建议 | 优先级 |
|---|---|---|
| 改端口 | Port 2222 挡扫描 |
高 |
| 禁 root 直登 | PermitRootLogin no |
高 |
| 关密码 | PasswordAuthentication no + 仅公钥 |
高 |
| 白名单 | 防火墙 + sshd AllowUsers 双闸 |
高 |
| fail2ban | 防白名单内爆破 | 中 |
| 密钥算法 | 用 ed25519:ssh-keygen -t ed25519 |
中 |
| 会话超时 | ClientAlive* 配置 |
中 |
| 双因素 | AuthenticationMethods publickey,keyboard-interactive + Google Authenticator |
高安全场景 |
生成密钥并分发(记住:先确认能密钥登录,再关密码):
bash
ssh-keygen -t ed25519 -C "$(whoami)@$(hostname)" -f ~/.ssh/id_ed25519
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 inneruser@10.10.20.141
6. 排障速查
bash
# 详细调试(看卡在哪一步:DNS / 认证 / 转发)
ssh -v 内网目标 # -vvv 更细
ssh -G inner # 打印 inner 最终生效的全部配置(排查 config 冲突神器)
# 配置改崩了连不上?用控制台/VNC 进机器后:
sshd -t # 先查语法
# 临时开个救援端口(必须用与主端口不同的端口,别撞车):
/usr/sbin/sshd -p 22222 -o PasswordAuthentication=yes
# 看谁在暴力试
journalctl -u sshd -n 50 --no-pager
grep "Failed password" /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -rn
# 跳板连不通时,分层验证:
ssh jump # 1. 跳板本身能登吗
ssh inner # 2. 走 config 的跳板能登吗
ssh -J jump inner -v # 3. 加 -v 看 ProxyJump 在哪断
7. 总结
- sshd 不是"开了就行":把密码认证关掉、改非标端口、禁 root,是基础三件套。
- 白名单要分层 :防火墙(方案3)做第一道物理闸 + sshd 原生
AllowUsers/Match Address(方案1)做逻辑闸,fail2ban 兜底防内部爆破。别迷信 TCP Wrappers------先ldd确认它到底编进去了没有。 - 跳板机首选
ProxyJump:-J一条命令穿透内网,配合~/.ssh/config把10.10.*全量走跳板,日常ssh inner就跟直连一样顺手。-D给浏览器开 SOCKS,-L/-R做端口转发,覆盖 90% 内网访问需求。 - 改配置铁律 :
sshd -t校验 → 保留一个已验证的救援会话/端口 → 再重启。别把自己锁门外。
把这篇里
203.0.113.0/24(办公/信任来源段)、10.10.0.0/16(内网段)、2222(SSH 端口)、jump.example.com(跳板机)换成你自己的网段、端口和跳板域名,即可直接落地。