服务器被挖矿之后
最近阿里云和腾讯云上的两台服务器相继被植入了挖矿程序,我不得不重装系统,一部分数据没能留下来。趁着这次重装,把加固的过程记一下。
个人服务器我跑了很多年,一直只当成兴趣项目的部署环境,上面没什么要紧数据。所以早些年发现有挖矿程序在跑,只要不影响我自己的网站,我也就没太当回事。
最近开始做小程序,情况不一样了——小程序碰的是真实的用户数据,这个丢不起。
重装完的第一件事,是禁用密码登录、改用 SSH 密钥。服务器开在公网上,SSH 每周会收到几千次爆破尝试;只认密钥之后,这些尝试就都是白费力气了。
客户端:~/.ssh/config
Host myserver
HostName <服务器 IP 或域名>
User root
IdentityFile ~/.ssh/<私钥文件>
IdentitiesOnly yes配好之后 ssh myserver 就能连,不用每次敲 IP 和 -i。
有两个地方容易踩:
必须用别名连,不能用 IP。 ssh root@<IP> 匹配不到 Host myserver 这条规则,整段配置不生效,ssh 会退回去试默认的 ~/.ssh/id_rsa、id_ed25519——服务器不认,于是报 Permission denied (publickey)。我在这里卡过一次。
IdentitiesOnly yes 是必要的。 不加的话 ssh 会把 ~/.ssh 下所有密钥挨个试一遍。密钥多了会超过服务器的 MaxAuthTries(默认 6),还没轮到正确那把就被踢掉了。
私钥
权限 600 ← 必须,宽于此 ssh 会直接拒绝使用
类型 RSA 2048 或 ED25519(新生成推荐后者)权限不对时的报错很明确:
Permissions 0644 for '...' are too open.修:chmod 600 ~/.ssh/<私钥文件>
服务器端:authorized_keys
公钥放在登录用户的家目录下:
~/.ssh/authorized_keys 权限 600
~/.ssh/ 权限 700权限过宽 sshd 会静默忽略这个文件——不报错,只是认证失败,排查时很容易被误导。查已授权的公钥:
ssh-keygen -l -f ~/.ssh/authorized_keys会列出每把钥匙的指纹和注释,方便核对哪把是哪台机器的,也方便清理不再用的。
sshd 配置
关键的几项,改 /etc/ssh/sshd_config:
PasswordAuthentication no # 关掉密码登录,最重要的一条
PubkeyAuthentication yes
PermitEmptyPasswords no
KbdInteractiveAuthentication no改完先验证语法再重启,避免把自己关在门外:
sshd -t # 语法检查
systemctl reload sshd验证的时候不要关掉当前的 SSH 连接。 另开一个终端试连,确认能进去了再退出原来那个。万一配错了,原连接还在,还有机会改回来。
看生效值用 sshd -T,它输出的是实际运行值,而不是配置文件的字面内容——有些项在别处被覆盖过,只看文件会看走眼:
sshd -T | grep -iE 'passwordauth|pubkeyauth|permitroot|port'把管理面板从公网上撤下来
密码登录关掉之后,SSH 这道门算是守住了。但我还开着一个 1Panel,它的端口是直接对 0.0.0.0/0 放行的。
这件事比想象中严重。面板不是一个普通的网页——它自带文件管理器和 Web 终端,等于把 root 权限包装成了一个网页表单。SSH 那边我费劲关掉密码登录、只认密钥,而面板这边只要一个用户名密码就能进,还是从任何 IP 都能敲。真出了认证绕过的漏洞,前面做的都白做。
挖矿程序进来的路子,多半就是这一类。
先搞清楚到底开了哪些门
云厂商的安全组规则是一份,服务器上实际在监听的端口是另一份,这两份经常对不上。先各查一遍:
# 服务器上实际在监听的端口
ss -tln | awk 'NR>1 {split($4,a,":"); print a[length(a)]}' | sort -un然后拿这个结果去比对安全组的放行列表。我比出来两条空规则——安全组放行了,但服务器上根本没有服务在听。一条是很久之前装 Gitea 留下的,容器早删了,规则忘了收;另一条我已经完全想不起来是干什么的。
空规则当下不造成危害,风险在将来:哪天你随手起一个服务恰好用了那个端口,它会立刻对全网可见,而你不会察觉。顺手删掉是零成本的。
用 SSH 隧道代替公网放行
面板不需要随时可达。平时用不到,用到的时候现开一条隧道就行:
ssh -N -L <面板端口>:127.0.0.1:<面板端口> myserver-N 表示只建隧道、不执行命令,所以这个终端会挂住不返回,这是正常的。保持它开着,浏览器访问 http://localhost:<面板端口>,流量从本地经 SSH 加密通道到服务器,面板对公网彻底隐身。
做成别名会顺手很多:
alias panel='ssh -N -L <面板端口>:127.0.0.1:<面板端口> myserver'两个坑
先验证隧道,再关安全组。 顺序反了,万一隧道有问题,你就同时失去了两条进面板的路。我的做法是:隧道建好、浏览器确认能正常登录,然后才去云控制台删规则。
面板可能有「安全入口」。 1Panel 支持配置一个入口路径,只有带上这段路径才能访问。我直接开 http://localhost:<端口> 时看到的是:
Access Temporarily Unavailable
The current environment has enabled secure login access.当时以为隧道没通,其实是通的,只是路径不对。查自己的入口:
1pctl user-info想到这里其实要说一句:这个提示页本身是个好设计。哪怕端口暴露在公网,扫描器扫到根路径也只会看到这句话,看不到登录框,也就无从下手。但它终究是靠”别人不知道路径”生效的一层混淆,不是真正的认证。
