suPHP and open_basedir together, for improved security.

likudio

Member
Jan 11, 2015
14
2
53
cPanel Access Level
Root Administrator
I. Preface:

Okay, so here it goes.
I knocked my head to the wall several times, after finding out some security issues which cPanel has, like:

  • using only suPHP isn't secure enough (I managed to open root files with suPHP enabled, without open_basedir);
  • the open_basedir tweak implemented in WHM limits users only to their working directory, not to the DocumentRoot of the domains that users have, so any script from any domain can navigate to other domain without any issues (see this topic).
  • suPHP doesn't work with open_basedir in WHMs default configuration (so you have to use DSO or other handler, which doesn't handle permissions also)
  • using only open_basedir without suPHP isn't secure, as if by mistake open_basedir gets disabled, without suPHP any user can navigate anywhere in the server.


So... as you can guess, the best solution is suPHP + open_basedir.
But, as cPanel doesn't offer a solution for this configuration together, I managed to create a solution by my own.


II. Purpose:

This solution secures your server considering two different perspectives:
  1. Limits a script to navigate outside of it's DocumentRoot (so you can have multiple domains in a cPanel account, which won't be able to get outside of their root directory; currently they are only limited to the Users Directory, so a script can reach wherever it wants in the UsersDir);
  2. Makes suPHP work with open_basedir together, which wasn’t possible until now using the current WHM configuration;



III. Solution - short description

RECOMANDATION: DO NOT DO THIS UNLESS YOU HAVE SOME LINUX OPERATING KNOWLEDGE;
(but if you own are a “Root Administrator”, I assume you have some :) )

The following scripts, were made to monitor the httpd.conf file, so whenever they find a change in it, they generate a separate php ini configuration file, based on the DocumentRoot paths, and loads the open_basedir directive in them, according to the paths from httpd.conf



IV. Solution - long description

This solution was tested with:

  • Easy Apache v3.28.1
  • Apache 2.2
  • PHP 5.4
  • Mod SuPHP 0.7.2


...but should work with the most cPanel configurations using suPHP.

1. The first thing to do, is to find out if PHP is configured to scan for additional ini files, other than its main php.ini . To find out this, considering you are logged in the console as root, run the following command:
Code:
php --ini
If your answer is like:

Code:
Configuration File (php.ini) Path: /usr/local/lib
Loaded Configuration File:         /usr/local/lib/php.ini
Scan for additional .ini files in: /usr/local/lib/php.ini.d
Additional .ini files parsed: (none);
it means that your PHP is configured to look for other php ini files, and you can skip to step 3.

Otherwise, if it looks like this:
Code:
Configuration File (php.ini) Path: /usr/local/lib
Loaded Configuration File:         /usr/local/lib/php.ini
Scan for additional .ini files in: (none)
Additional .ini files parsed:      (none)
get to step 2.


2. You have to recompile Apache and PHP to scan for additional .ini files.
To do this, go in EasyApache from WHM main menu, get to “Exhaustive Options List” tab, and look for “Safe PHP CGI” checkmark, and check it. Then recompile Apache and PHP. Once its done, run again:
Code:
php --ini
If it looks like this, it’s all good:

Code:
Configuration File (php.ini) Path: /usr/local/lib
Loaded Configuration File:         /usr/local/lib/php.ini
Scan for additional .ini files in: /usr/local/lib/php.ini.d
Additional .ini files parsed: (none);
3. The /usr/local/lib/php.ini.d directory might not exist. Run the following commands to create it:
Code:
cd /usr/local/lib/
mkdir php.ini.d
Note: in this directory, you can load your custom php.ini files, to configure whatever you want on your server; PHP will scan for ini files in this directory, and will load them instantly, basically without restart. (yeah, I wonder too how the hell it works without restart...)

Warning: Be aware of misspelled configuration directives, it might corrupt your PHP server.

4. For future purposes, let's create a file in /usr/local/lib/php.ini.d, named open_basedir.ini;
Execute the following command:

Code:
cd /usr/local/lib/php.ini.d
echo ";" > open_basedir.ini
(in order to run the script smoothly for the first time, this file must contain ; character in it, which we just did with the command above)

To see if PHP has already acknowledged the file, run again:

Code:
php --ini
and the result should look like this:

Code:
Configuration File (php.ini) Path: /usr/local/lib
Loaded Configuration File:         /usr/local/lib/php.ini
Scan for additional .ini files in: /usr/local/lib/php.ini.d
Additional .ini files parsed:      /usr/local/lib/php.ini.d/open_basedir.ini
(observe the last line)

5. For future investigation purposes of the generated open_basedir paths which will be written in this file, we create another directory in /usr/local/lib/, named “ops(ops comes from “old php settings”);

Code:
cd  /usr/local/lib/
mkdir ops
In this directory you will find any old used open_basedir.ini file, which was automatically replaced with a newer version, as to httpd.conf;
The files will be renamed like: open_basedir.ini.2015.01.16_02.42.55 name which will represent the date when they have been first put in use.



6. Enough with PHP ini files configuration. Now, let's get to the bottom of the problem:
We`ll create a directory in /home/, named .sys (yes, the dot is intended), where we will store the content of the project scripts.
These files will do all the job, monitor the httpd.conf file, and generate a php ini file according to the DocumentRoots found in httpd.conf;
Run the following commands:

Code:
cd /home/
mkdir .sys
cd .sys
wget http://download.mcbsoft.net/cpanel-openbasedir-project/open_basedir.tar
tar xvf open_basedir.tar
cd open_basedir
chmod +x open_basedir.sh
7. Good. So far we have extracted the script files and made the main file executable. Now it’s time to configure the script. Basically, all you have to configure is your e-mail address where you will receive your daily or the fail report (if case).
Now let’s open the “inc.user-config.php” file, where you will type your own mail address:

Code:
nano inc.user-config.php
And the first lines will look like this:

PHP:
<?php

        // write your e-mail address on the line down below, between the quotes.
        $CONFIG['WEBMASTER_MAIL'] = '[email protected]';
Now replace the [email protected] with your own mail address, between the quotes. Ctrl + X to save, Y to confirm and that’s all.

8. Now, all you have to do left is to install the main script file to the crontab, which will run the script:

Code:
crontab -e
Go to the last line, get to a new line, and paste the following code:

Code:
* * * * * /home/.sys/open_basedir/open_basedir.sh > /dev/null 2>&1
Ctrl + X, Y to save… installing new crontab and that’s it.

You will get an e-mail with the first report which will look like this:
Code:
2   2015-01-16 02:21:41  |  4~12   2015-01-16 02:43:34


OK -->
Array
(
    [STATUS] => 1
    [LOG] => Array
        (
            [0] => Starting $generateINI... 2015-01-16 02:43:34
            [1] => Succesfully loaded httpd.conf file, lenght: 55086
            [2] => Starting processing the httpd.conf file...
            [3] => Counted 4 paths...
            [4] => Array
(
    [path1] => Array
        (
            [0] => /home/path1
            [1] => /home/path1/public_html
        )

    [path2] => Array
        (
            [0] => /home/path2
            [1] => /home/path2/public_html
        )

    [path3] => Array
        (
            [0] => /home/path3
            [1] => /home/path3/public_html
            [2] => /home/path3/www_somepath
            [3] => /home/path3/www_someotherpath
            [4] => /home/path3/www_anotherpathl
            [8] => /home/path3/www_x
        )

    [path4] => Array
        (
            [0] => /home/path4
            [1] => /home/path4/public_html
        )
)

            [5] => Managed to copy the old open_basedir.ini file contents to /usr/local/lib/ops/open_basedir.ini.2015.01.16_02.42.55
            [6] => Succesfully deleted the previous open_basedir.ini file from /usr/local/lib/php.ini.d/
            [7] => Successfully written what we had to write...
            [8] => End time: 2015-01-16 02:43:34
            [9] => Array
                (
                    [PHP_INI_DIR] => /usr/local/lib/php.ini.d/
                    [OLD_PHP_INI_DIR] => /usr/local/lib/ops/
                    [HTTPD_CONF_FILE] => /usr/local/apache/conf/httpd.conf
                    [SETTINGS_INI_NAME] => open_basedir.ini
                    [STATUS] => 1
                    [STATUS_FILE] => ./status.log
                    [FAIL_STATUS_FILE] => ./last_fail.log
                    [OK_STATUS_FILE] => ./last_ok.log
                    [HTTPD_MTIME] => ./mtime.dat
                    [MAIL_STATUS_FILE] => ./mail.dat
                    [WEBMASTER_MAIL] => [email][email protected][/email]
                    [ALERT_SUBJECT] => open_basedir.ini status
                    [COUNT_USERS] => 6
                    [COUNT_DOMAINS] => 20
                )

        )

)
<-- OK

FAIL -->
<-- FAIL
which is basically the report of what the script did.


9. You can verify the generated open_basedir.ini configuration file, by running this command:
Code:
cat /usr/local/lib/php.ini.d/open_basedir.ini
And it will show you the generated php ini configuration file.

10. You can verify the status of your script anytime by running this command:
Code:
cat /home/.sys/open_basedir/status.log
It will output the last 30 changes made to httpd.conf, and the last line represents the last time the script verified httpd.conf.

Description of the line:

Code:
2   2015-01-16 02:45:16  |  6~21   2015-01-16 02:46:01
  • 2 - the first number is the status.
    • 0 means no changes found in httpd.conf
    • 1 means changes found, but the script failed to generate the open_basedir paths; it will issue a e-mail to your defined address with the fail report. The script sends only one failed e-mail per day.
    • 2 means changes found, and the script successfully generated the open_basedir.ini file.
  • 2015-01-16 02:45:16 - the first date displayed in the status line, is the last date when httpd.conf was changed and a new open_basedir.ini was generated.
  • 6~21 . 6 represents the number of user directories found in httpd.conf, and 21 is the total number of DocumentRoot paths found according to the last changes of the file.
  • 2015-01-16 02:46:01 - the last date represents the last time the script ran.

Also, you will get this report on e-mail everynight at 00:00 AM, server time.

If you are interested in what other files represent in the working directory:

  • last_fail.log - represents the log with the information of the last failure of generation. (should never fail anyway, but was “failsafe” programmed)
  • last_ok.log - represents the log with the information of the last time of a successful ini generation.


IV. Testing…

Create any PHP file anywhere with <?php phpinfo(); ?> content in it, and search for open_basedir value, and "voila" :) Also, try to create a script which tries to get any file from outside of the user directory, and see ^^

V. That's all folks...
I hope you find this script useful for you also, and I didn’t numb your head with so much information.
If anyone of you has any further questions or issues, feel free to ask them, I will respond do them as soon as I can.

Regards,

Adelin a.k.a. likudio
 

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,880
2,268
463
Hello :)

Thank you for taking the time to post this workaround. Please keep in mind that user-submitted workarounds are not reviewed by cPanel, so implement them at your own risk.
 

likudio

Member
Jan 11, 2015
14
2
53
cPanel Access Level
Root Administrator
Hi Michael,

Well, I'd like if cPanel representatives would take a look over my work around and see the issues that it solves, and the features that it brings.
It would be nice to have these features already implemented in a future cPanel version, as they aren't so complicated to implement; and also, cPanel developers might find an even better way to implement them.

Regards.
A.
 

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,880
2,268
463
You are welcome to submit a feature request for the additional functionality via:

Submit A Feature Request

Feature requests are often viewed by developers upon submission.

Thank you.
 

likudio

Member
Jan 11, 2015
14
2
53
cPanel Access Level
Root Administrator
Hi Michael,

Unfortunately, most of cPanel users aren't aware about the security vulnerabilities that using only suPHP or only open_basedir tweak brings, nor the benefits that such solution as one of the above has... so, it won't be such popular - therefore it won't get voted.

But, I already did submit a feature request that should implement a part of the above work around, but is still "under moderation" (even though several days have passed).

Anyway, I stopped bothering it. Whoever will want to get its server more secure, will implement the solution above.
Is that simple.

Thank you for your support anyway.
 

Clouseau

Active Member
Jan 17, 2015
41
3
58
cPanel Access Level
Root Administrator
Hi likudio. Nice post but I don't know why is this needed because cPanel documentation states how to enable it , although it doesn't work:
https://documentation.cpanel.net/di...ConfigurePHPandsuEXEC-Step4:PHPCustomizations

I have copied php.ini file from /usr/local/lib/php.ini to /home/user/php.ini and I have added in it:
open_basedir = /home/user

But I can still access root /etc/ /var with php from the website located in /home/user/www.
 

likudio

Member
Jan 11, 2015
14
2
53
cPanel Access Level
Root Administrator
Hi Clouseau.

1. cPanel states that suPHP with open_basedir tweak doesn't work. And it doesn't in the default configuration.
2. Copying the main ini from its source to users directory, doesn't help. And anyway, you shouldn't do that, as you want only new directives in the new ini file, not all directives.
3. It depends on how Apache + PHP is configured; this decides if the php.ini from UsersDir works or not.
4. Don't forget that if the php.ini file is in the user directory, the user might have access to it and modify it as needed (so it would get rid of your restrictions).
5. If you restrict user only to /home/user you will get your users into trouble, as they won't be able to access libs and tmps anymore, which might corrupt their websites. Be careful.
6. If you can still access /root /var /etc with PHP, you're definitely into trouble. You shouldn't allow such access...
7. Conclusion... your solution doesn't work :D I recommend to implement the solution from the tutorial I wrote above.

Regards.
 
Last edited:

Clouseau

Active Member
Jan 17, 2015
41
3
58
cPanel Access Level
Root Administrator
:D

What about setting this in global php.ini file open_basedir = /home/:/usr/lib/php:/usr/local/lib/php:/tmp
This is probably the same setting that is in Php configuration editor - Advanced - open_basedir? It is stated DEFAULT here but I could add above so the users would be locked to those directories, right?

EDIT: yes, that's the option. But doesn't help. Crap.
Your solution is a great find but its too much cumberstone for me, no ofense :)
 
Last edited:

likudio

Member
Jan 11, 2015
14
2
53
cPanel Access Level
Root Administrator
Adding a general /home/ open_basedir setting will prevent users from accessing /root or other directories, indeed, PHP should load it, but you still can't prevent multiple separate domains from accessing one another's resources.

:D
Your solution is a great find but its too much cumberstone for me, no ofense :)
Well... adding all users directories and domain paths to the main php.ini might be much more "cumberstone" :D and you have to do it every time a domain or subdomain is changed on the server, how can you possible monitor that, to add them, in a manual way? :D

Anyway, I'm not trying to convince anyone here, free-will for everybody.
But if I'd be your client, I'd like my files to be secured as hell :D and I'd question myself about my hosting solution, if my sysadmin's laziness would prevent him from implementing some security features | "cumberstone" (no offense also) :D
 

Clouseau

Active Member
Jan 17, 2015
41
3
58
cPanel Access Level
Root Administrator
Agreed. I planed to migrate all those clients to ISPConfig but in the end was too resource and time intensive that I gave up. ISPConfig has all that fixed and supports php-fpm but I have to stay with cpanel...
 

lorio

Well-Known Member
Feb 25, 2004
314
22
168
cPanel Access Level
Root Administrator
Unfortunately, most of cPanel users aren't aware about the security vulnerabilities that using only suPHP or only open_basedir tweak brings, nor the benefits that such solution as one of the above has... so, it won't be such popular - therefore it won't get voted.
That is an import topic. These feature voting is no silver bullet.

This is a nice post with an elaborated solution and it would be nice if a statement from the developers of cpanel could be added.
Perhaps suPHP is no longer the way to go? Too CPU demanding?

@likudio: I wanted to add a link to your feature request. But I haven't found it in the database.
I only found this one: Enable auto addition of open base directory protection in to suphp mode. | cPanel Feature Requests

I really wonder if suPHP isn't to CPU demanding for websites with x users/y connections? Is here anybody using suPHP on VPS with 4-8 cores (not sure how to compare them) and 8-16 GB for hosting e.g. a forum and can get a lot of users served?
 

likudio

Member
Jan 11, 2015
14
2
53
cPanel Access Level
Root Administrator
That is an import topic. These feature voting is no silver bullet.
@likudio: I wanted to add a link to your feature request. But I haven't found it in the database.
That's because it hasn't been approved by cPanel moderators ... (I wonder why, rhetorical).
 

cPanelMichael

Administrator
Staff member
Apr 11, 2011
47,880
2,268
463
That's because it hasn't been approved by cPanel moderators ... (I wonder why, rhetorical).
Could you verify the title of your feature request?

Thank you.