Skip to content

Commit 5454f62

Browse files
committed
track hash of jailkit sections/apps to avoid unnecessary jail rebuilds
1 parent aaf963a commit 5454f62

File tree

7 files changed

+512
-40
lines changed

7 files changed

+512
-40
lines changed

install/sql/incremental/upd_dev_collection.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ ALTER TABLE `web_domain` ADD `jailkit_chroot_app_sections` mediumtext NULL DEFA
22
ALTER TABLE `web_domain` ADD `jailkit_chroot_app_programs` mediumtext NULL DEFAULT NULL;
33
ALTER TABLE `web_domain` ADD `delete_unused_jailkit` enum('n','y') NOT NULL DEFAULT 'n';
44
ALTER TABLE `web_domain` ADD `last_jailkit_update` date NOT NULL DEFAULT FROM_UNIXTIME(0);
5+
ALTER TABLE `web_domain` ADD `last_jailkit_hash` varchar(255) DEFAULT NULL;

install/sql/ispconfig3.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,7 @@ CREATE TABLE `web_domain` (
20882088
`jailkit_chroot_app_programs` mediumtext NULL DEFAULT NULL,
20892089
`delete_unused_jailkit` enum('n','y') NOT NULL default 'n',
20902090
`last_jailkit_update` date NULL DEFAULT NULL,
2091+
`last_jailkit_hash` varchar(255) DEFAULT NULL,
20912092
PRIMARY KEY (`domain_id`),
20922093
UNIQUE KEY `serverdomain` ( `server_id` , `ip_address`, `domain` )
20932094
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

server/lib/classes/cron.d/600-jailkit_maintenance.inc.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function onRunJob() {
7575
// limit the number of jails we update at one time according to time of day
7676
$num_jails_to_update = (date('H') < 6) ? 25 : 3;
7777

78-
$sql = "SELECT domain_id, domain, document_root, php_fpm_chroot, jailkit_chroot_app_sections, jailkit_chroot_app_programs, delete_unused_jailkit FROM web_domain WHERE type = 'vhost' AND last_jailkit_update < (NOW() - INTERVAL 24 HOUR) AND server_id = ? ORDER by last_jailkit_update LIMIT ?";
78+
$sql = "SELECT domain_id, domain, document_root, php_fpm_chroot, jailkit_chroot_app_sections, jailkit_chroot_app_programs, delete_unused_jailkit, last_jailkit_hash FROM web_domain WHERE type = 'vhost' AND last_jailkit_update < (NOW() - INTERVAL 24 HOUR) AND server_id = ? ORDER by last_jailkit_update LIMIT ?";
7979
$records = $app->db->queryAllRecords($sql, $conf['server_id'], $num_jails_to_update);
8080

8181
foreach($records as $rec) {
@@ -101,19 +101,32 @@ public function onRunJob() {
101101
if (isset($web['jailkit_chroot_app_programs']) && $web['jailkit_chroot_app_programs'] != '') {
102102
$programs = $web['jailkit_chroot_app_programs'];
103103
}
104-
$app->system->web_folder_protection($rec['document_root'], false);
105-
$app->system->update_jailkit_chroot($rec['document_root'], $sections, $programs, $update_options);
106-
$app->system->web_folder_protection($rec['document_root'], true);
104+
$programs .= ' '.$jailkit_config['jailkit_chroot_cron_programs'];
105+
106+
$last_updated = preg_split('/[\s,]+/', $sections.' '.$programs);
107+
$last_updated = array_unique($last_updated, SORT_REGULAR);
108+
sort($last_updated, SORT_STRING);
109+
$update_hash = hash('md5', implode(' ', $last_updated));
110+
111+
if ($update_hash != $rec['last_jailkit_hash']) {
112+
$app->system->web_folder_protection($rec['document_root'], false);
113+
$app->system->update_jailkit_chroot($rec['document_root'], $sections, $programs, $update_options);
114+
$app->system->web_folder_protection($rec['document_root'], true);
115+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = ? WHERE `document_root` = ?", $update_hash, $rec['document_root']);
116+
} else {
117+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW() WHERE `document_root` = ?", $rec['document_root']);
118+
}
107119
} elseif ($rec['delete_unused_jailkit'] == 'y') {
108120
//$app->log('Removing unused jail: '.$rec['document_root'], LOGLEVEL_DEBUG);
109121
print 'Removing unused jail: '.$rec['document_root']."\n";
110122
$app->system->web_folder_protection($rec['document_root'], false);
111123
$app->system->delete_jailkit_chroot($rec['document_root']);
112124
$app->system->web_folder_protection($rec['document_root'], true);
113-
}
114125

115-
// might need to update master db here? checking....
116-
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW() WHERE `document_root` = ?", $rec['document_root']);
126+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = NULL WHERE `document_root` = ?", $rec['document_root']);
127+
} else {
128+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW() WHERE `document_root` = ?", $rec['document_root']);
129+
}
117130
}
118131

119132
parent::onRunJob();

server/plugins-available/apache2_plugin.inc.php

Lines changed: 222 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class apache2_plugin {
3737
var $action = '';
3838
var $ssl_certificate_changed = false;
3939
var $update_letsencrypt = false;
40+
var $website = null;
41+
var $jailkit_config = null;
4042

4143
//* This function is called during ispconfig installation to determine
4244
// if a symlink shall be created for this plugin.
@@ -797,6 +799,77 @@ function update($event_name, $data) {
797799
$app->system->chgrp($data['new']['document_root'].'/private', $groupname);
798800
}
799801

802+
// load jailkit server config
803+
$jailkit_config = $app->getconf->get_server_config($conf['server_id'], 'jailkit');
804+
805+
// website overrides
806+
if (isset($data['new']['jailkit_chroot_app_sections']) && $data['new']['jailkit_chroot_app_sections'] != '' ) {
807+
$jailkit_config['jailkit_chroot_app_sections'] = $data['new']['jailkit_chroot_app_sections'];
808+
}
809+
if (isset($data['new']['jailkit_chroot_app_programs']) && $data['new']['jailkit_chroot_app_programs'] != '' ) {
810+
$jailkit_config['jailkit_chroot_app_programs'] = $data['new']['jailkit_chroot_app_programs'];
811+
}
812+
813+
$last_updated = preg_split('/[\s,]+/', $jailkit_config['jailkit_chroot_app_sections']
814+
.' '.$jailkit_config['jailkit_chroot_app_programs']
815+
.' '.$jailkit_config['jailkit_chroot_cron_programs']);
816+
$last_updated = array_unique($last_updated, SORT_REGULAR);
817+
sort($last_updated, SORT_STRING);
818+
$update_hash = hash('md5', implode(' ', $last_updated));
819+
820+
// Create jailkit chroot when enabling php_fpm_chroot
821+
if($data['new']['php_fpm_chroot'] == 'y' && $data['old']['php_fpm_chroot'] != 'y') {
822+
$website = $app->db->queryOneRecord('SELECT * FROM web_domain WHERE domain_id = ?', $data['new']['domain_id']);
823+
$this->website = array_merge($website, $data['new'], array('new_jailkit_hash' => $update_hash));
824+
$this->jailkit_config = $jailkit_config;
825+
$this->_setup_jailkit_chroot();
826+
$this->_add_jailkit_user();
827+
$check_for_jailkit_updates=false;
828+
// else delete if unused
829+
} elseif ($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php_fpm_chroot'] != 'y') {
830+
$check_for_jailkit_updates=false;
831+
$this->_delete_jailkit_if_unused($data['new']['domain_id']);
832+
if(is_dir($data['new']['document_root'].'/etc/jailkit')) {
833+
$check_for_jailkit_updates=true;
834+
}
835+
// else update if needed
836+
} elseif ($data['new']['delete_unused_jailkit'] != 'y') {
837+
$check_for_jailkit_updates=true;
838+
}
839+
840+
// If jail exists (and wasn't deleted), we may need to update it
841+
if($check_for_jailkit_updates &&
842+
( ($data['old']['jailkit_chroot_app_sections'] != $data['new']['jailkit_chroot_app_sections']) ||
843+
($data['old']['jailkit_chroot_app_programs'] != $data['new']['jailkit_chroot_app_programs']) ) )
844+
{
845+
846+
if (isset($jailkit_config['jailkit_hardlinks'])) {
847+
if ($jailkit_config['jailkit_hardlinks'] == 'yes') {
848+
$options = array( 'hardlink', );
849+
} elseif ($jailkit_config['jailkit_hardlinks'] == 'no') {
850+
$options = array();
851+
}
852+
} else {
853+
$options = array( 'allow_hardlink', );
854+
}
855+
856+
$options[] = 'force';
857+
858+
$sections = $jailkit_config['jailkit_chroot_app_sections'];
859+
$programs = $jailkit_config['jailkit_chroot_app_programs'] . ' '
860+
. $jailkit_config['jailkit_chroot_cron_programs'];
861+
862+
// don't update if last_jailkit_hash is the same
863+
$tmp = $app->db->queryOneRecord('SELECT `last_jailkit_hash` FROM web_domain WHERE domain_id = ?', $data['new']['parent_domain_id']);
864+
if ($update_hash != $tmp['last_jailkit_hash']) {
865+
$app->system->update_jailkit_chroot($data['new']['document_root'], $sections, $programs, $options);
866+
867+
// this gets last_jailkit_update out of sync with master db, but that is ok,
868+
// as it is only used as a timestamp to moderate the frequency of updating on the slaves
869+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = ? WHERE `document_root` = ?", $update_hash, $data['new']['document_root']);
870+
}
871+
unset($tmp);
872+
}
800873

801874
// Remove the symlink for the site, if site is renamed
802875
if($this->action == 'update' && $data['old']['domain'] != '' && $data['new']['domain'] != $data['old']['domain']) {
@@ -1176,7 +1249,6 @@ function update($event_name, $data) {
11761249
//* Create custom php.ini
11771250
if(trim($data['new']['custom_php_ini']) != '') {
11781251
$has_custom_php_ini = true;
1179-
$custom_sendmail_path = false;
11801252
if(!is_dir($custom_php_ini_dir)) $app->system->mkdirpath($custom_php_ini_dir);
11811253

11821254
$php_ini_content = $this->get_master_php_ini_content($data['new']);
@@ -1201,13 +1273,6 @@ function update($event_name, $data) {
12011273
}
12021274
}
12031275

1204-
$custom_sendmail_path = false;
1205-
$line = strtok($php_ini_content, '\n');
1206-
while ($line !== false) {
1207-
if (strpos($line, 'sendmail_path') === 0) $custom_sendmail_path = true;
1208-
$line = strtok('\n');
1209-
}
1210-
12111276
$app->system->file_put_contents($custom_php_ini_dir.'/php.ini', $php_ini_content);
12121277
} else {
12131278
$has_custom_php_ini = false;
@@ -1252,7 +1317,7 @@ function update($event_name, $data) {
12521317
$trans = array(
12531318
'{DOCROOT}' => $vhost_data['web_document_root_www'],
12541319
'{DOCROOT_CLIENT}' => $vhost_data['web_document_root'],
1255-
'{DOMAIN}' => $vhost_data['domain']
1320+
'{DOMAIN}' => $vhost_data['domain']
12561321
);
12571322
$vhost_data['apache_directives'] = strtr($vhost_data['apache_directives'], $trans);
12581323

@@ -1317,8 +1382,6 @@ function update($event_name, $data) {
13171382
$vhost_data['seo_redirect_enabled'] = 0;
13181383
}
13191384

1320-
$vhost_data['custom_sendmail_path'] = (isset($custom_sendmail_path) && $custom_sendmail_path) ? 'y' : 'n';
1321-
13221385
$tpl->setVar($vhost_data);
13231386
$tpl->setVar('apache_version', $app->system->getapacheversion());
13241387

@@ -3376,7 +3439,6 @@ private function php_fpm_pool_update ($data, $web_config, $pool_dir, $pool_name,
33763439
}
33773440

33783441
$custom_session_save_path = false;
3379-
$custom_sendmail_path = false;
33803442
if($custom_php_ini_settings != ''){
33813443
// Make sure we only have Unix linebreaks
33823444
$custom_php_ini_settings = str_replace("\r\n", "\n", $custom_php_ini_settings);
@@ -3394,7 +3456,6 @@ private function php_fpm_pool_update ($data, $web_config, $pool_dir, $pool_name,
33943456
if($value != ''){
33953457
$key = trim($key);
33963458
if($key == 'session.save_path') $custom_session_save_path = true;
3397-
if($key == 'sendmail_path') $custom_sendmail_path = true;
33983459
switch (strtolower($value)) {
33993460
case '0':
34003461
// PHP-FPM might complain about invalid boolean value if you use 0
@@ -3417,7 +3478,6 @@ private function php_fpm_pool_update ($data, $web_config, $pool_dir, $pool_name,
34173478
}
34183479

34193480
$tpl->setVar('custom_session_save_path', ($custom_session_save_path ? 'y' : 'n'));
3420-
$tpl->setVar('custom_sendmail_path', ($custom_sendmail_path ? 'y' : 'n'));
34213481

34223482
$tpl->setLoop('custom_php_ini_settings', $final_php_ini_settings);
34233483

@@ -3632,6 +3692,154 @@ private function get_seo_redirects($web, $prefix = ''){
36323692
return $seo_redirects;
36333693
}
36343694

3695+
function _setup_jailkit_chroot()
3696+
{
3697+
global $app;
3698+
3699+
$app->uses('system');
3700+
3701+
if (isset($this->jailkit_config) && isset($this->jailkit_config['jailkit_hardlinks'])) {
3702+
if ($this->jailkit_config['jailkit_hardlinks'] == 'yes') {
3703+
$options = array( 'hardlink', );
3704+
} elseif ($this->jailkit_config['jailkit_hardlinks'] == 'no') {
3705+
$options = array();
3706+
}
3707+
} else {
3708+
$options = array( 'allow_hardlink', );
3709+
}
3710+
3711+
// should move return here if $this->website['new_jailkit_hash'] == $this->website['last_jailkit_hash'] ?
3712+
3713+
// check if the chroot environment is created yet if not create it with a list of program sections from the config
3714+
if (!is_dir($this->website['document_root'].'/etc/jailkit'))
3715+
{
3716+
$app->system->create_jailkit_chroot($this->website['document_root'], $this->jailkit_config['jailkit_chroot_app_sections'], $options);
3717+
$this->app->log("Added jailkit chroot", LOGLEVEL_DEBUG);
3718+
3719+
$this->_add_jailkit_programs($options);
3720+
3721+
$this->app->load('tpl');
3722+
3723+
$tpl = new tpl();
3724+
$tpl->newTemplate("bash.bashrc.master");
3725+
3726+
$tpl->setVar('jailkit_chroot', true);
3727+
$tpl->setVar('domain', $this->website['domain']);
3728+
$tpl->setVar('home_dir', $this->_get_home_dir(""));
3729+
3730+
$bashrc = $this->website['document_root'].'/etc/bash.bashrc';
3731+
if(@is_file($bashrc) || @is_link($bashrc)) unlink($bashrc);
3732+
3733+
file_put_contents($bashrc, $tpl->grab());
3734+
unset($tpl);
3735+
3736+
$this->app->log("Added bashrc script: ".$bashrc, LOGLEVEL_DEBUG);
3737+
3738+
$tpl = new tpl();
3739+
$tpl->newTemplate("motd.master");
3740+
3741+
$tpl->setVar('domain', $this->website['domain']);
3742+
3743+
$motd = $this->website['document_root'].'/var/run/motd';
3744+
if(@is_file($motd) || @is_link($motd)) unlink($motd);
3745+
3746+
$app->system->file_put_contents($motd, $tpl->grab());
3747+
3748+
} else {
3749+
// force update existing jails
3750+
$options[] = 'force';
3751+
3752+
$sections = $this->jailkit_config['jailkit_chroot_app_sections'];
3753+
$programs = $this->jailkit_config['jailkit_chroot_app_programs'] . ' '
3754+
. $this->jailkit_config['jailkit_chroot_cron_programs'];
3755+
3756+
if ($this->website['new_jailkit_hash'] == $this->website['last_jailkit_hash']) {
3757+
return;
3758+
}
3759+
3760+
$app->system->update_jailkit_chroot($this->website['document_root'], $sections, $programs, $options);
3761+
}
3762+
3763+
// this gets last_jailkit_update out of sync with master db, but that is ok,
3764+
// as it is only used as a timestamp to moderate the frequency of updating on the slaves
3765+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = ? WHERE `document_root` = ?", $this->website['new_jailkit_hash'], $this->website['document_root']);
3766+
}
3767+
3768+
function _add_jailkit_programs($opts=array())
3769+
{
3770+
global $app;
3771+
3772+
$app->uses('system');
3773+
3774+
//copy over further programs and its libraries
3775+
$app->system->create_jailkit_programs($this->website['document_root'], $this->jailkit_config['jailkit_chroot_app_programs'], $opts);
3776+
$this->app->log("Added app programs to jailkit chroot", LOGLEVEL_DEBUG);
3777+
3778+
$app->system->create_jailkit_programs($this->website['document_root'], $this->jailkit_config['jailkit_chroot_cron_programs'], $opts);
3779+
$this->app->log("Added cron programs to jailkit chroot", LOGLEVEL_DEBUG);
3780+
}
3781+
3782+
function _get_home_dir($username)
3783+
{
3784+
return str_replace("[username]", $username, $this->jailkit_config['jailkit_chroot_home']);
3785+
}
3786+
3787+
function _add_jailkit_user()
3788+
{
3789+
global $app;
3790+
3791+
// add the user to the chroot
3792+
$jailkit_chroot_userhome = $this->_get_home_dir($this->website['system_user']);
3793+
3794+
if(!is_dir($this->website['document_root'].'/etc')) mkdir($this->website['document_root'].'/etc');
3795+
if(!is_file($this->website['document_root'].'/etc/passwd')) $app->system->exec_safe('touch ?', $this->website['document_root'].'/etc/passwd');
3796+
3797+
// IMPORTANT!
3798+
// ALWAYS create the user. Even if the user was created before
3799+
// if we check if the user exists, then a update (no shell -> jailkit) will not work
3800+
// and the user has FULL ACCESS to the root of the server!
3801+
$app->system->create_jailkit_user($this->website['system_user'], $this->website['document_root'], $jailkit_chroot_userhome);
3802+
3803+
$app->system->mkdir($this->website['document_root'].$jailkit_chroot_userhome, 0755, true);
3804+
$app->system->chown($this->website['document_root'].$jailkit_chroot_userhome, $this->website['system_user']);
3805+
$app->system->chgrp($this->website['document_root'].$jailkit_chroot_userhome, $this->website['system_group']);
3806+
3807+
$this->app->log("Added created jailkit user home in : ".$this->website['document_root'].$jailkit_chroot_userhome, LOGLEVEL_DEBUG);
3808+
}
3809+
3810+
private function _delete_jailkit_if_unused($parent_domain_id) {
3811+
global $app, $conf;
3812+
3813+
// get jail directory
3814+
$parent_domain = $app->db->queryOneRecord("SELECT * FROM `web_domain` WHERE `domain_id` = ? OR `parent_domain_id` = ? AND `document_root` IS NOT NULL", $parent_domain_id, $parent_domain_id);
3815+
if (!is_dir($parent_domain['document_root'])) {
3816+
return;
3817+
}
3818+
3819+
// chroot is used by php-fpm
3820+
if (isset($parent_domain['php_fpm_chroot']) && $parent_domain['php_fpm_chroot'] == 'y') {
3821+
return;
3822+
}
3823+
3824+
// check for any shell_user using this jail
3825+
$inuse = $app->db->queryOneRecord('SELECT shell_user_id FROM `shell_user` WHERE `parent_domain_id` = ? AND `chroot` = ?', $parent_domain_id, 'jailkit');
3826+
if($inuse) {
3827+
return;
3828+
}
3829+
3830+
// check for any cron job using this jail
3831+
$inuse = $app->db->queryOneRecord('SELECT id FROM `cron` WHERE `parent_domain_id` = ? AND `type` = ?', $parent_domain_id, 'chrooted');
3832+
if($inuse) {
3833+
return;
3834+
}
3835+
3836+
$app->system->delete_jailkit_chroot($parent_domain['document_root']);
3837+
3838+
// this gets last_jailkit_update out of sync with master db, but that is ok,
3839+
// as it is only used as a timestamp to moderate the frequency of updating on the slaves
3840+
$app->db->query("UPDATE `web_domain` SET `last_jailkit_update` = NOW(), `last_jailkit_hash` = NULL WHERE `document_root` = ?", $parent_domain['document_root']);
3841+
}
3842+
36353843
} // end class
36363844

36373845
?>

0 commit comments

Comments
 (0)