一段往事
几年前我第一次买服务器,商家发来一封邮件,里面是一串东西:
1 | IP: 47.88.xx.xx |
我老老实实地打开终端,敲下:
1 | ssh root@47.88.xx.xx |
输入密码,连上了。那一刻觉得自己特别厉害——看,我会用服务器了。
但这种「厉害」只持续了三天。第四天我查看日志,发现有人从莫斯科、纽约、新加坡轮番尝试登录我的服务器。几十个 IP、几千次尝试。我的密码虽然不算弱,但那一刻的寒颤是真实的:互联网上,有人每分钟都在敲你的门。
那个密码后来被我换成了密钥登录,从此再没用过密码连服务器。
在服务器运维的世界里,SSH 是你最常用的工具,没有之一。但这把工具比你想象的要锋利得多——很多人只用到了它 20% 的功能。这篇文章,我们来把剩下那 80% 也翻一翻。
为什么密钥比密码安全得多?
密码的问题是:它是一串你记住的字符,长度有限、可被猜测、可能被泄露。而且每次连接都要输一遍,于是很多人用简单密码或重复密码。
SSH 密钥对是一对文件——公钥和私钥。它的工作原理是非对称加密:
- 你把公钥放到服务器上(
~/.ssh/authorized_keys) - 连接时,服务器用公钥加密一个随机数发给你
- 你的 SSH 客户端用私钥解密,把结果返回给服务器
- 服务器验证正确,放行
私钥永远不会在网络上传送。就算攻击者截获了整个连接过程,也无法推导出你的私钥。这是根本性的安全提升。
一句话:密码是可猜测的共享秘密,密钥是不可猜测的数学证明。
设置密钥登录
生成密钥对(如果还没有):
1 | ssh-keygen -t ed25519 -C "your-email@example.com" |
这里我推荐 ed25519 而不是传统的 rsa。原因:Ed25519 更短(更易复制粘贴)、更快、安全性不输 4096 位 RSA。几乎所有的 SSH 实现现在都支持它。如果你用的是非常老的系统(OpenSSH < 6.5),才需要用 rsa -b 4096。
然后上传公钥到服务器:
1 | ssh-copy-id user@your-server-ip |
这条命令会自动把公钥追加到服务器的 ~/.ssh/authorized_keys 里。如果没有 ssh-copy-id,手动做也一样:
1 | cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" |
完成后测试:ssh user@server 如果直接登录成功不再问密码,就对了。
最后一步也是很多人忘记的一步——禁用密码登录:
1 | # 在服务器上执行 |
这样做之后,只有持有私钥的人才能登录。没有密钥的攻击者连试探密码的机会都没有。
⚠️ 注意:禁用密码前请确保你的密钥登录已经正常工作了。开一个新终端测试一下,确认能连上再执行。如果你把自己锁在外面,唯一的办法是去服务器管理后台用 VNC 或控制台恢复。
SSH Config:你的连接管理器
当你要管理三五台服务器时,很多人还在这么做:
1 | ssh root@47.88.17.196 -p 2222 |
每次都要输 IP、端口、用户名、密钥路径——烦不烦?SSH config 就是解决这个问题的。
创建或编辑 ~/.ssh/config(这个文件没有后缀,就叫 config):
1 | Host blog |
然后你就可以这样连接了:
1 | ssh blog # 代替 ssh root@47.88.17.196 |
Host * 那一行是全局配置——对所有主机生效。ServerAliveInterval 60 的意思是每 60 秒发一个保活包,防止连接被防火墙切断。对于需要长时间保持的会话(比如 tmux 里跑长时间任务),这个配置是救命级别的。
SSH config 的匹配规则是从上到下,第一条匹配的生效。所以把更具体的配置放在前面,通用的 Host * 放在最后。
关于 Host 名称,它可以是任意别名——不一定非得是域名。你设 Host my-server,然后 ssh my-server 就能连。这就是别名的魔力。
SSH 隧道:把远程服务拉到本地
这是 SSH 最酷的功能之一,但也是很多人不知道的功能。
本地端口转发:把远程服务器上的某个端口映射到本地。
举个例子:你的开发数据库在远程服务器上,只监听 127.0.0.1:3306,不对外暴露。你想在本地用 Navicat 或 DataGrip 连上去看看。
1 | ssh -L 3307:127.0.0.1:3306 user@server |
这条命令的意思是:把本地的 3307 端口映射到服务器上的 127.0.0.1:3306。然后你本地连 127.0.0.1:3307 就相当于连到了远程的数据库。
语法拆解:-L 本地端口:目标地址:目标端口 跳板。
这里的目标地址是从 SSH 服务器看的。所以 127.0.0.1:3306 指的是 SSH 服务器本机的 3306 端口。如果你想通过 SSH 服务器访问它内网的另一个机器,就写那个内网 IP。
真实场景:我调试博客时,需要在本地预览 Hexo。我的博客在服务器上,我可以:
1 | ssh -L 4000:127.0.0.1:4000 root@xijun.qd.je |
反向隧道(远程端口转发):把本地的端口暴露到远程。
假设你在家里开发,想临时让朋友从外网访问你本地的某个 web 服务。如果你有一台公网服务器:
1 | ssh -R 8080:127.0.0.1:3000 user@public-server |
这会在公网服务器上开一个 8080 端口,所有访问这个端口的流量都会被转发到你本地的 3000 端口。你朋友访问 http://public-server:8080 就能看到你本地的页面了。
SCP 和 SFTP:远程文件操作
虽然很多人在用 FTP,但在有 SSH 的情况下,真的没必要。
上传文件:
1 | scp /path/to/local/file user@server:/path/to/remote/ |
下载文件:
1 | scp user@server:/path/to/remote/file /path/to/local/ |
在远程和本地之间传,scp 是最直接的。但如果你需要交互式浏览、删除、重命名之类的操作,用 sftp:
1 | sftp user@server |
很多人装了 FileZilla 之类的 GUI 工具来做 SFTP,但其实终端里的 sftp 就够了——而且更快。
日常小技巧
保持连接不断
如果你经常 SSH 一段时间不动就被断开,在 ~/.ssh/config 里加上:
1 | Host * |
跳板机(ProxyJump)
要从公网跳到内网服务器:
1 | ssh -J user@public-gateway user@internal-server |
或者在 config 里配置(如上文所示),用 ProxyJump 指定跳板机。这在管理内网集群时特别好用——一次配置,之后直接 ssh internal。
复用连接(多窗口不重复登录)
在 ~/.ssh/config 加:
1 | Host * |
这样你第一个 SSH 窗口登录后,后续同主机的连接会复用已有的连接通道,不用再次认证。开多个终端操作同一台服务器时,秒开。
别忘了创建控制目录:
1 | mkdir -p ~/.ssh/control |
快速复制公钥
1 | # 输出到终端,方便复制 |
查看登录日志
想知道有没有人尝试登录你的服务器?
1 | # 查看成功的登录 |
如果你发现大量失败的登录尝试,那就是有人在暴力破解你的 SSH。你做了前面说的「禁用密码登录」就不怕了——因为他们根本试不了密码。
SSH 的安全基线
最后,给你一个 SSH 加固清单,挨个检查一遍:
- 禁用密码登录(
PasswordAuthentication no) - 使用 Ed25519 密钥(或用 RSA 4096)
- 禁止 root 直接登录(
PermitRootLogin no,用普通用户 + sudo) - 更换默认端口(
Port 22222之类,可减少 99% 的扫描日志) - 安装 Fail2ban,连续失败自动封 IP
- 公钥加密码短语(passphrase),即使私钥泄露也不能直接用
- 定期审计
~/.ssh/authorized_keys,清理不再需要的公钥
其中前三条是最低要求。做了这三条,你的 SSH 就已经比互联网上 90% 的服务器安全了。
更换端口这招虽然「安全通过模糊化」不算是真正的安全措施,但实用性很强。默认 22 端口每天被扫描上千次,换成高位端口后,扫描日志直接降到个位数。和禁用密码登录配合,效果极佳。
写在最后
回到开头那段往事。那台服务器后来被我重装了无数次系统,从 CentOS 换到 Ubuntu 再换到 Debian,但有一个习惯再也没变过:拿到新服务器的第一件事,就是配置密钥登录、禁用密码。
SSH 是你和服务器之间的那扇门。把门锁好,关掉猫眼,配上监控——然后你才能真正安心地把后背交给这台机器。
你每天在终端里敲的 ssh,远不止「能连上就行」那么简单。它的设计之美在于:一个 1985 年诞生的协议,到 2026 年的今天仍然是远程管理的黄金标准——不是因为没人想替代它,而是因为它实在太难超越了。
用好 SSH,就是善待每一个和你服务器相处的日夜。