最近在新服务器上装了宝塔面板,用了几天发现确实很方便,大部分 Linux 系统常用的操作都可以可视化,UI 也简洁直观,比敲命令要方便不少。
但是好景不长,有一天我突然无法通过 SSH 登录我的服务器,一直报错 Permission denied (publickey)
😭 Debug 一通之后,发现宝塔面板的一个迷惑操作 😖
文中所有操作在 Debian 11 和宝塔 7.9.4 版本下运行。
检查客户端 SSH 公钥
既然是 ssh 登录报错,直觉上当然是首先怀疑客户端 ssh 操作出了问题。
因为我配置了 ssh 免密登录,并且关闭了密码登录,所以第一时间想办法检查客户端的公钥 id_ras.pub
是否还能对应上服务器 ~/.ssh/authorized_keys
中的配置。检查完后发现公钥在服务器上的配置没问题。
检查服务器 SSHD 配置
既然客户端的公钥没问题,那我开始怀疑是不是服务器上 sshd 服务的配置文件出了什么幺蛾子。
sshd 作为 Linux 系统最核心最基本的服务之一,基本所有发行版本中,配置文件都是
/etc/ssh/sshd.config
一番检查后发现与 ssh 密钥登录有关的关键配置 PubkeyAuthentication yes
和 RSAAuthentication yes
都没有有问题,包括 root 账号登录配置 PermitRootLogin yes
也没问题。
检查客户端 SSH 登录日志
客户端和服务器的配置都没问题,常规检查已经没办法了,只能查日志了。
查日志之前最好是对 ssh 登录的过程有一个基本的了解,否则日志可能也看不大明白。推荐学习阮一峰先生写的这个 SSH 教程
客户端 ssh 密钥登录的基本流程如下:
- 客户端向服务器发起 SSH 登录的请求。
- 服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。
- 客户端收到服务器发来的数据,使用私钥对数据进行签名,然后再发还给服务器。
- 服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录。
在客户端 ssh 登录的时候加上 -v
参数,即可查看详细的登录日志:
OpenSSH_8.6p1, LibreSSL 2.8.3
debug1: Reading configuration data /Users/asimov/.ssh/config
debug1: /Users/asimov/.ssh/config line 43: Applying options for deskmini-lan
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 21: include /etc/ssh/ssh_config.d/* matched no files
debug1: /etc/ssh/ssh_config line 54: Applying options for *
debug1: Authenticator provider $SSH_SK_PROVIDER did not resolve; disabling
debug1: Connecting to 192.168.1.6 [192.168.1.6] port 22.
debug1: Connection established.
# 此处省略一些不太重要的日志内容。。。。。。
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,sk-ssh-ed25519@openssh.com,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ecdsa-sha2-nistp256@openssh.com,webauthn-sk-ecdsa-sha2-nistp256@openssh.com>
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/asimov/.ssh/id_rsa RSA SHA256:2feuRGdWIrQv7w4bEhPS+qqslDeLuyWP4kX+gFK7uRU
debug1: Authentications that can continue: publickey
debug1: Trying private key: /Users/asimov/.ssh/id_dsa
debug1: Trying private key: /Users/asimov/.ssh/id_ecdsa
debug1: Trying private key: /Users/asimov/.ssh/id_ecdsa_sk
debug1: Trying private key: /Users/asimov/.ssh/id_ed25519
debug1: Trying private key: /Users/asimov/.ssh/id_ed25519_sk
debug1: Trying private key: /Users/asimov/.ssh/id_xmss
debug1: No more authentication methods to try.
root@192.168.1.6: Permission denied (publickey).
从客户端日志上看到,从读取配置,到建立连接,再到发送登录请求,再到接收签名数据都是正常的,但是最后无法获得服务器的授权,而是得到 root@192.168.1.6: Permission denied (publickey)
,登录失败。
正常情况下在 SSH2_MSG_SERVICE_ACCEPT received
之后应该是下面这样
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/asimov/.ssh/id_rsa RSA SHA256:2feuRGdWIrQv7w4bEhPS+qqslDeLuyWP4kX+gFK7uRU
debug1: Server accepts key: /Users/asimov/.ssh/id_rsa RSA SHA256:2feuRGdWIrQv7w4bEhPS+qqslDeLuyWP4kX+gFK7uRU
debug1: Authentication succeeded (publickey).
Authenticated to 192.168.1.6 ([192.168.1.6]:22).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: pledge: filesystem full
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
debug1: client_input_hostkeys: searching /Users/asimov/.ssh/known_hosts for 192.168.1.6 / (none)
debug1: client_input_hostkeys: searching /Users/asimov/.ssh/known_hosts2 for 192.168.1.6 / (none)
debug1: client_input_hostkeys: host key found matching a different name/address, skipping UserKnownHostsFile update
debug1: Remote: /root/.ssh/authorized_keys:2: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding
debug1: Remote: /root/.ssh/authorized_keys:2: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding
debug1: Sending environment.
debug1: channel 0: setting env LANG = "zh_CN.UTF-8"
根据日志分析,基本可以判断,问题大概出在服务器验证身份的过程中。
检查服务器 SSHD 服务日志
前面已经检查过客户端公钥、服务器配置以及客户端登录过程,都没问题,那就只能再查查服务器上 sshd 的日志看看了。
/var/log/auth.log
这个文件保存着所有服务器上 ssh 登录日志,在客户端上再尝试登录一次,然后拉到最后看到报错:
debian sshd[2178]: rexec line 126: Deprecated option RSAAuthentication
debian sshd[2178]: reprocess config line 126: Deprecated option RSAAuthentication
debian sshd[2178]: Authentication refused: bad ownership or modes for file /root/.ssh/authorized_keys
debian sshd[2178]: Connection closed by authenticating user root 192.168.1.4 port 56782 [preauth]
关键信息是这一句 Authentication refused: bad ownership or modes for file /root/.ssh/authorized_keys
,意思是 authorized_keys
的权限不对,拒绝登录。
Linux 系统为了保证 ssh 登录的安全,对每个用户的 .ssh 目录及里面的文件有一些权限上的要求,推荐的权限配置为:.ssh 目录对用户读/写/执行,并且不能被其他所有用户访问,目录中的文件对用户读/写,并且不能被其他所有用户访问。
也就是.ssh 700
,.ssh/* 600
,且~/.ssh
目录及其文件拥有者必须与当前登录用户一致。
于是我查看 .ssh 的权限如下:
# ~/.ssh 目录权限
drwx------ 2 www www 4096 Sep 21 10:26 .ssh
# ~/.ssh 中文件的权限
-rw-r--r-- 1 www www 565 Sep 21 10:26 id_rsa.pub
-rw------- 1 www www 2602 Sep 21 10:26 id_rsa
-rw------- 1 www www 1308 Sep 21 12:26 authorized_keys
# /root 目录权限
dr-xr-x--- 7 www www 4096 Sep 21 13:02 root
看得出来,~/.ssh
和里面的文件权限没问题,但是我登录的是 root 用户,目录和文件的所有者却是 www,而且整个 /root 文件夹的所有者都变成了 www,这应该就是问题所在了。
于是我用 chown -R root:root /root/
将整个 /root 文件夹的拥有者改了回来,再次尝试 ssh 登录,成功登录。
至此 ssh 登录认证失败的问题解决了,但是更大的问题来了:是谁改了 /root 的权限 ?!😖
分析 www 用户行为
上面看到 /root 的拥有者被改成了 www 用户,而这个用户我记得是宝塔面板创建的,看名字应该是用来执行 web 服务的。
于是我开始检查在宝塔上创建的 web 服务。
我一共在宝塔上部署了 4 web 个服务:1 个 PHP项目
AriaNg,2 个 Go 项目
Alist 和 Frp,1 个 其他项目
Aria2
一番检查后果然发现,宝塔进程本身虽然是 root 用户持有的,但是宝塔中的 Go 项目
和 其他服务
默认都是用 www
用户执行的
从进程中也能看到 aria2 服务进程的持有者是 www 用户
root@debian:~# ps -ef | grep aria2
www 5034 1 0 13:03 ? 00:00:00 /opt/aria2/aria2c --conf-path=/opt/aria2/aria2.conf
root 5247 1325 0 13:37 pts/1 00:00:00 grep aria2
于是我一个一个的开关这几个项目(还好不多😂)反复对比前后 /root 目录的权限变化。
最后锁定到 其他服务
中的这个 Aria2 项目,只要这个项目一启动,整个 /root 就会立刻被改成 www 用户拥有。
宝塔面板的迷惑操作
于是我又花了很多时间检查 aria2c,确认无论是二进制文件还是运行的配置文件,确实没有一个地方有涉及到操作 /root 的权限,甚至没有地方用到 /root 目录,所有操作都在 /opt/aria2
目录下进行。
进行到到这儿,我懵逼了一阵,不知道下一步该从何下手 😖
最后我灵机一动,想会不会是宝塔面板在启动项目的时候做了什么奇怪的操作。
于是我找到了宝塔面板中 其他项目
这个功能下对应的目录 /www/server/other_project
,这里面有一个 log 文件夹,一个 scripts 文件夹
root@debian:/www/server/other_project# pwd
/www/server/other_project
root@debian:/www/server/other_project# ls
logs scripts
进入 scripts 目录一看,果然有个 Aria2.sh
文件,打开一看,好家伙:
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
cd /root
nohup /opt/aria2/aria2c --conf-path=/opt/aria2/aria2.conf 2>&1 >> /www/server/other_project/logs/Aria2.log &
echo $! > /var/tmp/other_project/Aria2.pid
脚本很简单,就是启动在宝塔面板里配置的 aria2 项目,但是这个 cd /root
让人有些迷惑了,为什么要进入到 /root 目录下启动项目?
更让人费解的是,这个 cd /root
操作虽然迷惑,可并没有操作 /root 的权限,而且根据上面的分析,执行这个脚本,持有这个进程的用户是 www,这个用户并没有修改 /root 目录 owner 的权限。
那究竟宝塔面板究竟是 为何 以及 如何 修改 /root owner 权限的呢?
我把这个项目换用 Go 项目
功能部署就没有上面那些奇怪的问题了。
分析到这儿基本就结束了,再往下就要扒宝塔的代码了,这工程量就有点大了,我打算去官方论坛问问,问出结果了再来更新本文。
更新:在宝塔 官方论坛 和 V2EX 都没问出个所以然,算了😐
评论