美国CN2 VPS的“CN2”三个字母容易给人一种错觉,仿佛线路质量本身就意味着安全。实际上CN2优化的是中国电信用户到美国机房之间的传输路径,它跟服务器上跑着什么服务、开着哪些端口、谁能登录进来,完全是两回事。一台刚交付的VPS,默认状态下SSH端口对全网开放,密码登录启用,root账户可直接远程连接。放在公网上,扫描器发现它到开始尝试暴力破解,中间可能只隔几分钟。
安全加固的合理顺序不是把能改的都改一遍,而是先确保自己不会把自己锁在外面,再逐步收紧入口。SSH密钥认证和防火墙端口管控是第一步的两件事:前者解决“谁能进来”,后者解决“从哪能进来”。两者都做完并且验证通过之后,再考虑Fail2ban、改端口这些辅助手段,才不会在配置出错时失去恢复能力。
动手之前:先看清服务器上在监听什么
在改任何配置之前,先以root身份登录VPS,执行一条命令看清当前暴露面:
ss -lntup
输出中LISTEN状态的条目就是当前正在监听的端口。重点关注三类:0.0.0.0:22或:::22是SSH,这是你必须保留的管理入口;:80和:443是Web服务,如果业务需要对外提供访问,这两个要放行;其他端口,尤其是3306(MySQL)、6379(Redis)、5432(PostgreSQL)这类数据库端口,如果监听在0.0.0.0上,意味着任何知道IP的人都能尝试连接。数据库不应该直接暴露在公网,后续防火墙配置时要将它们关掉或限制来源。
把这份清单记下来,后面每放行一个端口,都要能对应到清单上的一个具体用途。说不清用途的监听端口,先不要放行。
第一步:SSH密钥认证,但别急着关密码
密钥认证的核心逻辑很简单:用一对密钥代替密码。私钥留在你本地电脑上,公钥放到服务器的~/.ssh/authorized_keys文件里。登录时服务器发一个挑战,只有持有私钥的一方能正确应答,密码猜不中、也偷不走。
在本地生成密钥对。 如果你用的是Windows 10/11,PowerShell里直接有OpenSSH客户端。推荐用Ed25519算法而不是RSA,密钥更短、签名更快,安全性不低于3072位RSA。执行:
ssh-keygen -t ed25519 -a 100 -C "us-vps-admin"
-a 100把密钥派生轮数提高到100,如果有人拿到了你的私钥文件试图暴力猜解密口令,计算成本会显著上升。-C后面的注释只是标签,方便你日后辨认这把钥匙对应哪台机器。执行过程中会提示保存路径和设置私钥口令(passphrase),口令建议设置,它相当于私钥文件的第二道锁。
把公钥传到服务器。 最简单的方式是用ssh-copy-id:
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@你的服务器IP
如果本地没有这个命令(Windows PowerShell可能没有),手动操作也不复杂:先登录服务器,创建.ssh目录并设置权限:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
然后把本地公钥文件的内容(id_ed25519.pub,注意是.pub结尾的那个)追加到服务器的authorized_keys:
echo "你的公钥内容" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
关键一步:先测试,再改配置。 打开一个新的终端窗口,用密钥登录:
ssh -i ~/.ssh/id_ed25519 root@你的服务器IP
如果能不输密码(或只输私钥口令)就进去,说明密钥认证已经生效。不要关掉当前那个用密码登录的旧会话,它是你的退路。如果新窗口登录失败,回到旧会话排查,而不是继续往下改。
确认密钥可用之后,再禁用密码登录。 编辑SSH配置文件:
sudo vim /etc/ssh/sshd_config
找到并修改以下几项。如果某项前面有#,去掉注释:
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin prohibit-password
PermitRootLogin prohibit-password的意思是root仍然可以通过密钥登录,但不能用密码。如果你已经创建了普通用户并且配置了sudo权限,更稳妥的做法是设为no,完全禁止root直接SSH,日常用普通用户登录后sudo提权。但对刚上手的VPS来说,prohibit-password是一个可接受的过渡状态。
保存后重启SSH服务:
sudo systemctl restart sshd
再次强调:重启之前确认新窗口的密钥登录是通的。配置写错加上旧会话关闭,就意味着只能去云控制台用VNC或救援模式恢复,麻烦程度远超省下的那几分钟验证时间。
第二步:防火墙端口管控,先放行再启用
防火墙的常见翻车场景是:配置了默认拒绝入站,忘了放行SSH端口,然后执行了启用命令。下一秒,当前SSH连接还在(已建立的连接通常不受新规则影响),但一旦断开就再也连不上了。
先确认防火墙工具。 Ubuntu/Debian系默认用UFW,RHEL/CentOS/Alma/Rocky系用firewalld。如果不确定:
which ufw firewalld
有输出的是已安装的,选一个用。不要在同一个系统上同时维护UFW和firewalld,规则冲突时排查成本很高。
UFW的操作路径。 在启用防火墙之前,先把SSH端口放行:
sudo ufw allow 22/tcp
如果你已经按后文建议修改了SSH端口,把22换成实际端口。然后放行业务端口(以Web服务为例):
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
确认规则列表:
sudo ufw status verbose
看到22/tcp ALLOW、80/tcp ALLOW、443/tcp ALLOW之后,再设置默认策略并启用:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
系统会提示可能中断现有SSH连接,输入y确认。由于SSH规则已经提前放行,当前连接不会断,新连接也能正常建立。
firewalld的操作路径。 先查看当前活动区域和已放行内容:
sudo firewall-cmd --list-all
然后按端口放行,注意--permanent参数让规则重启后仍然有效:
sudo firewall-cmd --permanent --add-port=22/tcp
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
--reload之后规则才真正生效。如果在--list-ports里看不到刚添加的端口,说明--permanent漏掉了或者reload没有执行。
一个容易被忽略的细节:云控制台的安全组。 如果VPS提供商的控制台里还有一层“安全组”或“防火墙”配置,它和主机防火墙是串联关系。两层都放行,端口才通;任何一层拒绝,外部都访问不了。配置完成后从外部测试端口连通性,如果主机防火墙显示已放行但外部连不上,去控制台检查安全组规则。
第三步:把不需要的端口彻底关掉
防火墙默认拒绝入站之后,没有被显式放行的端口自然就不可达了。但有一种情况需要单独处理:某些服务监听在0.0.0.0上,而你在防火墙里“忘记”了它们,它们实际上就被默认策略挡住了。这听起来没问题,但如果你日后为了排查某个问题临时放行了一个端口,忘记撤销,那个端口就重新暴露了。
更干净的做法是从服务本身入手,让数据库这类服务只监听本地回环地址。以MySQL/MariaDB为例,编辑配置文件(通常位于/etc/mysql/mysql.conf.d/mysqld.cnf或/etc/my.cnf),找到bind-address,改为:
bind-address = 127.0.0.1
重启数据库服务后,ss -lntup的输出中MySQL那一行会从0.0.0.0:3306变成127.0.0.1:3306。这意味着即使防火墙规则出了纰漏,外部也无法直接连接数据库端口。Redis同理,在redis.conf中确保bind 127.0.0.1且protected-mode yes。
做完这一步,回到ss -lntup的输出,对照动手前记下的清单:每个仍然监听在0.0.0.0上的端口,要么是业务必需的(22、80、443),要么是应该被关掉的。没有第三类。
验证:从外部看,而不是从内部
配置改完之后,在服务器内部看ufw status或firewall-cmd --list-ports显示一切正常,不代表外部真的按预期工作。验证要站在外部视角。
从你本地电脑(不是服务器上)测试SSH端口是否可达:
nc -zv 你的服务器IP 22
如果返回succeeded,SSH入口是通的。再测一个不应该开放的端口,比如3306:
nc -zv 你的服务器IP 3306
返回Connection refused或超时,说明防火墙正在按预期拦截。如果返回succeeded,说明防火墙规则没有覆盖到这个端口,或者数据库仍然监听在公网地址上,回到上一步检查。
最后,用密钥登录一次,确认PasswordAuthentication no确实生效。尝试强制用密码登录:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no root@你的服务器IP
预期结果是Permission denied (publickey)。如果仍然提示输入密码并且能登进去,说明sshd_config的修改没有生效,检查是否有sshd_config.d/目录下的覆盖文件,以及是否执行了systemctl restart sshd。
相关内容
