diff --git a/server/conf/nginx_vhost.conf.master b/server/conf/nginx_vhost.conf.master index d3d7b4c518a17d0e01ae14295fe8ecafae013ee5..cdf1cb4c15a569d2d0b9af18a0c85c474a2a1d5b 100644 --- a/server/conf/nginx_vhost.conf.master +++ b/server/conf/nginx_vhost.conf.master @@ -184,7 +184,8 @@ server { fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /web$fastcgi_script_name; - + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #fastcgi_param PATH_INFO $fastcgi_script_name; diff --git a/server/plugins-available/nginx_plugin.inc.php b/server/plugins-available/nginx_plugin.inc.php index 279fa6cf6b27939f7f289a5def62257152989e18..cd33c12d6cc3fa248c62539fefc8db5508dfe10d 100644 --- a/server/plugins-available/nginx_plugin.inc.php +++ b/server/plugins-available/nginx_plugin.inc.php @@ -1026,8 +1026,10 @@ class nginx_plugin { if($data['new']['php_fpm_chroot'] == 'y'){ $php_fpm_chroot = 1; + $php_fpm_nochroot = 0; } else { $php_fpm_chroot = 0; + $php_fpm_nochroot = 1; } if($data['new']['php_fpm_use_socket'] == 'y'){ $use_tcp = 0; @@ -1039,6 +1041,7 @@ class nginx_plugin { $tpl->setVar('use_tcp', $use_tcp); $tpl->setVar('use_socket', $use_socket); $tpl->setVar('php_fpm_chroot', $php_fpm_chroot); + $tpl->setVar('php_fpm_nochroot', $php_fpm_nochroot); $fpm_socket = $socket_dir.$pool_name.'.sock'; $tpl->setVar('fpm_socket', $fpm_socket); $tpl->setVar('rnd_php_dummy_file', '/'.md5(uniqid(microtime(), 1)).'.htm'); @@ -2035,7 +2038,14 @@ class nginx_plugin { //exec('fuser -km '.escapeshellarg($data['old']['document_root'].'/'.$log_folder).' 2>/dev/null'); exec('umount '.escapeshellarg($data['old']['document_root'].'/'.$log_folder).' 2>/dev/null'); } - + + //try umount mysql + if(file_exists($data['old']['document_root'].'/var/run/mysqld')) { + $fstab_line = '/var/run/mysqld ' . $data['old']['document_root'] . '/var/run/mysqld none bind,nobootwait 0 0'; + $app->system->removeLine('/etc/fstab', $fstab_line); + $command = 'umount ' . escapeshellarg($data['old']['document_root']) . '/var/run/mysqld/'; + exec($command); + } // remove letsencrypt if it exists (renew will always fail otherwise) $old_domain = $data['old']['domain']; diff --git a/server/plugins-available/php_fpm_jailkit_plugin.inc.php b/server/plugins-available/php_fpm_jailkit_plugin.inc.php new file mode 100755 index 0000000000000000000000000000000000000000..e5ab5c45ebe85f9e32f08df2844cd3da5d649970 --- /dev/null +++ b/server/plugins-available/php_fpm_jailkit_plugin.inc.php @@ -0,0 +1,310 @@ +plugins->registerEvent('web_domain_insert', $this->plugin_name, 'insert'); + $app->plugins->registerEvent('web_domain_update', $this->plugin_name, 'update'); + $app->plugins->registerEvent('web_domain_delete', $this->plugin_name, 'delete'); + + } + + //* This function is called, when a website is inserted in the database + function insert($event_name, $data) { + global $app, $conf; + + $this->action = 'insert'; + // just run the update function + $this->update($event_name, $data); + + } + + //* This function is called, when a website is updated in the database + function update($event_name, $data) + { + global $app, $conf; + + if ($data["new"]["php_fpm_chroot"] != 'y') { + if ($this->action == 'update' && $data['old']['document_root'] != '' && $data['new']['document_root'] != $data['old']['document_root']) { + $this->remove_old_mount_mysql($data); + } + return 0; + } + if( file_exists("/var/run/mysqld")) { + if ($this->action == 'update' && $data['old']['document_root'] != '' && $data['new']['document_root'] != $data['old']['document_root']) { + $this->remove_old_mount_mysql($data); + } + + $fstab_line = '/var/run/mysqld ' . $data['new']['document_root'] . '/var/run/mysqld none bind,nobootwait 0 0'; + $app->system->replaceLine('/etc/fstab', $fstab_line, $fstab_line, 0, 1); + + $command = 'mount --bind /var/run/mysqld ' . escapeshellarg($data['new']['document_root']) . '/var/run/mysqld/'; + exec($command); + } + + $this->parent_domain = $data["new"]; + $parent_domain = $data["new"]; + if(!$app->system->is_allowed_user($parent_domain['system_user'], true, true) + || !$app->system->is_allowed_group($parent_domain['system_group'], true, true)) { + $app->log("Websites cannot be owned by the root user or group.", LOGLEVEL_WARN); + return false; + } + + $app->uses('system'); + + + if($app->system->is_user($parent_domain['system_user'])) { + + /** + * Setup Jailkit Chroot System If Enabled + */ + + $app->log("Jailkit Plugin (PHP-FPM Chroot) -> setting up jail", LOGLEVEL_DEBUG); + // load the server configuration options + + $app->uses("getconf"); + $this->data = $data; + $this->app = $app; + $this->jailkit_config = $app->getconf->get_server_config($conf["server_id"], 'jailkit'); + + $this->_update_website_security_level(); + + $app->system->web_folder_protection($parent_domain['document_root'], false); + + $this->_setup_jailkit_chroot(); + $this->_add_jailkit_user(); + + $this->_update_website_security_level(); + $app->system->web_folder_protection($parent_domain['document_root'], true); + + + $app->log("Jailkit Plugin (PHP-FPM Chroot) -> update username:".$parent_domain['system_user'], LOGLEVEL_DEBUG); + + } else { + $app->log("Jailkit Plugin (PHP-FPM Chroot) -> update username:".$parent_domain['system_user']." skipped, the user does not exist.", LOGLEVEL_WARN); + } + } + + function remove_old_mount_mysql($data){ + global $app, $conf; + + if(!file_exists("/var/run/mysqld")){ + return; + } + $fstab_line = '/var/run/mysqld ' . $data['old']['document_root'] . '/var/run/mysqld none bind,nobootwait 0 0'; + + $app->system->removeLine('/etc/fstab', $fstab_line); + + $command = 'umount '.escapeshellarg($data['old']['document_root']).'/var/run/mysqld/'; + exec($command); + } + + //* This function is called, when a website is deleted in the database + function delete($event_name, $data) { + global $app, $conf; + + $this->remove_old_mount_mysql($data); + } + + function _setup_jailkit_chroot() + { + global $app; + + //check if the chroot environment is created yet if not create it with a list of program sections from the config + if (!is_dir($this->parent_domain['document_root'].'/etc/jailkit')) + { + $command = '/usr/local/ispconfig/server/scripts/create_jailkit_chroot.sh'; + $command .= ' '.escapeshellcmd($this->parent_domain['document_root']); + $command .= ' \''.$this->jailkit_config['jailkit_chroot_app_sections'].'\''; + exec($command.' 2>/dev/null'); + + $this->app->log("Added jailkit chroot with command: ".$command, LOGLEVEL_DEBUG); + + //$this->_add_jailkit_programs(); // done later on + + $this->app->load('tpl'); + + $tpl = new tpl(); + $tpl->newTemplate("bash.bashrc.master"); + + $tpl->setVar('jailkit_chroot', true); + $tpl->setVar('domain', $this->parent_domain['domain']); + $tpl->setVar('home_dir', $this->_get_home_dir("")); + + $bashrc = escapeshellcmd($this->parent_domain['document_root']).'/etc/bash.bashrc'; + if(@is_file($bashrc) || @is_link($bashrc)) unlink($bashrc); + + $app->system->file_put_contents($bashrc, $tpl->grab()); + unset($tpl); + + $this->app->log('Added bashrc script: '.$bashrc, LOGLEVEL_DEBUG); + + $tpl = new tpl(); + $tpl->newTemplate('motd.master'); + + $tpl->setVar('domain', $this->parent_domain['domain']); + + $motd = escapeshellcmd($this->parent_domain['document_root']).'/var/run/motd'; + if(@is_file($motd) || @is_link($motd)) unlink($motd); + + $app->system->file_put_contents($motd, $tpl->grab()); + + } + $this->_add_jailkit_programs(); + } + + function _add_jailkit_programs() + { + global $app; + + //copy over further programs and its libraries + $jailkit_chroot_app_programs = preg_split("/[\s,]+/", $this->jailkit_config['jailkit_chroot_app_programs']); + if(is_array($jailkit_chroot_app_programs) && !empty($jailkit_chroot_app_programs)){ + foreach($jailkit_chroot_app_programs as $jailkit_chroot_app_program){ + $jailkit_chroot_app_program = trim($jailkit_chroot_app_program); + if(is_file($jailkit_chroot_app_program) || is_dir($jailkit_chroot_app_program)){ + //copy over further programs and its libraries + $command = '/usr/local/ispconfig/server/scripts/create_jailkit_programs.sh'; + $command .= ' '.escapeshellcmd($this->data['new']['dir']); + $command .= ' '.$jailkit_chroot_app_program; + exec($command.' 2>/dev/null'); + + $this->app->log("Added programs to jailkit chroot with command: ".$command, LOGLEVEL_DEBUG); + } + } + } + +// $command = '/usr/local/ispconfig/server/scripts/create_jailkit_programs.sh'; +// $command .= ' '.escapeshellcmd($this->parent_domain['document_root']); +// $command .= ' \''.$this->jailkit_config['jailkit_chroot_cron_programs'].'\''; +// exec($command.' 2>/dev/null'); +// +// $this->app->log("Added cron programs to jailkit chroot with command: ".$command, LOGLEVEL_DEBUG); + } + + function _add_jailkit_user() + { + global $app; + + //add the user to the chroot + $jailkit_chroot_userhome = $this->_get_home_dir($this->parent_domain['system_user']); + + if(!is_dir($this->parent_domain['document_root'].'/etc')) mkdir($this->parent_domain['document_root'].'/etc'); + if(!is_file($this->parent_domain['document_root'].'/etc/passwd')) exec('touch '.$this->parent_domain['document_root'].'/etc/passwd'); + + // IMPORTANT! + // ALWAYS create the user. Even if the user was created before + // if we check if the user exists, then a update (no shell -> jailkit) will not work + // and the user has FULL ACCESS to the root of the server! + $command = '/usr/local/ispconfig/server/scripts/create_jailkit_user.sh'; + $command .= ' '.escapeshellcmd($this->parent_domain['system_user']); + $command .= ' '.escapeshellcmd($this->parent_domain['document_root']); + $command .= ' '.$jailkit_chroot_userhome; + $command .= ' '.escapeshellcmd("/bin/bash"); + exec($command.' 2>/dev/null'); + + $this->app->log("Added jailkit user to chroot with command: ".$command, LOGLEVEL_DEBUG); + + $app->system->mkdir(escapeshellcmd($this->parent_domain['document_root'].$jailkit_chroot_userhome), 0755, true); + $app->system->chown(escapeshellcmd($this->parent_domain['document_root'].$jailkit_chroot_userhome), escapeshellcmd($this->parent_domain['system_user'])); + $app->system->chgrp(escapeshellcmd($this->parent_domain['document_root'].$jailkit_chroot_userhome), escapeshellcmd($this->parent_domain['system_group'])); + + } + + function _get_home_dir($username) + { + return str_replace("[username]", escapeshellcmd($username), $this->jailkit_config["jailkit_chroot_home"]); + } + + //* Update the website root directory permissions depending on the security level + function _update_website_security_level() { + global $app, $conf; + + // load the server configuration options + $app->uses("getconf"); + $web_config = $app->getconf->get_server_config($conf["server_id"], 'web'); + + $web = $this->parent_domain['new']; + + //* If the security level is set to high + if($web_config['security_level'] == 20 && is_array($web)) { + $app->system->web_folder_protection($web["document_root"], false); + $app->system->chmod($web["document_root"], 0755); + $app->system->chown($web["document_root"], 'root'); + $app->system->chgrp($web["document_root"], 'root'); + $app->system->web_folder_protection($web["document_root"], true); + } + } + + //* Wrapper for exec function for easier debugging + private function _exec($command) { + global $app; + $app->log('exec: '.$command, LOGLEVEL_DEBUG); + exec($command); + } + + + +} // end class + +?> diff --git a/server/scripts/create_jailkit_chroot.sh b/server/scripts/create_jailkit_chroot.sh index cc39b6e4cf1767d9a93c4db5b657c46c06cce891..9ddfb3834809fdf14824c25d381c1f8d0b17aae4 100755 --- a/server/scripts/create_jailkit_chroot.sh +++ b/server/scripts/create_jailkit_chroot.sh @@ -44,3 +44,8 @@ if [ -e "/var/run/mysqld/mysqld.sock" ] then ln /var/run/mysqld/mysqld.sock $CHROOT_HOMEDIR/var/run/mysqld/mysqld.sock fi +# copy zone info +if [ ! -d "$CHROOT_HOMEDIR/usr/share/zoneinfo" ] +then + jk_cp -v -j $CHROOT_HOMEDIR/ /usr/share/zoneinfo +fi \ No newline at end of file