The Community Forums

Interact with an entire community of cPanel & WHM users!
  1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

suPHP and open_basedir together, for improved security.

Discussion in 'Workarounds and Optimization' started by likudio, Jan 15, 2015.

  1. likudio

    likudio Member

    Joined:
    Jan 11, 2015
    Messages:
    13
    Likes Received:
    0
    Trophy Points:
    1
    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'] = 'your.mail.address.goes.here@isp.com';
    Now replace the ‘your.mail.address.goes.here@isp.com’ 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]your@email.com[/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
     
  2. cPanelMichael

    cPanelMichael Forums Analyst
    Staff Member

    Joined:
    Apr 11, 2011
    Messages:
    30,762
    Likes Received:
    662
    Trophy Points:
    113
    cPanel Access Level:
    Root Administrator
    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.
     
  3. likudio

    likudio Member

    Joined:
    Jan 11, 2015
    Messages:
    13
    Likes Received:
    0
    Trophy Points:
    1
    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.
     
  4. cPanelMichael

    cPanelMichael Forums Analyst
    Staff Member

    Joined:
    Apr 11, 2011
    Messages:
    30,762
    Likes Received:
    662
    Trophy Points:
    113
    cPanel Access Level:
    Root Administrator
    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.
     
  5. likudio

    likudio Member

    Joined:
    Jan 11, 2015
    Messages:
    13
    Likes Received:
    0
    Trophy Points:
    1
    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.
     
  6. Clouseau

    Clouseau Active Member

    Joined:
    Jan 17, 2015
    Messages:
    28
    Likes Received:
    0
    Trophy Points:
    1
    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.
     
  7. likudio

    likudio Member

    Joined:
    Jan 11, 2015
    Messages:
    13
    Likes Received:
    0
    Trophy Points:
    1
    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.
     
    #7 likudio, Jan 22, 2015
    Last edited: Jan 22, 2015
  8. Clouseau

    Clouseau Active Member

    Joined:
    Jan 17, 2015
    Messages:
    28
    Likes Received:
    0
    Trophy Points:
    1
    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 :)
     
    #8 Clouseau, Jan 22, 2015
    Last edited: Jan 22, 2015
  9. likudio

    likudio Member

    Joined:
    Jan 11, 2015
    Messages:
    13
    Likes Received:
    0
    Trophy Points:
    1
    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.

    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
     
  10. Clouseau

    Clouseau Active Member

    Joined:
    Jan 17, 2015
    Messages:
    28
    Likes Received:
    0
    Trophy Points:
    1
    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...
     
  11. lorio

    lorio Well-Known Member

    Joined:
    Feb 25, 2004
    Messages:
    243
    Likes Received:
    3
    Trophy Points:
    18
    cPanel Access Level:
    Root Administrator
    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?
     
  12. likudio

    likudio Member

    Joined:
    Jan 11, 2015
    Messages:
    13
    Likes Received:
    0
    Trophy Points:
    1
    cPanel Access Level:
    Root Administrator
    That's because it hasn't been approved by cPanel moderators ... (I wonder why, rhetorical).
     
  13. cPanelMichael

    cPanelMichael Forums Analyst
    Staff Member

    Joined:
    Apr 11, 2011
    Messages:
    30,762
    Likes Received:
    662
    Trophy Points:
    113
    cPanel Access Level:
    Root Administrator
    Could you verify the title of your feature request?

    Thank you.
     
Loading...

Share This Page