1. SSH连接的底层逻辑:先分清客户端、服务端和认证方式
1.1 一条ssh命令背后发生了什么
在Windows上敲下ssh root@192.168.1.10的那一刻,其实同时发生了三件事:你本地的OpenSSH客户端把输入加密成数据流,通过网络发到目标机器的22端口;目标机器上运行的sshd服务解密数据,检查你的用户名和认证信息;验证通过之后,服务端才给你分配一个远程shell。整个过程里,密码、命令、输出都不会以明文在网络上传,这就是SSH能在今天取代Telnet成为运维默认协议的核心原因。
生活化一点,可以把SSH理解成一个双向加密的通道协议:主机地址决定你敲哪扇门,端口号决定你走哪个入口,用户名和认证信息决定你是不是这个家里的人。Windows和Linux之间的连接,不是用一个特殊软件给对方发消息,而是两边都遵守SSH这套协议,客户端负责发起,服务端负责响应。搞清楚这个分工,下面所有操作都不会乱。
还要强调一点:同一台机器既可以当客户端,也可以当服务端。Windows上装了OpenSSH Client,它可以连Linux;Windows上再装OpenSSH Server,Linux反过来也能连它。很多文章只讲“Windows怎么连Linux”,导致大家以为SSH是Linux专用,这是最常见的误解。
1.2 开始操作前必须确认的三件事
我在帮人排查“为什么连不上”的时候,发现大部分问题不在命令,而在三个基础信息没核对清楚。
第一,目标机器的IP或主机名。最简单的方式是到目标机器上执行ip addr(Linux)或ipconfig(Windows),确认当前地址。虚拟机场景特别容易卡在这:用VMware的NAT网络时,宿主机上ping不通虚拟机,是因为没有选对网卡地址。建议先把虚拟机网络调成桥接模式,或者确保两台机器在同一个网段,再继续。
第二,目标端口。OpenSSH默认是22端口,但如果之前有人改过sshd_config里的Port,或者用了非默认端口转发,就必须带-p参数指定。排查时在目标机器上执行ss -tlnp | grep ssh,看看服务到底监听在哪里。
第三,账号名。Linux下连接用的是系统用户,比如ubuntu、root;Windows下连接用的是Windows用户,比如Administrator。很多人习惯用自己的昵称或邮箱前缀,结果认证一直失败。记住:SSH的用户名就是对方系统里的真实账户名,这一点没法自定义。
三件事确认完,再发起连接会顺利很多。如果你是云服务器用户,还要在云厂商的安全组里确认放行了对应端口,这一步和系统内防火墙是两条独立的链路,少一个都连不上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Windows连接Linux实操:用系统自带OpenSSH客户端建立首个连接
2.1 Windows 10/11自带的OpenSSH客户端如何启用
Windows 10 1809版本之后,OpenSSH客户端基本是系统自带的可选功能,只是有些精简版系统没装上。打开“设置 -> 应用 -> 可选功能”,在列表里找“OpenSSH客户端”,找到就不需要再操作;找不到就点“添加功能”,搜索“OpenSSH客户端”并安装。
用PowerShell也能装,以管理员身份打开终端执行:
powershell复制Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
这条命令会列出当前系统的OpenSSH客户端和服务端状态。然后执行:
powershell复制Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
装完重新打开一个终端窗口,输入ssh -V,能看到类似OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2的输出,就说明客户端可用了。我建议日常直接用Windows Terminal或者PowerShell窗口,别再用老的CMD,主要是为了中文显示和剪贴板体验更好。如果终端里出现中文乱码,试着执行chcp 65001切到UTF-8编码,很多坑其实是编码问题不是网络问题。
2.2 在Linux侧把sshd服务准备好
Linux发行版之间略有差异,但思路一致。拿最常见的Ubuntu/Debian系举例:
bash复制sudo apt update
sudo apt install -y openssh-server
sudo systemctl enable --now ssh
systemctl status ssh --no-pager
看到active (running)就说明服务已经起来了。如果你用的是CentOS、Rocky Linux这类RHEL系,服务名通常叫sshd:
bash复制sudo dnf install -y openssh-server
sudo systemctl enable --now sshd
注意这里的细节:Ubuntu的服务名是ssh,而RHEL系是sshd,很多人在不同发行版之间切换时总习惯性敲systemctl start sshd,在Ubuntu上会提示找不到服务,这不是系统坏了,是服务名不一样。
确认端口监听可以执行:
bash复制ss -tlnp | grep ssh
正常会看到0.0.0.0:22或者*:22的监听条目。如果装了防火墙,再放行一下。Ubuntu用ufw的话:
bash复制sudo ufw allow OpenSSH
RHEL系用firewalld的话:
bash复制sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --reload
2.3 第一次发起连接与常用参数
准备工作做完,在Windows终端里执行:
code复制ssh 用户名@目标IP
比如我想用root账户连192.168.1.10:
code复制ssh root@192.168.1.10
第一次连接会看到类似这样的提示:
code复制The authenticity of host '192.168.1.10 (192.168.1.10)' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxx.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
这里输入yes回车就行。它问的是你信不信任这台服务器,确认过IP是对的就可以信任。之后系统会把这台主机的指纹记录到~/.ssh/known_hosts文件里,下次就不会再问。
如果端口不是22,需要加-p参数:
code复制ssh -p 2222 root@192.168.1.10
如果只是临时指定密钥文件,用-i参数:
code复制ssh -i C:\Users\你的用户名\.ssh\id_ed25519 root@192.168.1.10
连接成功后,命令行提示符会变成Linux风格,比如root@ubuntu:~#,此时你已经在远程机器上了,所有命令都在对方系统里执行。退出远程登录直接输入exit或者按Ctrl+D。
3. Linux连接Windows实操:把Windows的OpenSSH Server开起来
3.1 在Windows 10/11上安装并启动OpenSSH Server
很多人不知道Windows也能被SSH连进来,其实从Win10开始微软就提供了官方SSH服务端组件。安装方式和客户端类似,在“设置 -> 应用 -> 可选功能”里找到“OpenSSH服务器”,添加即可。PowerShell管理员模式执行:
powershell复制Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
装完启动服务并设置开机自启:
powershell复制Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
Get-Service sshd
Get-Service sshd应该显示Running。首次安装时,Windows通常会自动在防火墙里创建允许22端口入站的规则,但有些安全软件或自定义策略会拦住它。保险起见,可以手动确认:
powershell复制Get-NetFirewallRule -Name *ssh*
如果没有对应规则,手动创建一条:
powershell复制New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
3.2 从Linux发起连接:账号名、权限和默认Shell
Windows的SSH服务跑起来之后,Linux端连接方式和连普通Linux服务器没有区别:
bash复制ssh Administrator@192.168.1.200
这里的Administrator换成Windows上的真实账户名,IP换成Windows主机的IP。认证时输入的密码是Windows账户的登录密码。如果使用微软账户登录Windows,情况会稍微复杂一些,因为本地账户名可能不是你邮箱前缀,建议先用whoami命令确认当前用户的准确账户名,再用net user查看系统里有哪些账户。
连接成功后,默认会进入CMD命令行,看到C:\Users\Administrator>这样的提示符。如果想一连接就直接进入PowerShell,需要修改注册表指定默认Shell:
powershell复制New-Item -Path "HKLM:\SOFTWARE\OpenSSH" -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
改完重启sshd服务:
powershell复制Restart-Service sshd
这里有个非常容易被坑的点:如果你想用Administrator账户通过密钥免密连接Windows,公钥不是放在用户目录下的.ssh\authorized_keys,而是要放在C:\ProgramData\ssh\administrators_authorized_keys这个特殊位置。普通用户的公钥则放在C:\Users\用户名\.ssh\authorized_keys。第一次配置免密失败,十有八九就是放错了地方。
Windows这端作为服务端时,文件权限要求也很严格。C:\ProgramData\ssh\下的文件默认权限是给Administrator和SYSTEM的,如果你用普通用户去改动,会因为权限拒绝无法保存。建议先用管理员PowerShell操作,再通过文件属性里的“安全”页签检查权限是否只保留了必要的账户。
4. 免密登录配置:密钥对、公钥部署和权限细节一条龙
4.1 生成密钥对:为什么推荐ed25519而不是RSA
密码登录每次都要输入,而且密码本身有被暴力破解的风险。SSH的免密登录基于非对称加密:你生成一对密钥,私钥留在本地,公钥放到远程服务器的授权列表里。连接时,服务端用公钥验证你手里确实握着对应的私钥,验证通过就直接放行。
生成密钥对推荐用ed25519算法:
bash复制ssh-keygen -t ed25519 -C "username@hostname"
-C参数是注释,可以随便填,方便你以后认出这把钥匙是谁的。一路回车会在~/.ssh/下生成两个文件:id_ed25519是私钥,id_ed25519.pub是公钥。
为什么不推荐老的RSA 2048?主要原因有两个:一是密钥长度长,但安全性上并不比ed25519更强;二是很多新版本OpenSSH默认不信任过短的RSA密钥,如果你的工具链比较老,还可能遇到“no matching host key type”的报错。ed25519在OpenSSH 6.5之后的版本里都支持,兼容性已经非常好。当然,如果对方系统特别老,只支持RSA,那也可以用:
bash复制ssh-keygen -t rsa -b 4096 -C "comment"
生成过程中会让你设置passphrase,也就是私钥的密码短语。这里建议设置一个,哪怕简单一点也比空着强。私钥一旦泄露,如果没有passphrase保护,别人拿到文件就能直接登录;加了passphrase,别人拿到后还需要再破一道口令。怕每次连接都要输passphrase麻烦?后面4.4节会用ssh-agent解决这个问题。
4.2 把公钥部署到目标机器的标准动作
Linux服务器之间部署公钥,最简单的方式是用ssh-copy-id:
bash复制ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
执行后会提示你输入一次密码,然后自动把公钥追加到远端用户的~/.ssh/authorized_keys里。Windows端默认没有ssh-copy-id命令,需要手工完成同样的事,推荐用管道方式:
bash复制cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
这条命令做的事情是:把公钥内容通过管道发送到远端,远端先创建.ssh目录,设置700权限,然后把公钥追加到authorized_keys文件,最后把文件权限设为600。每一段都不能少。
如果你要部署的目标是Windows OpenSSH服务器,普通用户就把公钥内容放到C:\Users\用户名\.ssh\authorized_keys;Administrator用户放到C:\ProgramData\ssh\administrators_authorized_keys。文件内容就是id_ed25519.pub里那一整行,不要带任何格式。
4.3 权限和存放位置:最容易翻车的两个细节
免密登录配置好后连不上,最普遍的原因就是权限不对。Linux这边,远端家目录不能对其他用户可写,否则sshd出于安全策略会拒绝信任公钥。检查方法:
bash复制ls -ld ~
ls -ld ~/.ssh
ls -l ~/.ssh/authorized_keys
建议权限如下:
| 路径 | 推荐权限 | 说明 |
|---|---|---|
家目录 ~ |
755或700 | 不能是777 |
~/.ssh目录 |
700 | 只有自己可读写执行 |
~/.ssh/authorized_keys |
600 | 只有自己可读写 |
私钥 id_ed25519 |
600 | 必须私有 |
公钥 id_ed25519.pub |
644 | 公开文件不影响安全 |
如果发现权限不对,直接修正:
bash复制chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519
Windows端同样在意权限。很多人把公钥放进C:\ProgramData\ssh\administrators_authorized_keys后还是不生效,打开文件属性里的“安全”页签,发现继承的权限里有Everyone或Users组可读。OpenSSH在Windows上对公钥文件的权限检查比Linux还严格,必须确保只有Administrator和SYSTEM有权限。调整方法是在文件属性里禁用继承,然后删除多余用户,只保留当前管理员和SYSTEM。
4.4 ssh-agent:解决私钥passphrase反复输入的问题
如果你给私钥设置了passphrase,每次连接都要输入一次,时间长了很烦。Windows上的解决办法是启动ssh-agent服务:
在PowerShell管理员模式下执行:
powershell复制Get-Service ssh-agent
如果状态不是Running,先启动并设为自动:
powershell复制Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent
然后把私钥加入代理:
powershell复制ssh-add $env:USERPROFILE\.ssh\id_ed25519
输入一次passphrase,之后本次开机周期内连接就不需要再输入了。Linux/Mac环境下同理,ssh-agent一般是系统自带,执行ssh-add即可。这个机制的本质是把私钥加载进内存中的代理进程,SSH连接时直接通过代理完成签名,既不用每次敲passphrase,也不用把私钥复制到其他机器。
配置完免密后,建议用ssh -v详细模式验证一次:
bash复制ssh -v user@server
看到Authenticated to ...就表示免密已经生效。
5. 连接失败现场排查:从报错信息倒推根因
5.1 Permission denied类报错的根因图谱
Permission denied (publickey,password)应该是最常见的报错。看到它别急着以为是“没权限”,先按下面顺序检查。
如果是密码登录失败,先确认用户名和密码是否真的正确。Linux下的密码在输入时不回显,很多人以为键盘坏了,其实是正常的。Windows连接Linux时,如果用户名写错,服务端日志会显示无效用户。
如果是密钥认证失败,最常见的五个原因:
- 公钥没有放进对方用户的
authorized_keys文件里。 - 公钥放进去了,但
authorized_keys或.ssh目录权限不对。 - 私钥在本地路径不对,客户端根本没找到。
- Windows管理员公钥放错了路径,没放
C:\ProgramData\ssh\administrators_authorized_keys。 sshd_config里设置了PubkeyAuthentication no或者PasswordAuthentication no,导致某种登录方式被关闭。
排查时用详细模式看日志输出:
bash复制ssh -vvv user@server
客户端会在输出里明确提示Offering public key、Authentications that can continue等信息,能定位到问题是卡在密钥格式、权限还是服务端策略上。服务端日志通常记录得更清楚,Ubuntu看/var/log/auth.log,RHEL系看/var/log/secure:
bash复制sudo grep "sshd" /var/log/auth.log | tail -20
看到类似Authentication refused: bad ownership or modes for directory就是权限问题;看到User root from 1.2.3.4 not allowed because listed in DenyUsers那就是用户被禁了。
5.2 Connection refused类报错的服务端排查
Connection refused和超时是另一个大方向,说明TCP层都没建立起来。此时问题不在认证,而在服务没监听、端口被挡或防火墙拦截。
排查步骤很固定:
bash复制# 1. 目标机器上确认sshd进程在运行
ps -ef | grep sshd
# 2. 确认监听端口
ss -tlnp | grep ssh
# 3. 如果监听在127.0.0.1而不是0.0.0.0,说明只有本机能连
如果服务没起来,先看启动日志:
bash复制journalctl -u ssh --no-pager | tail -30
很多时候是sshd_config写错导致服务起不来。改过配置后务必先测试再重启:
bash复制sudo sshd -t
这个命令只做语法检查,不实际启动服务。能通过再执行sudo systemctl reload ssh,避免把自己锁在门外。
防火墙在了这一步。云服务器要检查两层:系统内防火墙和云安全组。我在实际中遇到过最奇怪的情况是系统内防火墙全关、sshd也在监听,但连接还是超时,最后发现是云厂商安全组策略里没放行22端口。这个坑不在系统内,排查时要跳出单机视角。
5.3 known_hosts指纹冲突与网络设备场景
有时候报错是这样的:
code复制WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
ED25519 host key for 192.168.1.10 has changed.
这说明本地known_hosts里记录的指纹和当前服务器返回的不一致。原因通常是目标机器重装了系统,或者你连的IP被分配给了另一台机器,也可能是有人在你和目标之间做了地址伪装。在确认目标没问题的前提下,可以用这条命令删除旧指纹:
bash复制ssh-keygen -R 192.168.1.10
然后重新连接并接受新指纹。
这个场景在网络设备上特别常见。很多人用SecureCRT或类似工具去连华为交换机,之前交换机的SSH密钥是旧的,设备升级或恢复出厂后指纹变了,客户端还在按老记录校验,就会提示密钥不匹配。处理思路一样:删除本地保存的旧主机密钥记录,重新建立信任。SecureCRT里可以在“SSH2 -> Host Key”的会话选项里删除旧密钥,命令行工具就用ssh-keygen -R。
5.4 VSCode Remote-SSH的专属坑
VSCode连远程服务器是现在最常见的开发方式,但它的报错有时候和普通SSH不太一样。装好Remote-SSH扩展后,在~/.ssh/config里配置好Host信息,点击连接后,VSCode会在远程机器上下载一个vscode-server。这个环节失败率最高。
常见问题是下载慢或下载失败,因为vscode-server要从微软的服务器拉取。这种情况可以检查远程机器上能否正常访问外网,也可以手动下载对应的commit版本解压到~/.vscode-server/bin目录。网上有很多现成脚本会帮你完成这个动作,但核心逻辑就是:让远程机器存在一个完整的vscode-server目录。
另一个常见问题是VSCode连接时提示Failed to connect to the remote extension host server。这通常不是SSH的问题,而是远程机器缺少curl、wget、tar或unzip,导致VSCode无法部署服务端。解决办法很直白,先到远程机器上把基础工具装齐,再重新连接:
bash复制sudo apt install -y curl wget tar unzip # Ubuntu
sudo dnf install -y curl wget tar unzip # RHEL系
还有一类老服务器算法兼容问题。如果你的VSCode是OpenSSH 8.9以上,但远程机器还是旧版本的ssh服务,会看到类似no matching host key type found. Their offer: ssh-rsa。临时解法是在SSH配置文件里加上旧算法支持:
code复制Host myserver
HostName 192.168.1.10
User root
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
这个方案只适合旧环境过渡,如果服务器是你自己管理的,建议直接升级OpenSSH服务端。
6. 连上之后的常用动作:文件传输、远程开发与多主机管理
6.1 用scp和sftp解决跨系统传文件
SSH免密配置好之后,最直接的收益就是文件传输方便了。scp命令基于SSH协议,本地Linux或Windows都能直接使用。从Windows上传文件到Linux:
code复制scp C:\local\path\data.tar.gz root@192.168.1.10:/root/
传整个目录加-r:
code复制scp -r C:\local\project\ root@192.168.1.10:/home/user/project
从远程下载到本地:
code复制scp root@192.168.1.10:/root/result.log C:\local\path\
scp适合一次性操作。如果你需要交互式浏览远端目录、增删文件,可以进入sftp:
code复制sftp root@192.168.1.10
进去之后,ls看远端目录,lcd切换本地目录,get下载文件,put上传文件,mget和mput支持通配符批量操作。比scp灵活很多。
6.2 用VSCode Remote-SSH把远端当本地写
远程开发的核心需求是:代码在服务器上,编辑在本地。VSCode的Remote-SSH扩展能把你本地的VSCode界面完整映射到远程环境,终端、调试、代码提示都像是直接在远程机器上工作。
建议在~/.ssh/config里写清楚连接配置,这样VSCode能直接识别。Windows用户在C:\Users\你的用户名\.ssh\config,Linux用户在~/.ssh/config:
code复制Host dev
HostName 192.168.1.10
User root
Port 22
IdentityFile ~/.ssh/id_ed25519
保存后,在VSCode里按Ctrl+Shift+P输入“Remote-SSH: Connect to Host”,选dev就可以连上。连接过程会看到插件在远程安装扩展,第一次通常要等一会儿。连上后打开的是远程目录,/root/project这些路径就直接出现在资源管理器里了。
我在实际使用中的体会是:远程开发最怕网络不稳导致频繁掉线。如果连接持续断开,先检查WiFi环境是不是有功率节约策略,Windows的电源管理里把无线网卡的最小省电模式调到“最高性能”,会明显改善。服务器端也可以在sshd_config里加ClientAliveInterval 60和ClientAliveCountMax 3,让服务端主动保活,而不是等连接超时了才发现断了。
6.3 用config文件和ssh-agent管理多台主机
主机一多,每次敲完整命令就很烦。在SSH配置里给每台主机起个别名,之后直接ssh 别名就能连接,这个文件同时兼容Windows和Linux的OpenSSH客户端。一个多主机配置示例:
code复制Host prod
HostName 120.xx.xx.xx
User deploy
Port 2202
IdentityFile ~/.ssh/id_ed25519
Host office
HostName 192.168.1.20
User admin
IdentityFile ~/.ssh/office_key
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Host *是通配配置,对所有主机生效。ServerAliveInterval让客户端每60秒发一次保活包,防止长时间无操作被服务端断开。
多机批量操作不建议自己写循环加SSH,维护成本高还容易出错。真需要批量执行命令,用Ansible这类工具,它底层还是SSH,但把密钥、用户、sudo权限都归到了一套体系里管理。先用免密登录把互信打通,再在Ansible的inventory里配置好主机组,一次ansible all -m ping就能检测几十台机器的连通性。
7. SSH服务安全加固实践:把系统侧的门锁好
7.1 sshd_config里值得改的几个参数
SSH跑在公网上的时候,几乎每小时都会收到暴力破解尝试。登录文件一翻,全是来自各个IP的Failed password。这种情况下,除非你非常需要密码登录,否则直接禁用密码认证是最彻底的做法。
修改/etc/ssh/sshd_config,重点看这几个配置:
text复制# 默认端口改成非标准端口,比如 2222
Port 2222
# 禁止root直接用密码登录
PermitRootLogin prohibit-password
# 禁用密码认证,只允许密钥
PasswordAuthentication no
# 允许的登录用户白名单
AllowUsers root dev ops
# 单次连接最多允许3次认证尝试
MaxAuthTries 3
# 登录未成功前最多等待30秒
LoginGraceTime 30
# 客户端保活参数
ClientAliveInterval 60
ClientAliveCountMax 3
PermitRootLogin prohibit-password的意思是root可以通过密钥登录,但不能通过密码登录。这样既保证管理入口存在,又把最容易被爆破的root+密码组合断掉。
改之前先备份:
bash复制sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%F)
改完之后先语法检查:
bash复制sudo sshd -t
没问题再重载服务:
bash复制sudo systemctl reload ssh
如果你是远程操作,强烈建议开两个会话:一个会话改配置,另一个会话保持连接不动。改完先别关当前连接,在第二个会话里测试新配置能不能连上,确认无误再结束旧会话。我就亲眼见过有人把端口改成2222,防火墙忘了放行,然后重载服务后把自己锁在云服务器外的场景。
7.2 用fail2ban和防火墙应对暴力尝试
如果确实还需要密码登录,那必须加一层防护。fail2ban会监控SSH登录日志,发现某个IP在短时间内多次失败就自动封禁一段时间。Ubuntu安装:
bash复制sudo apt install -y fail2ban
默认配置就能工作,但建议写一个白名单和封禁策略。编辑/etc/fail2ban/jail.local:
ini复制[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
maxretry是失败次数,bantime是封禁秒数。RHEL系日志路径改成/var/log/secure。启动并设置开机自启:
bash复制sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
看到Banned IP list里有IP,说明已经在干活了。firewalld也可以直接限制来源IP,比fail2ban更硬:
bash复制sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.5" port port="22" protocol="tcp" reject'
sudo firewall-cmd --reload
这就把某个异常IP彻底挡在外面。
7.3 收到大量异常SSH连接时的紧急处理思路
如果你突然发现服务器负载升高、who命令下方出现一大排陌生登录记录,或者journalctl里全是SSH连接日志,先冷静,按这个顺序处理。
第一步,立刻查看当前登录会话,确认有没有非法登录:
bash复制who
last -20
第二步,查看失败登录来源:
bash复制sudo grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c
第三步,临时封禁排名靠前的恶意IP。iptables命令一行就能加:
bash复制sudo iptables -I INPUT -s 1.2.3.4 -j DROP
如果有firewalld,用前面提到的rich-rule方式。注意iptables规则重启会丢,临时封锁后要尽快从防火墙配置里做持久化。
第四步,对照检查sshd_config是否开启了PermitRootLogin yes和PasswordAuthentication yes。这两项同时开启几乎等于把root密码爆破的门打开了一半。如果业务允许,直接改掉这两个配置并重启sshd。
第五步,确认服务端口是否直接暴露在公网。很多场景下SSH只需要在办公网或内网访问,建议在云安全组或本地防火墙里把来源IP范围收窄。只允许公司和固定出口IP连22端口,爆破量会直接下降几个数量级。
还有一个小技巧:如果你看到某个IP集中攻击,可以通过whois查询该IP归属,如果是云厂商的扫描段,直接封禁来源段往往更有效。但要注意封禁前确保该段不是你自己的云服务器出口IP,否则会把自己也挡在外面。
我在实际运维中养成的习惯是:所有服务器统一走密钥认证,密码登录全部关闭,非默认端口视场景决定是否修改。公网直接暴露的机器一定开fail2ban。这几条做下来,SSH相关的告警几乎可以忽略不计。对于只在内网使用的测试虚拟机或者本地Windows和Linux互连,至少也要把密码改成强密码,并保持系统和OpenSSH组件及时更新,因为SSH自身的漏洞修复都是通过版本升级解决的。
