Welcome to Centmin Mod Community
Become a Member

Previews Jailed / chrooted SFTP & SSH user Nginx vhost menu

Discussion in 'Beta release code' started by eva2000, May 25, 2014.

Thread Status:
Not open for further replies.
  1. eva2000

    eva2000 Administrator Staff Member

    53,154
    12,110
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,645
    Local Time:
    2:43 PM
    Nginx 1.27.x
    MariaDB 10.x/11.4+
    Reproducing my blog preview at WordPress Auto Installer & SSH, SFTP, SCP chrooted user accounts for Nginx vhost | Centmin Mod Blog on the forums.

    Background
    Centmin Mod currently is at v1.2.3-eva2000.06 release but it still has one item missing that I always wanted to add and that is proper per Nginx domain vhost user accounts for chrooted SSH, SFTP and SCP access. To understand why this is the case, you have to understand a bit of the history of how Centmin Mod came into being as essentially the base structure for Nginx vhosts was derived from the original Centmin script.

    Centmin Mod started out in essentially as a modified version of Centmin original script. I came across the original Centmin script which auto installed via the Linux command line via a shell based script Nginx, PHP-FPM and Oracle MySQL 5.1 and around that time I was starting to get into using Nginx and MariaDB based MySQL fork. I loved the concept of the original Centmin script and thought to myself, how can I tailor the original Centmin script for my own specific custom needs ? I had no experience with shell based scripting at that time although I had plenty of experience with linux system administration. So I started Googling and reading up on shell based scripting and found it quite easy to pick up.

    I started slowly making changes to the original Centmin script – adding various new features and additional Nginx modules and PHP-FPM compilation options. Some of these changes were merged and incorporated into the official script with acknowledgement to my contribution. The biggest change for me was switching out Oracle MySQL 5.1 default to MariaDB 5.2 MySQL as default MySQL version. This was in 2011 and MariaDB 5.2 MySQL was the latest version of MySQL drop in replacement fork at the time and from my own MySQL comparison benchmarks(Oracle MySQL vs Percona vs MariaDB) at the time, it showed that MariaDB 5.2 was the best performing MySQL version for MyISAM+InnoDB table usage.

    Slowly over the course of a few months I had modified the original Centmin script so much and there was user demand for this modified version, I decided to name my version Centmin Mod so as to not confuse users over the two different versions. I eventually registered the domain centminmod.com and then came the biggest change yet – I changed the auto installer from command line to a shell based menu for easier selection of preset options.

    So up until now, Centmin Mod’s core Nginx structure is derived from the original script’s default structure – that being of intended use by one administrator (root user) to manage one’s own group of Nginx powered domains/sites from the base directory structure of /home/nginx/domains/domainname.com. There was no individual user accounts nor was there individual ftp account access. It was all done from root user and SCP/SFTP. Centmin Mod’s FAQ item #2 reflected such specifically mentioning that Centmin Mod was not suited to shared hosting as there was no jailed or chrooted user accounts set up. You would think this would be a show stopper for users, but currently there are over 1,500 new downloads of Centmin Mod script every month ! However, there’s always the question of whether Centmin Mod is suited for some form of shared hosting.

    Wanting to improve Centmin Mod script, last year I tried my first attempt (beta testing stages) at adding restricted SFTP only support for Centmin Mod(via rssh) changing the structure to /home/username/domainname.com. Unfortunately, that didn’t pan out with some weird issues with the auto migration feature of moving the old /home/nginx/domains/domainname.com to the new structure. For whatever reason, WordPress plugins kept installing into the old structure instead of the new. After numerous days of figuring it out, I left it at that as I usually only spent one day per week on Centmin Mod. I intended on coming back to revisit this when I had more time.

    Present Day
    The past week or so, I decided to try my second attempt at improving this aspect of Centmin Mod. This time instead of rssh, I decided to use Jailkit. It’s the first time I used Jailkit but this time was successful with early beta version of the code. The code is contained within a single include file so it’s just a drop in replacement to work with any Centmin Mod version. Just replace existing inc/nginx_addvhost.inc file with the new beta coded file and it works via the existing shell based menu – option #2 – Add Nginx vhost domain.

    There’s 2 options in the new code:

    1. non-chroot normal Nginx vhost setup like existing Centmin Mod setup managed via root user
    2. chroot setup with setups a new user account attached to each Nginx vhost domain. There are 2 methods of setup: one for SFTP+SCP access only or second method SFTP+SCP+SSH (limited SSH). I chose to keep the old Nginx vhost menu 2 option intact, as chrooted username/domain might have restrictions in what type of apps you can run out of the box right now i.e. perl, node.js, python etc. If you choose SFTP+SCP, your username account gets chrooted to /home/chroot_sftp/home/username. If you choose SSH+SFTP+SCP, your username account gets chrooted to /home/chroot_shell/home/username. Those are the paths you need to get to if you’re working as full root user. When you log into the chrooted username’s account you will only see /home/username.
    Wordpress auto installer routine
    Centmin Mod has official standalone Addons and one of them is WP-CLI WordPress command line installer by WP-CLI.org. The Addon only installed the WP-CLI code and still required the end user to run WP-CLI to install WordPress via the command line. I always wanted to extend this to an auto installer for WordPress at initial Nginx domain vhost creation stage. So decided to incorporate such code into the new shell based menu – option #2 - Add Nginx vhost domain option.

    So while doing tests of the new beta code, I decided to document some of this information on this blog so as to gather my thoughts and give Centmin Mod users and potential users some insights of what’s to come. This beta code still needs a lot of testing so won’t be available any time soon. But below is a screenshot preview of the current state of the code.

    Beta Screenshot Preview
    First the new shell based menu – option #2 - Add Nginx vhost domain option. I extended the menu into a submenu to offer the 2 above outlined modes of Nginx vhost setup.

    [​IMG]

    Selecting submenu option #2 for chrooted setup for the first time will install and configure Jailkit (one time only)


    After Jailkit install, it will prompt you to continue and enter a username for your SFTP/SCP only or SSH/SFTP/SCP only account. Then prompt you to add the domain name and whether or not you want to auto install WordPress on this new domain.. Here I used user1 and domain1.comas an example.


    Then you’ll be prompted to choose between SCP+SFTP only access (no SSH) or SSH+SCP+SFTP access. If you want to allow user1 to have SSH access in chrooted environment, you should select y for yes. At which point, the script:

    1. configures and sets up the user in the jailed chroot directory at /home/chroot_shell/home/user1.
    2. creates a specific PHP-FPM config file at /usr/local/nginx/conf/phpfpmd/phpfpm_user1.conf with it’s own user and group and own PHP-FPM pool [php_user1] with auto incrementing TCP listening port option – in this case port 9006 which is commented out by default and uses PHP-FPM socket instead.
    3. creates a specific PHP include config file at /usr/local/nginx/conf/php_user1.conf
    4. auto detects and calculates how much free system memory is available and auto sets via a preset formula (2/5ths of free memory) a PHP memory_limit based on that free memory availability
    [​IMG]

    Next up comes the actual WP-CLI installation if not already installed, the script will auto install WP-CLI first. Then it will prompt for relevant WordPress configuration questions and info. It will ask you to confirm the entered info before starting actual WordPress auto installation.

    [​IMG]


    Once you confirm the entered WordPress setup info is correct, the script will create the MySQL database, MySQL user/password and then grant the basic MySQL privileges needed for the MySQL user to access the specified MySQL database. It will also output what was created and granted via SHOW GRANTS option. Then finally, WP-CLI command line will go to work auto installing WordPress on your chosen Nginx domain vhost account.

    WP-CLI will auto generate the wp-config.php and automatically populate the file with relevant settings. The WP-CLI script also auto generates the SECURE/AUTH Keys too ! I coded my part of the script to go one step further and will also randomly generate a different WordPress database prefix and change the default wp_ prefix.

    [​IMG]

    Next part is the wonderful feature of WP-CLI allowing you to automatically install, update and even activate WordPress plugins from the command line ! I coded my script to auto install and activate some highly recommended and used WordPress plugins. These include the following WordPress plugins:

    [​IMG]

    Final completion stage, outputs the complete Nginx vhost and username information. Save this info to secure file for your records. You can also get a copy of the directory path info in a Nginx vhost database file that is created at /usr/local/nginx/conf/conf.d/domainname.db.

    [​IMG]

    Then visiting your newly created Nginx powered domain via your browser you’d be greeted with a fully working and installed WordPress installation !

    [​IMG]

    Logging into Admin side

    [​IMG]



    Auto installed and activated WordPress plugins

    [​IMG]



    Acunetix default security alerts

    [​IMG]

    Enabling security settings

    [​IMG]

    Security alerts after enabling settings – almost all green !

    [​IMG]

    Protection against brute force WordPress login attempts

    [​IMG]

    Sucuri WordPress Integrity Check options as well as many other options to dig into.

    [​IMG]

    WordPress Updates Notifier Plugin – get email notifications when WordPress updates for plugins and themes are available !

    [​IMG]

    Logging into user1 account via chrooted SSH

    [​IMG]

    MySQL client access for WordPress database

    [​IMG]

    WordPress Super Cache Auto installation
    Also added a new option to auto install and configure at Nginx vhost level, WordPress Super Cache. Easier to setup that Nginx fastcgi_cache.

    Admin settings do need to be manually enabled though

    [​IMG]

    Setting Advanced options and ensuring ‘Use mod_rewrite to serve cache files‘ is enabled.

    [​IMG]

    Listing of cached pages. Need to regenerate cache stats first.

    [​IMG]

    Checking to see if WordPress Super Cache is working via curl and Siege benchmark tests of WordPress index page as guest non-logged visitor as well as enabling debug logging.

    Code:
    curl -S http://domain2.com/ | tail -4
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100  7564  100  7564    0     0  21.5M      0 --:--:-- --:--:-- --:--:-- 7386k
    <!-- Dynamic page generated in 0.191 seconds. -->
    <!-- Cached page generated by WP-Super-Cache on 2014-01-31 11:15:26 -->
    
    <!-- super cache -->
    Debug Log

    Code:
    11:15:25 / supercache dir: /public/wp-content/cache/supercache/domain2.com/
    11:15:25 / No Super Cache file found for current URL: /public/wp-content/cache/supercache/domain2.com/index.html
    11:15:26 / In WP Cache Phase 2
    11:15:26 / Setting up WordPress actions
    11:15:26 / Created output buffer
    11:15:26 / Output buffer callback
    11:15:26 / Anonymous user detected. Only creating Supercache file.
    11:15:26 / Writing non-gzipped buffer to supercache file.
    11:15:26 / Renamed temp supercache file to /public/wp-content/cache/supercache/domain2.com/index.html
    11:15:26 / Sending buffer to browser
    11:15:26 / wp_cache_shutdown_callback: collecting meta data.
    11:15:26 / Did not write meta file: wp-cache-d648e02b13c18cc73d9566b7ed76bdf9.meta *1* *0* *1*
    Siege benchmark

    Code:
    siege -q -b -c10 -r10 http://domain2.com/
    Transactions: 100 hits
    Availability: 100.00 %
    Elapsed time: 0.10 secs
    Data transferred: 0.23 MB
    Response time: 0.01 secs
    Transaction rate: 1000.00 trans/sec
    Throughput: 2.28 MB/sec
    Concurrency: 6.50
    Successful transactions: 100
    Failed transactions: 0
    Longest transaction: 0.04
    Shortest transaction: 0.00
    So this ends the screenshot preview of what’s to come in future Centmin Mod versions. Be sure to follow Centmin Mod progress on Twitter @centminmod, Centmin Mod Google+ Page, Google+ Community and of course on the official Centmin Mod web site.
     
    Last edited: Sep 30, 2014
  2. eva2000

    eva2000 Administrator Staff Member

    53,154
    12,110
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,645
    Local Time:
    2:43 PM
    Nginx 1.27.x
    MariaDB 10.x/11.4+

    Preview Status Notes:

     
    Last edited: Sep 7, 2014
  3. eva2000

    eva2000 Administrator Staff Member

    53,154
    12,110
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,645
    Local Time:
    2:43 PM
    Nginx 1.27.x
    MariaDB 10.x/11.4+

    September 2014 Updates



    • Minor movement with regards to jailed/chroot user improvements in Nginx vhost menu generator feature. Will start testing for CentOS 7 as well as 6.5 soon. Internal test still with first beta access available later when ready to Premium Users :)
    • Adding Wordpress Widget Cache Plugin to default Wordpress Super Cache auto installer routine
     
    Last edited: Sep 7, 2014
  4. eva2000

    eva2000 Administrator Staff Member

    53,154
    12,110
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,645
    Local Time:
    2:43 PM
    Nginx 1.27.x
    MariaDB 10.x/11.4+

    Pure-ftpd Virtual FTP users



    Full jailed/chroot user development outlined above is still a long way off. So for now have added to Centmin Mod .08 beta builds a work around for implementing pure-ftpd and virtual FTP user support which should lock the FTP user to the Nginx vhost directory. Full details and example here.

    Pure-ftpd method will only give isolated virtual FTP user access. There's no SSH access. My above fully jailed/chrooted preview allows for both SFTP jailed user access and/or SFTP + SSH user jailed user access.
     
    Last edited: Jan 18, 2015
  5. eva2000

    eva2000 Administrator Staff Member

    53,154
    12,110
    113
    May 24, 2014
    Brisbane, Australia
    Ratings:
    +18,645
    Local Time:
    2:43 PM
    Nginx 1.27.x
    MariaDB 10.x/11.4+

    July 2016 Experiments



    Still a long way off, but experimenting some more :)

    restricted system user = george to only sftp, scp and rsync privileges so when you try to SSH in you get:
    Code (Text):
    This account is restricted by rssh.
    Allowed commands: scp sftp rsync
    
    If you believe this is in error, please contact your system administrator.
    

    user george web root listing
    Code (Text):
    ls -lah /home/george/domain.com/public/
    total 68K
    drwxr-s---+ 3 george george 4.0K Jul 12 06:11 .
    drwxr-s---+ 6 george george   56 Jul 12 05:07 ..
    -rw-r--r--  1 george george 1.6K Jul 12 05:07 403.html
    -rw-r--r--  1 george george 1.6K Jul 12 05:07 404.html
    -rw-r--r--  1 george george 2.1K Jul 12 05:07 500.html
    -rw-r--r--  1 george george 2.1K Jul 12 05:07 502.html
    -rw-r--r--  1 george george 2.2K Jul 12 05:07 503.html
    -rw-r--r--  1 george george 7.6K Jul 12 05:07 503.jpg
    -rw-r--r--  1 george george 2.1K Jul 12 05:07 504.html
    -rw-r--r--  1 george george 2.2K Jul 12 05:07 50x.html
    -rw-r--r--  1 george george 1.4K Jul 12 05:07 index.html
    -rw-r--r--  1 george george 1.7K Jul 12 05:07 maintenance.html
    -rw-r-----+ 1 george george   17 Jul 12 05:43 phpinfo.php
    drwxr-s---+ 2 george george    6 Jul 12 06:10 testdir1
    -rw-r-----+ 1 george george    0 Jul 12 06:11 testfile1.txt
    -rw-r-----+ 1 george george  143 Jul 12 06:24 test.php
    -rw-r--r--+ 1 george george    0 Jul 12 05:14 test.txt
    

    output for test.php file with exec() functions as follows
    Code (Text):
    <?php
    echo exec('whoami');
    echo exec('ls -lah /home/nginx/domains/domain.com/public');
    echo exec('ls -lah /home/george/domain.com/public');
    ?>
    

    results in following output where only testfile1.txt was uploaded via sftp user = george - as exec() only lists last line of output instead of shell_exec()
    Code (Text):
    george-rw-r-----+ 1 george george 0 Jul 12 06:11 testfile1.txt
    

    so need to edit /usr/local/nginx/conf/phpfpmd/phpfpm_george.conf and remove shell_exec() restriction from disable_functions
    Code (Text):
    php_admin_value[disable_functions] = shell_exec
    

    Change to
    Code (Text):
    ;php_admin_value[disable_functions] = shell_exec
    

    then change test.php file with exec() functions as follows
    Code (Text):
    <?php
    echo exec('whoami');
    echo shell_exec('ls -lah /home/nginx/domains/domain.com/public');
    echo shell_exec('ls -lah /home/george/domain.com/public');
    ?>
    

    end up with test.php output below just for user = george owned files
    Code (Text):
    georgetotal 68K drwxr-s---+ 3 george george 4.0K Jul 12 06:38 . drwxr-s---+ 6 george george 56 Jul 12 05:07 .. -rw-r--r-- 1 george george 1.6K Jul 12 05:07 403.html -rw-r--r-- 1 george george 1.6K Jul 12 05:07 404.html -rw-r--r-- 1 george george 2.1K Jul 12 05:07 500.html -rw-r--r-- 1 george george 2.1K Jul 12 05:07 502.html -rw-r--r-- 1 george george 2.2K Jul 12 05:07 503.html -rw-r--r-- 1 george george 7.6K Jul 12 05:07 503.jpg -rw-r--r-- 1 george george 2.1K Jul 12 05:07 504.html -rw-r--r-- 1 george george 2.2K Jul 12 05:07 50x.html -rw-r--r-- 1 george george 1.4K Jul 12 05:07 index.html -rw-r--r-- 1 george george 1.7K Jul 12 05:07 maintenance.html -rw-r-----+ 1 george george 17 Jul 12 05:43 phpinfo.php -rw-r-----+ 1 george george 155 Jul 12 06:42 test.php -rw-r--r--+ 1 george george 0 Jul 12 05:14 test.txt drwxr-s---+ 2 george george 6 Jul 12 06:10 testdir1 -rw-r-----+ 1 george george 0 Jul 12 06:11 testfile1.txt -rw-r-----+ 1 george george 0 Jul 12 06:38 testfile2.txt
    

    separate pool for system user = george
    Code (Text):
    root     22424  0.0  1.4 114428 27848 ?        Ss   06:14   0:00 nginx: master process /usr/local/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    nginx    22425  0.0  1.8 118524 34100 ?        S<   06:14   0:00  \_ nginx: worker process
    root     22448  0.0  0.5 526976  9448 ?        Ss   06:14   0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
    george   22449  0.0  0.5 526828 10676 ?        S    06:14   0:00  \_ php-fpm: pool george

    php-fpm separate pool for system user = george at /usr/local/nginx/conf/phpfpmd/phpfpm_george.conf
    Code (Text):
    cat /usr/local/nginx/conf/phpfpmd/phpfpm_george.conf
    [george]
    user = george
    group = george
    
    listen = 127.0.0.1:9015
    listen.allowed_clients = 127.0.0.1
    listen.backlog = 65535
    
    ;listen = /tmp/php5-fpm-george.sock
    listen.owner = george
    listen.group = george
    listen.mode = 0660
    
    pm = ondemand
    pm.max_children = 4
    ; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
    pm.start_servers = 4
    pm.min_spare_servers = 2
    pm.max_spare_servers = 6
    pm.max_requests = 1000
    
    ; PHP 5.3.9 setting
    ; The number of seconds after which an idle process will be killed.
    ; Note: Used only when pm is set to 'ondemand'
    ; Default Value: 10s
    pm.process_idle_timeout = 10s;
    
    rlimit_files = 65536
    rlimit_core = 0
    
    ; The timeout for serving a single request after which the worker process will
    ; be killed. This option should be used when the 'max_execution_time' ini option
    ; does not stop script execution for some reason. A value of '0' means 'off'.
    ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
    ; Default Value: 0
    ;request_terminate_timeout = 0
    ; Default Value: 0
    ;request_slowlog_timeout = 0
    slowlog = /var/log/php-fpm/www-slow-george.log
    
    pm.status_path = /phpstatus-george
    ping.path = /phpping-george
    ping.response = pong
    
    ; Limits the extensions of the main script FPM will allow to parse. This can
    ; prevent configuration mistakes on the web server side. You should only limit
    ; FPM to .php extensions to prevent malicious users to use other extensions to
    ; exectute php code.
    ; Note: set an empty value to allow all extensions.
    ; Default Value: .php
    security.limit_extensions = .php .php3 .php4 .php5
    
    ; catch_workers_output = yes
    php_admin_value[error_log] = /var/log/php-fpm/www-php.error-george.log
    php_admin_value[disable_functions] = shell_exec
    

    upload_2016-7-12_16-20-9.png

    Just experimenting so long way of from testing and roll out :)
     
    Last edited: Jul 12, 2016
Thread Status:
Not open for further replies.