1. 项目概述:SSH是什么,为什么你绕不开它
SSH,全称Secure Shell(安全外壳协议),是Linux运维和开发工作中几乎每天都离不开的基础工具。不管你是管理一台云服务器、登录实验室的机器,还是往服务器上传代码,底层用的基本都是SSH。可以说,没有SSH,就不存在“远程管理Linux”这回事。
刚接触Linux的同学常问:不就是登录个远程机器吗,密码输进去不就行了?这个想法没错,但前提是你面对的是可信网络。SSH的价值在于,它把所有传输内容加密,账号密码、命令输入输出、文件内容全程不裸奔,而且它支持比密码更安全的密钥认证方式。这篇文章从原理讲起,配合实际操作,完整走一遍SSH服务的安装、配置、密钥登录、安全加固,最后把常见报错和排查思路也一并整理出来。内容面向有一定Linux基础、但还没系统摸过SSH的学习者,也适合那些配了半天连不上、或者想提升服务器安全性的朋友作参考。
我会尽量把“为什么要这样配”讲清楚,而不是丢一堆命令让你照着敲完就完事。
2. SSH的核心机制:加密方式和登录流程
2.1 为什么需要SSH而不是Telnet
在SSH出现之前,远程登录主要靠Telnet。Telnet有个致命问题:所有数据,包括用户名和密码,都是明文传输。意思是,只要有人在你和目标服务器之间的网络链路上抓包,账号密码就直接暴露了。这在早期互联网环境还算勉强能用,放到今天完全不可接受。
SSH解决的正是这个问题。它用加密隧道把所有通信内容保护起来,即使中间有人截获数据包,看到的也只是一堆无法解密的密文。所以现在的Linux发行版基本都默认不带Telnet服务端,而SSH已经被系统内置或者可以一键安装。
你或许会想:那我用的密码还是简单的,加密有意义吗?有意义。加密解决的是“传输过程被窃听”的问题,密码强度解决的是“账号被暴力破解”的问题。两者是不同维度的安全,缺一不可。SSH的加密机制保证你输入的密码不会在网络上泄露,但如果密码本身是123456,对方照样可以通过不断试探的方式猜出来。
2.2 对称加密、非对称加密和会话密钥
先说对称加密。它用同一个密钥完成加密和解密,特点是速度快,适合大流量数据。但有个安全问题:密钥怎么安全地传给对方?如果通过网络明文传密钥,那加密就失去意义了。
非对称加密则使用一对密钥:公钥和私钥。公钥可以公开分发,私钥必须自己保管。用公钥加密的数据只能用私钥解密,用私钥加密的数据只能用公钥验证(但无法解密)。非对称加密解决了密钥分发的问题,但速度慢,不适合加密大体积的数据流。
SSH的设计很巧妙:它把两种加密方式结合起来。建立连接时,双方先通过非对称加密协商出一个临时的对称密钥(会话密钥),之后的通信全部用这个会话密钥加密。这样做既保证了初始协商过程的安全性,又兼顾了后续通信的速度。这套机制你不需要完全吃透细节,但理解它有助于后面理解“主机密钥验证失败”这类报错,以及为什么每次连接时客户端会提示你确认服务器指纹。
2.3 密码登录和密钥登录的完整流程
密码登录的流程是这样的:客户端发起连接,服务器把自己的主机公钥发给客户端,双方协商会话密钥,之后客户端把用户名密码通过加密隧道发给服务器,服务器验证通过就建立会话。这个流程很好理解,但有一个隐患:密码可能会被暴力破解。只要你的密码不够复杂,攻击者就可以不断地尝试登录,直到试对为止。
密钥登录则完全换了一套逻辑。你本地生成一对密钥:一个私钥,一个公钥。公钥放到服务器的信任列表里。登录时,服务器给客户端发一个随机挑战,客户端用私钥对这个挑战签名,服务器用事先保存的公钥验证签名。验签通过就允许登录,全程不传输密码。这种方式的安全性建立在私钥保密的基础上,只要私钥不泄露,即使服务器端被拖库,攻击者也无法逆向出你的私钥。
我见过不少朋友一开始嫌密钥登录麻烦,坚持用密码。直到某天发现服务器日志里满屏的Failed password,才痛下决心改密钥认证。老实说,只要测过一次免密登录,就会明白这个麻烦是值得的。
3. 环境准备与SSH服务端配置
3.1 检查已有环境,安装OpenSSH
大多数Linux发行版都带OpenSSH客户端,服务端则需要确认是否安装。用下面这条命令检查:
systemctl status sshd如果提示Unit sshd.service could not be found,说明服务端还没装。各发行版安装命令不同:
Debian/Ubuntu系列:
sudo apt update sudo apt install openssh-server -yRHEL/CentOS系列:
sudo yum install openssh-server -y安装完成后启动服务并设置开机自启:
sudo systemctl start sshd sudo systemctl enable sshd验证监听状态:
ss -lntp | grep 22看到22端口处于LISTEN状态,就说明SSH服务已经正常跑起来了。这里补充一句:修改任何SSH配置后,不建议直接重启sshd服务,而是先用下面这条命令检查配置语法:
sudo sshd -t没输出就是配置没问题,再执行reload:
sudo systemctl reload sshdreload会平滑重载配置,不断开现有连接,比restart更安全。这个习惯建议从第一次配置起就养成。
3.2 sshd_config关键配置项详解
SSH服务端的配置文件位于/etc/ssh/sshd_config。这个文件决定了SSH服务的全部行为。拿到手先别急着大改,搞清楚几个关键项再说。
Port字段指定监听端口,默认22。修改端口的常见理由是减少扫描攻击,因为网络上大量扫描器默认只扫22端口。但改端口不能真正提升安全性,它只是让自动化扫描少盯你一眼。如果确实要改,注意两点:选一个1024以上未占用的端口,并确保防火墙放行新端口。
PermitRootLogin字段控制是否允许root直接登录,可选值有yes、no、prohibit-password。推荐设为prohibit-password,意思是禁止root用密码登录,但允许用密钥登录。这样平时管理方便,又堵死了密码爆破的路。
PasswordAuthentication字段控制是否允许密码登录。设为no后,只有密钥认证能登录,这是安全加固中非常关键的一步。建议在确保密钥登录没问题之后再关密码登录,避免把自己锁在外面。
PubkeyAuthentication字段用于开启密钥认证,默认就是yes。改配置文件时,每项修改后都要记得检查语法并重载服务。用一个表格来总结常用配置项会更直观:
| 配置项 | 默认值 | 推荐值 | 作用 |
|---|---|---|---|
| Port | 22 | 按需修改 | 设置SSH监听端口 |
| PermitRootLogin | prohibit-password | prohibit-password | 禁止root密码登录,允许密钥登录 |
| PasswordAuthentication | yes | 按需改为no | 是否允许密码认证 |
| PubkeyAuthentication | yes | yes | 是否允许密钥认证 |
| MaxAuthTries | 6 | 3 | 限制单连接最大认证尝试次数 |
| LoginGraceTime | 120 | 30 | 登录超时时间(秒) |
3.3 从客户端发起第一次连接
服务端就绪后,客户端连接的命令很简单:
ssh 用户名@服务器地址第一次连接会出现一段提示,内容是服务器的ECDSA密钥指纹,并询问你是否确认连接:
The authenticity of host '192.0.2.10 (192.0.2.10)' can't be established. ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxx. Are you sure you want to continue connecting (yes/no)?这一步千万别无脑敲yes。正确做法是:通过可靠渠道(比如云控制台、同事告知)确认这个指纹确实属于目标服务器,再输入yes。确认指纹本质上是在防止中间人攻击。之后客户端会把服务器的主机公钥保存在known_hosts文件里,下次连接不会再提示。
输入密码后,你会看到类似Welcome to Ubuntu 22.04这样的欢迎信息,这就说明连接成功了。退出的命令是exit,或者用快捷键Ctrl+D。
4. 密钥对生成与免密登录配置
4.1 生成密钥:算法选择和参数设定
在客户端机器上生成密钥对,用到的是ssh-keygen命令。推荐使用ed25519算法,它比传统的RSA更安全、更高效,生成出的密钥也更短。如果客户端环境比较老,不支持ed25519,退而求其次用RSA 4096位也完全可以。
生成命令:
ssh-keygen -t ed25519 -C "用于生产服务器登录" -f ~/.ssh/id_ed25519-t指定算法,-C是注释,建议写上用途方便日后管理,-f指定密钥保存路径。执行后会提示设置passphrase,也就是私钥口令。强烈建议设置一个passphrase。有人觉得麻烦,选择空口令,这等于把私钥裸放在磁盘上。一旦个人电脑被人拿到,密钥就一起泄露了,服务器随之失守。设个口令,即使私钥文件泄露出去了,对方还需要再解一道锁。
生成完成后,~/.ssh目录下会有两个文件:id_ed25519是私钥,严格自持;id_ed25519.pub是公钥,可以放心分发。私钥的权限必须收紧,检查一下:
ls -l ~/.ssh/id_ed25519权限正常情况下是-rw-------,如果不是,用chmod 600修正。公钥权限是-rw-r--r--即可。
4.2 将公钥安装到服务器
安装公钥最省事的方式是ssh-copy-id工具。它会把本地公钥追加到服务器上对应用户的authorized_keys文件里,一条命令搞定:
ssh-copy-id 用户名@服务器地址执行后需要输入一次密码,之后公钥就被写入了。在没有ssh-copy-id的环境里,也可以手动操作。把公钥内容复制到服务器上指定用户的~/.ssh/authorized_keys文件里,每行一个公钥。注意目录和文件的权限有严格要求:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys权限不对,即使公钥内容正确,服务端也会直接拒绝密钥认证,默默回退到密码认证。这是很多人配置免密登录失败的隐藏原因,后面还会再提到。
公钥追加完毕,验证一下能否直接登录:
ssh 用户名@服务器地址如果配置正确,应该直接进入会话而不问密码。假如还是提示输入密码,说明密钥认证还没生效,继续排查。
4.3 多台服务器和无口令密钥的日常使用
实际工作中,经常手里有好几台服务器,每台都要能免密登录,这就要学习ssh-agent的用法。ssh-agent是一个常驻后台的程序,帮你保管已解锁的私钥。你不用每次连接都输入passphrase,只需在一开始把私钥加进去。
Windows下怎么操作?打开服务,找到OpenSSH Authentication Agent,把它启动并设置为自动启动,然后在PowerShell里执行:
Add-SSHKey -Path ~/.ssh/id_ed25519Mac和Linux下启动agent并添加私钥的命令是:
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519之后再连接服务器,私钥签名操作会由agent自动完成,绝大部分场景下体验和免密一样。我自己管理多台测试机时,会在本地写一个脚本,循环分发公钥,省去重复输入密码的麻烦:
for host in server1 server2 server3; do ssh-copy-id 用户名@$host done5. 安全加固:让SSH经得起试探
5.1 禁用密码登录,封死暴力破解的通道
SSH服务暴露在公网上,每天都会收到大量自动化扫描和暴力破解尝试。日志里如果出现大量Failed password记录,说明你的服务器正在被试探。这时候最有效的措施就是禁用密码登录,只保留密钥认证。
修改sshd_config文件:
sudo vim /etc/ssh/sshd_config找到PasswordAuthentication,改成no,然后检查语法并重载:
sudo sshd -t sudo systemctl reload sshd改完之后,所有依赖密码的登录全部失效,只有持有私钥的人能连进来。这一步做完,暴力破解基本被彻底挡在门外,因为对方连尝试密码的机会都没有了。
千万注意顺序:先在另一个终端确认密钥登录可用,再关密码登录。我就见过有人先关了密码认证,结果自己的密钥还没配好,最后只能去云控制台重启机器救场。稳妥做法是,配置完密钥后不关当前会话,另开一个新终端测试密钥登录,确认通过后再切换PasswordAuthentication为no。
5.2 修改默认端口和限制尝试次数
修改监听端口,可以削减被扫描的概率。编辑sshd_config:
Port 22022然后调整防火墙,放行新端口。Ubuntu上用ufw的话:
sudo ufw allow 22022/tcpCentOS上用firewalld的话:
sudo firewall-cmd --permanent --add-port=22022/tcp sudo firewall-cmd --reload改端口只是障眼法,攻击者只要做全端口扫描照样能找到,但它能让你的日志干净不少。
MaxAuthTries字段限制单次连接最多允许的认证尝试次数,默认6次太高,推荐改成3:
MaxAuthTries 3LoginGraceTime是连接建立后多少秒内必须完成认证,超时自动断,默认120秒太宽松,改成30:
LoginGraceTime 30这两项配合使用,能有效拖慢暴力破解工具的节奏。另外提一句,有条件可以部署fail2ban,它能自动封禁多次尝试失败的IP。但fail2ban是覆盖范围更大的话题,这里就不展开了。
5.3 登录后的日志审阅很有用
安全加固做得再好,也要养成查看日志的习惯。SSH相关的登录日志,Ubuntu这类用journald的系统可以这样查:
journalctl -u ssh -n 50CentOS系列传统路径是/var/log/secure。定期扫一眼日志,能发现异常登录尝试的苗头。记录里出现了非自己操作的登录成功记录,那就是危险信号,立刻处理。我的习惯是每周检查一次,重点看有没有来自陌生IP的Accepted publickey记录。
6. 常见故障与排查思路实录
6.1 连接不上:先分清是哪一端的问题
SSH连接报错,原因可能在客户端,也可能在服务端,还可能卡在中间的网络设备。我的排查顺序是:
先看网络通不通,用ping + telnet组合判断。ping通说明主机可达,telnet 服务器IP 22测试端口是否开放:
telnet 192.0.2.10 22端口通,会返回SSH-2.0-OpenSSH开头的横幅。端口不通,检查服务端sshd有没有在监听,防火墙是不是挡了。如果ping不通,优先怀疑网络和防火墙规则。云服务器用户还要检查安全组规则是否放行了对应端口,这个问题经常被本地防火墙排查忽略。
另一种常见情况是连接超时,而不是拒绝连接。Connection refused和Connection timed out含义完全不同。前者说明目标端口拒绝连接,服务可能没启动或防火墙拒绝;后者说明数据包根本没到达目标,大概率是路由或安全组问题。
6.2 免密登录失效:权限与文件属主
密钥认证配好了,headache的是某天它突然失效,又开始要密码。排查按优先级来:
第一看服务器上authorized_keys权限。前面提过,~/.ssh必须700,~/.ssh/authorized_keys必须600。权限过宽直接会导致认证失败。用命令快速修正:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys第二看这个文件属主是不是当前登录用户。如果authorized_keys的属主是root,而你用普通用户登录,认证同样不通过。改正:
chown 用户名:用户名 ~/.ssh/authorized_keys第三看sshd_config里PubkeyAuthentication是否被意外关掉,或AuthorizedKeysFile路径是否被改过。经验告诉我,90%的免密失效都是前两类权限问题,路径问题很少见但碰到了特别隐蔽。
6.3 主机密钥验证失败的处理
连接时提示Host key verification failed,核心原因是known_hosts里记录的服务器主机公钥和现在服务器给出的不一致。诚实地说,这个报错其实是一种保护机制,提醒你可能连接到了错误的主机,或者服务器系统被重装过、主机密钥被重新生成过。
如果确认服务器确实重装过系统,或者换了新主机,可以这样清除旧记录:
ssh-keygen -R 服务器IP然后重新连接确认指纹即可。如果服务器没重装过,但报了这个错,就要高度警惕了,这有可能是中间人攻击。我的做法是:立即中止连接,通过其他管理通道核验服务器的真实指纹,再决定下一步。
6.4 登录缓慢和断连问题
登录要等好几秒才提示输密码,通常是DNS反向解析超时。默认情况下,sshd对客户端IP做反向DNS解析,内网环境DNS配置不好就会卡在那。解决办法是在sshd_config里加一行:
UseDNS no然后重载服务,立竿见影。这个优化对于家用宽带和经典内网环境尤其有用,实测下来登录速度基本秒回。遇到连接频繁断开的问题,优先检查网络质量和服务端是否设置不活跃超时。稳定性优先的情况下,专注排查Network层面比调SSH参数更有意义。
另外一个小tips:客户端配置~/.ssh/config,可以为不同服务器写别名和连接参数。比如:
Host web HostName 192.0.2.10 User deploy IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 60这样以后只需要ssh web就能登录,ServerAliveInterval 60会让客户端每60秒发一次心跳包,避免长时间无操作被网络设备掐断连接。这个文件还能统一管理不同服务器的用户和密钥,长期使用下来非常舒服。
我个人在实际操作中的体会是,SSH这块知识越早花时间吃透越划算。它牵扯到的加密原理、配置文件细节、权限管理,和Linux系统管理里其他很多内容是贯通的。后面你再碰远程开发、自动化部署、服务器运维,都绕不开这套东西。配置踩坑的时候不要急,把加密流程和权限模型想明白,大部分问题自己就能定位出来。