1. 先搞清楚:SSH 不只是"远程登录"那么简单
很多人把 SSH 当成一个普通的远程登录工具,装了就能用,能用就不管。但在 Ubuntu 22.04 上真正把 SSH 配好,尤其是配到"能上生产环境"的程度,其实涉及一整套链路:协议选型、密钥体系、服务配置、登录策略、防火墙配合、暴力破解防护。这个版本默认用的是 OpenSSH 8.9p1,有几个行为变化值得注意,后面会提到。
这篇内容围绕 Ubuntu 22.04 下的 SSH 服务展开,从零开始把安装、密钥登录、安全加固、常见故障排查全部走一遍。适合三类人:一是刚接触 Linux 服务器、想把自己的 VPS 或实验室机器管起来的初学者;二是公司内部运维,需要统一 SSH 安全基线的人;三是自己折腾 NAS、树莓派、HomeLab,想远程管理又不放心裸奔的玩家。无论哪种场景,读完你都能照着操作,至少能把默认状态下的 SSH 提升到"可以长期暴露在公网"的水平。
先说一个基本的认知:SSH 服务端最重要的配置文件是/etc/ssh/sshd_config,几乎所有的安全策略都在这里调整。但配置只是最后一步,前面还有密钥生成、文件权限、防火墙联动这些容易被忽略的环节。很多"明明配置了密钥登录还是失败"或者"改了端口连不上了"的案例,问题都出在配置之外的地方。
2. 安装与服务管理:先把地基打好
2.1 检查是否已有 SSH 服务
Ubuntu 22.04 的桌面版默认没有安装 openssh-server,服务器版则在安装系统时可选。第一步先确认系统里有没有:
systemctl status ssh如果提示Unit ssh.service could not be found,说明没装。也可以用dpkg -l | grep openssh-server验证包是否安装。
没装的话直接安装:
sudo apt update sudo apt install -y openssh-server安装完成后,用sudo systemctl status ssh能看到服务状态。正常情况下应该是active (running),监听端口默认是 22。
这里有第一个容易踩的坑:Ubuntu 22.04 下服务的名称是ssh而不是sshd。虽然你执行systemctl status sshd在某些情况下也能有反应,那是因为系统做了兼容处理,但规范做法是用ssh。另外,修改配置后重载服务用的是sudo systemctl restart ssh,不是reload——reload只会让主进程重新读取配置,而某些参数(比如ListenAddress变更)在 reload 时并不会完全生效,保险起见直接 restart。
2.2 防火墙状态检查
很多用户遇到"SSH 连不上"的第一反应是配置错了,但其实新装的 Ubuntu 22.04 默认可能开了 ufw 但没放行 SSH。如果你之前启用过防火墙,重启后 SSH 服务虽然起来了,但外部流量进不来。
检查当前防火墙状态:
sudo ufw status verbose如果显示Status: active,确认是否有 OpenSSH 或 22 端口的放行规则。没有的话:
sudo ufw allow OpenSSH这里我建议用服务名而非端口号。ufw 的/etc/ufw/applications.d/openssh-server里定义了这个服务,直接用allow OpenSSH的好处是以后即使改了 SSH 端口,只要配套更新这个服务定义,规则也能同步。不过多数人改端口后懒得动服务定义,所以直接sudo ufw allow 22/tcp也完全可以,看个人习惯。
2.3 确认网络层可达
SSH 服务起没起来,和你能不能连上是两回事。排查链路是:服务状态 -> 本机回环测试 -> 局域网/公网测试。
本机先测:
ssh localhost如果能通,说明服务本身没问题。再查看监听地址:
sudo ss -tlnp | grep ssh正常情况下能看到0.0.0.0:22或:::22。如果这里只显示127.0.0.1:22,说明 sshd 只监听了本机回环地址,外部肯定连不进来。这种情况常见于某些安全加固脚本把ListenAddress改坏了,或者系统里存在多个配置文件互相覆盖。
从本机测试到外部测试之间,最好也确认一下 IP 地址:
ip addr show找到你网卡对应的 IP(通常是eth0或ens33这类),然后在另一台机器上ssh 用户名@IP测试。
3. 核心配置逐项拆解:看懂 sshd_config 再动手
3.1 先备份,再修改
这个习惯我强调过无数次:修改任何系统级配置文件之前,先备份。因为 SSH 配置出问题会导致你远程登录不上,如果服务器不在身边,那可能就得靠机房带外管理或者重启进救援模式,麻烦得很。
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)这样备份带日期,以后想回滚也知道是哪天的版本。
3.2 端口与监听地址
默认端口 22 是 SSH 的"标准答案",但也是扫描器最优先探测的端口。公网服务器上每天被扫描上千次是很常见的。把端口改成一个不常用的高位端口,能在一定程度上减少被扫描到的概率——注意,只是减少,不是杜绝。真正防暴力破解靠的是密钥认证和 fail2ban。
修改端口:
Port 22022这里有两个细节:
第一,端口范围建议选 1024 以上的非著名端口。低于 1024 的端口需要 root 权限监听,sshd 启动时确实以 root 身份运行,所以没问题,但没必要去凑那些知名端口。
第二,Port指令可以写多个,sshd 会同时监听多个端口。这在迁移期间很有用:先加一个新端口,确认能连上,再移除旧端口,降低把自己锁在外面的风险。比如:
Port 22 Port 22022改完后重启服务,用新端口登录确认没问题,再注释掉 22。
监听地址方面,如果服务器有多块网卡,而你只需要在内网使用 SSH,可以限定只监听内网 IP:
ListenAddress 192.168.1.100这样即使防火墙规则写漏了,外部网络也无法直接访问这个端口。这种"纵深防御"的思路比单纯依赖防火墙更可靠。
3.3 认证方式的优先级设置
sshd 的认证逻辑是这样的:先验证客户端能提供的认证方法,按顺序逐个尝试。AuthenticationMethods参数可以控制必须使用哪些方法的组合。
最常见的安全要求是"必须密钥登录,禁止密码登录"。对应配置:
PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no PermitEmptyPasswords no等一下,KbdInteractiveAuthentication这个很多人忽略。有些系统上密码认证是通过 keyboard-interactive 方式实现的(比如 PAM 配置导致),只关PasswordAuthentication不够,KbdInteractiveAuthentication也要一起关掉,否则密码登录依然可能成功。
验证当前生效的认证方式:
sudo sshd -T | grep -E 'passwordauthentication|pubkeyauthentication'sshd -T可以输出最终生效的配置(包括默认值和 Include 进来的其他配置文件的值),排查问题非常好用。
3.4 登录用户限制
允许谁登录、禁止谁登录,也是安全加固的重要部分。
禁止 root 直接登录:
PermitRootLogin no这个策略的理由很简单:root 用户名是固定的,暴力破解只需要猜密码。而普通用户名还需要先猜出来,安全级别差不少。如果你确实需要 root 权限,正确的做法是用普通用户登录,再sudo -i提权。
只允许特定用户登录:
AllowUsers alice bob或者反过来,禁止特定用户:
DenyUsers charlie建议使用AllowUsers白名单模式,管理更清晰。特别是服务器上可能有多个系统账户(比如数据库服务创建的账户),这些账户不需要 SSH 登录,白名单能避免遗漏。
还有一个容易被忽略的:AllowGroups。配合系统用户组来做权限管理更灵活,例如:
AllowGroups ssh-users然后sudo usermod -aG ssh-users alice,以后新员工加入只需要加组,离职移除组即可,不用改配置。
3.5 会话超时与最大尝试次数
这几个参数虽然不起眼,但对安全性影响不小:
ClientAliveInterval 300 ClientAliveCountMax 2 MaxAuthTries 3 LoginGraceTime 60ClientAliveInterval表示服务端每隔 300 秒向客户端发送一次存活探测,如果ClientAliveCountMax次探测都没有响应,就断开连接。这样即使你离开座位忘了退出终端,连接也不会一直被占用。对于长时间跑任务的场景,这个值可以调大一点,但别完全关闭。
MaxAuthTries限制的是每次连接中认证尝试的最大次数。默认值是 6,攻击者在一个连接里可以试 6 次密码。改成 3 更合理。
LoginGraceTime是登录超时时间,默认 120 秒。如果你在 60 秒内没完成认证,连接就被断开。这个值不宜设置过小,否则网速慢的时候用户输密码时间不够,会被莫名断开。
4. 密钥认证配置:彻底告别密码
4.1 为什么推荐密钥认证
密码认证最大的问题是:密码本身可以被暴力破解、可以被钓鱼、可以被键盘记录器截获。而密钥认证基于非对称加密,私钥永远保存在本地,不会在网络上传输。即便服务器被攻破,攻击者拿到的也只是公钥,没有私钥就无法登录其他机器。
从运维角度,密钥认证还有一个隐形好处:配合ssh-agent可以实现免密跳板登录。比如你从笔记本登录跳板机,再从跳板机登录内网机器,密钥转发配置好以后,一次认证全程通行,不用每台机器都输密码。
4.2 密钥生成与上传
生成密钥对使用ssh-keygen,这里建议直接用 ed25519 算法。比 RSA 更快、签名更短,安全性在同等位数下更高。RSA 还需要 3072 位以上才够安全,而 ed25519 天生就是强安全的。
ssh-keygen -t ed25519 -C "your_email_or_nickname" -f ~/.ssh/id_ed25519执行后会提示设置 passphrase(私钥口令)。这个 passphrase 是用来加密私钥文件的,即使私钥文件泄露,没有 passphrase 也无法使用。我建议一定设置,别嫌麻烦。
生成的公钥是~/.ssh/id_ed25519.pub,私钥是~/.ssh/id_ed25519。把公钥内容追加到服务器的~/.ssh/authorized_keys中:
cat ~/.ssh/id_ed25519.pub | ssh username@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"或者更推荐使用ssh-copy-id工具,它会自动处理权限和目录问题:
ssh-copy-id -i ~/.ssh/id_ed25519.pub username@server这里有个极容易踩的坑:authorized_keys文件权限必须是 600,.ssh目录权限必须是 700。如果权限过宽松(比如 644 或 755),sshd 出于安全考虑会直接忽略这个文件里的密钥,导致登录失败。等下,这个"忽略"不是说报错拒绝,而是表现为:密钥认证不生效,然后退回密码认证。很多人排查半天,最后发现就是权限问题。
4.3 Windows 客户端的密钥配置
如果你是 Windows 用户,OpenSSH 客户端也是内置的。生成密钥的方法一致:
ssh-keygen -t ed25519 -C "your_email" -f "$env:USERPROFILE\.ssh\id_ed25519"上传公钥同样可以用ssh-copy-id的 PowerShell 等价形式,或者直接读取公钥文件内容追加到服务器上。Windows 自带的 OpenSSH 版本较新,兼容性没问题。
4.4 验证密钥登录并关闭密码
密钥配置完成后,先不要急着关密码认证。用密钥方式登录一次确认无误:
ssh -i ~/.ssh/id_ed25519 username@server确认能登录后,再修改sshd_config关闭密码登录:
PasswordAuthentication no KbdInteractiveAuthentication no然后重启服务。重启前务必确认当前连接不会断开——如果你正在用 SSH 连接那台服务器,重启服务不会断掉现有连接,但是如果配置语法有误,服务起不来,你当前的连接虽然还活着,新的连接却进不来了。所以改完配置后,开一个新终端测试连接,确认没问题再关掉旧终端。
5. 安全加固进阶:fail2ban 与防火墙联动
5.1 fail2ban 的作用与安装
密钥认证部署后,密码暴力破解基本被挡住了,但 Fail2ban 仍然建议安装。理由是什么?SSH 服务本身暴露在公网,即使无法通过密码登录,攻击者依然可以发起大量连接试探,消耗系统资源;而且如果系统里还有其他服务(比如 Web 管理面板、邮件服务),fail2ban 也能统一防护。
安装:
sudo apt install -y fail2banfail2ban 的配置目录是/etc/fail2ban/,主配置文件是jail.conf。这个文件会被升级覆盖,所以不建议直接改。正确做法是在jail.d/目录下新建一个.local文件,覆盖默认配置:
sudo nano /etc/fail2ban/jail.d/sshd.local写入:
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 3600解释一下参数:
maxretry = 5:5 次认证失败就触发封禁。bantime = 3600:封禁 1 小时,单位是秒。findtime默认值是 600 秒,也就是在 10 分钟内达到maxretry次数才会封禁。logpath在 Ubuntu 22.04 上是/var/log/auth.log,如果是其他发行版可能是/var/log/secure。
启动服务:
sudo systemctl enable --now fail2ban sudo systemctl status fail2ban5.2 fail2ban 的封禁查看与手动解封
查看当前封禁的 IP:
sudo fail2ban-client status sshd输出会显示Currently banned列表。如果误封了自己(比如你连续输错密码),手动解封:
sudo fail2ban-client set sshd unbanip 192.168.1.100这里有个实践技巧:bantime不要一开始就设置成永久(-1)。因为如果误封,影响会很大。先设置 1 小时,观察一段时间,确认没有误伤,再逐步加大。
5.3 UFW 联动
fail2ban 和 ufw 可以联动:当 fail2ban 判定某个 IP 需要封禁时,自动向 ufw 添加一条 deny 规则。这样不仅 SSH 被拦截,该 IP 对所有开放端口都会失效。
配置方式是在sshd.local中增加:
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 3600 action = ufw重启 fail2ban 后,封禁会自动通过 ufw 规则实现。查看:
sudo ufw status你会看到类似DENY 192.168.1.100的规则。
5.4 SSH 相关的高级加固选项
除了 fail2ban,还有几个纵深防御选项值得启用。
TCP Wrappers 在 Ubuntu 22.04 上已经不再推荐(libwrap 支持正在弱化),不用浪费时间。更现代的做法是用 sshd 自身的Match块做条件控制。
比如,对内网和公网来源区别对待:
Match Address 192.168.0.0/16 PasswordAuthentication yes Match Address * PasswordAuthentication no这段配置的含义是:来自内网 192.168.0.0/16 的连接允许密码登录,其余来源必须密钥认证。这种场景适合公司内部:同事在内网用密码登录方便,外部访问必须走密钥。
另一个容易被忽视的参数是X11Forwarding。如果你不需要通过 SSH 转发图形界面,建议关掉:
X11Forwarding no减少不必要的攻击面。同理,AllowTcpForwarding如果不需要端口转发,也建议关:
AllowTcpForwarding no但注意:如果你用 SSH 做隧道(比如访问内网数据库、代理上网),这个必须保持yes。
5.5 关键文件权限一览
SSH 相关的文件权限如果不对,会出现各种莫名其妙的问题。整理一份权限速查表:
| 路径 | 权限 | 归属 |
|---|---|---|
/etc/ssh/sshd_config | 644 | root:root |
/etc/ssh/目录 | 755 | root:root |
~/.ssh目录 | 700 | 用户:用户 |
~/.ssh/authorized_keys | 600 | 用户:用户 |
~/.ssh/id_ed25519 | 600 | 用户:用户 |
~/.ssh/id_ed25519.pub | 644 | 用户:用户 |
记住一个原则:私钥文件和 authorized_keys 文件必须是当前用户可读写,其他用户无权限。sshd 对权限敏感的检查非常严格,权限不对宁可拒绝认证也不冒险执行。
6. 实操中常见的坑与排查实录
6.1 改端口后连不上怎么办
这是最经典的事故。场景:你改了Port配置,重启服务,发现连接失败。
排查顺序:
第一步,确认服务状态:
sudo systemctl status ssh服务是active (running)但连接依然失败,继续往下。
第二步,确认端口监听:
sudo ss -tlnp | grep ssh确认新的端口是否在监听。
第三步,检查防火墙:
sudo ufw status大概率是这里的问题——新端口没在 ufw 中放行。解决:
sudo ufw allow 22022/tcp如果你是在云服务器上操作(比如某云厂商的安全组),还需要去控制台放行新端口。云厂商的安全组规则和系统内防火墙是两层独立的,缺一不可。这个顺序很重要:先改防火墙,再重启 SSH 服务,避免服务重启后新端口被墙住。
6.2 公钥认证失败但密码能登录
这个问题的典型场景是:authorized_keys配置正确,密钥也上传了,但登录时依然提示输入密码。
按以下优先级排查:
- 检查
~/.ssh/authorized_keys是否存在并且内容正确。 - 检查文件和目录权限:
必须分别是 700 和 600。ls -ld ~/.ssh ls -l ~/.ssh/authorized_keys - 检查
sshd_config中PubkeyAuthentication是否为yes。 - 检查
AuthorizedKeysFile路径是否正确,默认值是.ssh/authorized_keys。如果你用了一个自定义路径,比如/etc/ssh/authorized_keys/%u,需要确认那个目录也存在且权限正确。 - 查看系统日志:
日志里会显示sudo tail -n 30 /var/log/auth.logFailed publickey for ...、Authentication refused等关键信息。
补充一个冷知识:如果你登录时看到sign_and_send_pubkey: no mutual signature supported,说明客户端和服务端支持的签名算法不匹配。比如服务端配置了PubkeyAcceptedAlgorithms限制了算法类型,而客户端生成的密钥类型不在白名单里。检查sshd -T | grep pubkeyacceptedalgorithms确认。
6.3 连接非常慢,要卡好几秒才让输密码
这种情况通常是 DNS 反向解析导致的。sshd 默认会尝试对客户端 IP 做反向 DNS 解析,如果 DNS 不通,会等超时。
解决方式,在sshd_config中:
UseDNS no同时建议关闭 GSSAPI 认证(如果不用 Kerberos 环境):
GSSAPIAuthentication no这两个选项改完后,连接速度会有明显改善。特别是内网环境里没有 DNS 解析条件的机器,效果立竿见影。
6.4 多台服务器批量配置的技巧
如果你有十几台服务器都要做同样的 SSH 加固,一台台操作效率太低。可以写一个简单的脚本配合ssh-copy-id批量处理。比如将公钥批量复制到多台机器:
for host in server1 server2 server3; do ssh-copy-id -i ~/.ssh/id_ed25519.pub "$host" done再配合 Ansible 或纯 bash 脚本统一修改sshd_config。需要注意:批量操作前先在测试环境验证一遍配置模板,避免因为某台机器环境差异导致批量事故。
6.5 误改配置导致远程连接彻底断了怎么办
最坏的情况:你改坏了sshd_config,服务重启失败,远程连接断开,而且手上没有其他登录方式。这时候只能靠带外管理(比如云厂商的 VNC 控制台、物理机的 iLO/IPMI)进入系统。提前想好这个应急预案是运维的基本素养。
如果把配置改坏了,但服务还没重启,可以先执行sudo sshd -t检查配置语法。这个命令不启动服务,只解析配置。即使当前 sshd 服务已经挂掉,只要你能通过某种方式进入系统(哪怕是救援模式),都能用它定位错误。
7. 一些值得分享的实操心得
最后说几点长期运维下来积累的经验。
配置 SSH 服务一定要养成"先验证、再收尾"的习惯。每次改动配置,都先在新开的终端里测试连接正常,才认为改动成功。这个习惯帮我躲过了很多次把自己锁在门外的尴尬。特别是改端口、改认证方式、加AllowUsers这类操作,宁可多花半分钟开个新终端,也别赌"肯定没问题"。
关于安全策略的粒度,不需要把所有的配置项都调到最严格。比如PermitRootLogin no和密钥登录是必须的,MaxAuthTries 3合理,但有些参数(比如ClientAliveInterval调成 30 秒)反而会影响正常使用。安全配置的目标是"够用且不伤体验",不是越严越好。
公钥的私钥文件建议定期检查。如果你发现某台机器的私钥可能泄露了(比如笔记本丢了、同步了不该同步的目录),立即从所有服务器的authorized_keys中删除对应的公钥,并重新生成一对。不要抱着"应该没人发现"的侥幸心理。
Ubuntu 22.04 的 OpenSSH 默认禁用了 ssh-dss 算法,也默认移除了对 RSA SHA-1 签名的支持。如果你有一些老设备(比如很老的嵌入式设备)需要连接这台服务器,可能需要在sshd_config中显式配置兼容性参数。但除非确有必要,建议优先升级老设备上的 SSH 客户端,而不是降低服务端的安全标准。
SSH 配置看起来参数多,其实核心逻辑很简单:管住谁能进、怎么进、进来了能做什么。把这三点想清楚,配置就有章法了。我在实际配置中始终遵循这个思路推进,虽然过程中踩过改端口忘开防火墙、密钥权限不对导致认证失败、改配置没备份回滚困难这些坑,但每一次趟过去,后面的运维都轻松不少。