mysqlperldbd

Perl running as user "apache" cannot load DBD::mysql while other users can


The site I administer has some CGI scripts that run scripts of the form:

#!/usr/bin/env bash

perl my-script.pl

my-script.pl uses DBD::mysql.

use DBD::mysql;

My scripts use many CPAN modules and I do not want to pollute the "system" Perl (5.16) installed by the Linux distro. Our security policy requires that httpd run as user "apache" and that apache not have a home directory on our server, so my solution has been to install Perl with perlbrew under a different home dir I have access to. Then the Apache config file for the virtual host sets some env vars to access it.

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

This works well-enough for loading most modules. For example, apache can run:

perl -mDateTime -e 'print $DateTime::VERSION' # prints "1.52"

but if apache attempts:

perl -mDBD::mysql -e 'print $DBD::mysql::VERSION'

it barfs:

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.

The error message "Can't locate ..." is misleading. I confirmed that DBD::mysql is available from the 3rd path in @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

Furthermore, user1 can load DBD::mysql with no problem:

 perl -mDBD::mysql -e 'print $DBD::mysql::VERSION'  # prints 4.050

Therefore, I suspect that the above error message should have read "Can't load libmysqlclient.so ..."

libmysqlclient.so is located in /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

If user1 runs perl -V, the Linker and Dynamic Linking sections show the following:

  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'

If I run this same perl as apache, it will produce the same result:

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'

How come user1 perl can load DBD::mysql but apache can't even though both are running the same Perl with the same @INC paths and their dynamic library loading paths look identical? Does anyone know what else can I do to get to the bottom of this?


Solution

  • For starters, you should never do

    SetEnv PERL5LIB /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/lib 
    

    If you use .../perl-5.30.2/bin/perl, it will know to look in .../perl-5.30.2/lib, and that's the only perl that should look in that directory.


    Ideally, you wouldn't do the following either:

    SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}
    

    The shebang of the script should point to the perl it's meant to use (the one with which it was tested and known to work).

    In other words, use the following in the bash script:

    ./my-script.pl
    

    And use the following shebang in my-script.pl:

    #!/export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin/perl
    

    What you are currently doing isn't terrible, but could bite you if you try to upgrade something.


    Finally, perl can't find the module because of permission issues. Assuming the apache user isn't a member of the user1 group, you showed that apache user can't access lib/site_perl/5.30.2/x86_64-linux/auto/DBD/mysql (and it might not be able access other pertinent files either).

    Fix:

    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