Perl 运行 作为用户 "apache" 无法加载 DBD::mysql 而其他用户可以
Perl running as user "apache" cannot load DBD::mysql while other users can
我管理的网站有一些 CGI 脚本 运行 脚本的形式:
#!/usr/bin/env bash
perl my-script.pl
my-script.pl 使用 DBD::mysql.
use DBD::mysql;
我的脚本使用了很多 CPAN 模块,我不想污染 Linux 发行版安装的 "system" Perl (5.16)。我们的安全策略要求 httpd 运行 作为用户 "apache" 并且 apache 在我们的服务器上没有主目录,所以我的解决方案是在我有权访问的不同主目录下安装 Perl 和 perlbrew。然后虚拟主机的 Apache 配置文件设置一些环境变量来访问它。
SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib # this may not be needed
这足以加载大多数模块。例如,apache 可以 运行:
perl -mDateTime -e 'print $DateTime::VERSION' # prints "1.52"
但是如果 apache 尝试:
perl -mDBD::mysql -e 'print $DBD::mysql::VERSION'
它吐了:
Can't locate loadable object for module DBD::mysql in @INC (@INC contains: /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2/x86_64-linux /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2) at -e line 0.
Compilation failed in require.
BEGIN failed--compilation aborted.
错误消息 "Can't locate ..." 具有误导性。我确认 DBD::mysql 可从@INC 的第三条路径获得:
$ find ~user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 -name mysql -ls
16540213 4 drwxr-x--- 2 user1 user1 4096 Apr 21 12:51 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql
16540211 4 drwxr-xr-x 2 user1 user1 4096 Apr 21 11:26 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux/DBD/mysql
此外,user1 可以毫无问题地加载 DBD::mysql:
perl -mDBD::mysql -e 'print $DBD::mysql::VERSION' # prints 4.050
因此,我怀疑上面的报错信息应该是"Can't load libmysqlclient.so ..."
libmysqlclient.so 位于 /usr/lib64/mysql/
ls -l /usr/lib64/mysql/
total 3076
lrwxrwxrwx 1 root root 17 Apr 16 11:59 libmysqlclient_r.so -> libmysqlclient.so
lrwxrwxrwx 1 root root 20 Apr 16 11:59 libmysqlclient.so -> libmysqlclient.so.18
lrwxrwxrwx 1 root root 24 Apr 16 11:57 libmysqlclient.so.18 -> libmysqlclient.so.18.0.0
-rwxr-xr-x 1 root root 3135664 Aug 18 2019 libmysqlclient.so.18.0.0
-rwxr-xr-x 1 root root 6758 Aug 18 2019 mysql_config
drwxr-xr-x. 2 root root 4096 Apr 16 11:57 plugin
如果 user1 运行s perl -V,链接器和动态链接部分显示如下:
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib /lib64 /usr/lib64 /usr/local/lib64
libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.17.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.17'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
如果我 运行 使用与 apache 相同的 perl,它会产生相同的结果:
sudo -u apache bash
PATH=~user1/perl5/perlbrew/perls/perl-5.30.2/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin
perl -V
...
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib /lib64 /usr/lib64 /usr/local/lib64
libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.17.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.17'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
为什么 user1 perl 可以加载 DBD::mysql 但 apache 不能,即使两者都 运行 使用相同的 @INC 路径使用相同的 Perl 并且它们的动态库加载路径看起来相同?有谁知道我还能做些什么来弄清楚这个问题?
对于初学者,你永远不应该这样做
SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib
如果您使用 .../perl-5.30.2/bin/perl
,它将知道在 .../perl-5.30.2/lib
中查找,而这是唯一应该在该目录中查找的 perl
。
理想情况下,您也不会执行以下操作:
SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
脚本的 shebang 应该指向它要使用的 perl
(经过测试并已知可以使用的那个)。
换句话说,在 bash
脚本中使用以下内容:
./my-script.pl
并在 my-script.pl
中使用以下 shebang:
#!/export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin/perl
你目前所做的并不可怕,但如果你尝试升级某些东西,可能会咬你。
最后,perl
因为权限问题找不到模块。假设 apache
用户不是 user1
组的成员,您表明 apache
用户无法访问 lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql
(并且它可能无法访问其他相关的文件)。
修复:
chmod go+X \
/export \
/export/home \
/export/home/user1\
/export/home/user1/perl5 \
/export/home/user1/perl5/perlbrew \
/export/home/user1/perl5/perlbrew/perls
chmod -R go+rX /export/home/user1/perl5/perlbrew/perls/perl-5.30.2
我管理的网站有一些 CGI 脚本 运行 脚本的形式:
#!/usr/bin/env bash
perl my-script.pl
my-script.pl 使用 DBD::mysql.
use DBD::mysql;
我的脚本使用了很多 CPAN 模块,我不想污染 Linux 发行版安装的 "system" Perl (5.16)。我们的安全策略要求 httpd 运行 作为用户 "apache" 并且 apache 在我们的服务器上没有主目录,所以我的解决方案是在我有权访问的不同主目录下安装 Perl 和 perlbrew。然后虚拟主机的 Apache 配置文件设置一些环境变量来访问它。
SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib # this may not be needed
这足以加载大多数模块。例如,apache 可以 运行:
perl -mDateTime -e 'print $DateTime::VERSION' # prints "1.52"
但是如果 apache 尝试:
perl -mDBD::mysql -e 'print $DBD::mysql::VERSION'
它吐了:
Can't locate loadable object for module DBD::mysql in @INC (@INC contains: /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2/x86_64-linux /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/5.30.2) at -e line 0.
Compilation failed in require.
BEGIN failed--compilation aborted.
错误消息 "Can't locate ..." 具有误导性。我确认 DBD::mysql 可从@INC 的第三条路径获得:
$ find ~user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2 -name mysql -ls
16540213 4 drwxr-x--- 2 user1 user1 4096 Apr 21 12:51 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql
16540211 4 drwxr-xr-x 2 user1 user1 4096 Apr 21 11:26 /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/x86_64-linux/DBD/mysql
此外,user1 可以毫无问题地加载 DBD::mysql:
perl -mDBD::mysql -e 'print $DBD::mysql::VERSION' # prints 4.050
因此,我怀疑上面的报错信息应该是"Can't load libmysqlclient.so ..."
libmysqlclient.so 位于 /usr/lib64/mysql/
ls -l /usr/lib64/mysql/
total 3076
lrwxrwxrwx 1 root root 17 Apr 16 11:59 libmysqlclient_r.so -> libmysqlclient.so
lrwxrwxrwx 1 root root 20 Apr 16 11:59 libmysqlclient.so -> libmysqlclient.so.18
lrwxrwxrwx 1 root root 24 Apr 16 11:57 libmysqlclient.so.18 -> libmysqlclient.so.18.0.0
-rwxr-xr-x 1 root root 3135664 Aug 18 2019 libmysqlclient.so.18.0.0
-rwxr-xr-x 1 root root 6758 Aug 18 2019 mysql_config
drwxr-xr-x. 2 root root 4096 Apr 16 11:57 plugin
如果 user1 运行s perl -V,链接器和动态链接部分显示如下:
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib /lib64 /usr/lib64 /usr/local/lib64
libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.17.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.17'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
如果我 运行 使用与 apache 相同的 perl,它会产生相同的结果:
sudo -u apache bash
PATH=~user1/perl5/perlbrew/perls/perl-5.30.2/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin
perl -V
...
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib /lib/../lib64 /usr/lib/../lib64 /lib /lib64 /usr/lib64 /usr/local/lib64
libs=-lpthread -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.17.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.17'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'
为什么 user1 perl 可以加载 DBD::mysql 但 apache 不能,即使两者都 运行 使用相同的 @INC 路径使用相同的 Perl 并且它们的动态库加载路径看起来相同?有谁知道我还能做些什么来弄清楚这个问题?
对于初学者,你永远不应该这样做
SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib
如果您使用 .../perl-5.30.2/bin/perl
,它将知道在 .../perl-5.30.2/lib
中查找,而这是唯一应该在该目录中查找的 perl
。
理想情况下,您也不会执行以下操作:
SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
脚本的 shebang 应该指向它要使用的 perl
(经过测试并已知可以使用的那个)。
换句话说,在 bash
脚本中使用以下内容:
./my-script.pl
并在 my-script.pl
中使用以下 shebang:
#!/export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin/perl
你目前所做的并不可怕,但如果你尝试升级某些东西,可能会咬你。
最后,perl
因为权限问题找不到模块。假设 apache
用户不是 user1
组的成员,您表明 apache
用户无法访问 lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql
(并且它可能无法访问其他相关的文件)。
修复:
chmod go+X \
/export \
/export/home \
/export/home/user1\
/export/home/user1/perl5 \
/export/home/user1/perl5/perlbrew \
/export/home/user1/perl5/perlbrew/perls
chmod -R go+rX /export/home/user1/perl5/perlbrew/perls/perl-5.30.2