2

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?

Lawrence I. Siden
  • 9,191
  • 10
  • 43
  • 56

1 Answers1

2

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
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • The config should have read "SetEnv PATH /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin:${PATH}". I tried to obfuscate the real user name but I guess I wasn't careful enough. My bad. Kindly, change it in your response as well if you don't mind, so that I can comply with my employer's security policies. – Lawrence I. Siden Apr 23 '20 at 14:34
  • thank you so much. I'll leave the history alone. I just don't want to leave a "welcome" sign. Meanwhile, I tried this: env -i bash -c 'source apache-path; env |sort; perl -mDBD::mysql -e "print \$DBD::mysql::VERSION" ' and it worked, so apache _can_ load DBD::mysql if run from the command line. The file apache-path sets the PATH env var. – Lawrence I. Siden Apr 23 '20 at 15:04
  • Sets its incorrectly, from what you shown (There's no directory named `~user1`). Are you sure the right `perl` was executed in your tests? You should be testing using `sudo -u apache /export/home/user1/perl5/perlbrew/perls/perl-5.30.2/bin/perl -mDBD::mysql -e1` Anyway, you showed the permissions being `drwxr-x--- user1 user1` for `auto/`. That's clearly wrong. Did you fix that as detailed in my answer? – ikegami Apr 23 '20 at 16:02
  • I ran it again with more control over the environment. This time: "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)" There is no "auto" in INC this time. DBD/mysql.pm is available in the first path. – Lawrence I. Siden Apr 23 '20 at 16:07
  • @inktomi, the error message says "Can't locate loadable object for module DBD::mysql ..." I still think that means Perl it can't find the .so file for mysql when run by apache. It's there: "-rwxr-xr-x 1 root root 3135664 Aug 18 2019 /usr/lib64/mysql/libmysqlclient.so.18.0.0". Perl can load it successfully when run as user1 from the command line. It also works when I run it from my own personal account on the server. Only apache has trouble, even from the command line. – Lawrence I. Siden Apr 23 '20 at 16:16
  • Sorry, I wasn't ignoring you. I didn't see "auto" in INC. I'll try that now. I already followed your other suggestions. – Lawrence I. Siden Apr 23 '20 at 16:22
  • Re "*I wasn't ignoring you.*", I said the problem was that you needed to fix the permissions. Thrice. /// `auto` shouldn't be in `@INC` (only the directory containing `auto`) – ikegami Apr 23 '20 at 16:23
  • I got it. You were right. Forgive me for not understanding sooner. I need to read up on Perl's module search method and how it uses 'auto'. – Lawrence I. Siden Apr 23 '20 at 16:29
  • It must be where Dynaloader and XSLoader locate compiled XS (C) components of modules (the glue between Perl and libmysqlclient, in this case) – ikegami Apr 23 '20 at 16:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/212372/discussion-between-lawrence-i-siden-and-ikegami). – Lawrence I. Siden Apr 23 '20 at 16:41