diff --git a/README.md b/README.md index 3f331e8..eee0d07 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a setup for a TOR based shared hosting server. It is provided as is and Installation Instructions: -------------------------- -The configuration was tested with a standard Debian sid and Ubuntu 16.04 LTS installation. It's recommended you install Debian sid on your server, but with a little tweaking you may also get this working on other distributions and/or versions. +The configuration was tested with a standard Debian buster and Ubuntu 18.04 LTS installation. It's recommended you install Debian buster (or newer) on your server, but with a little tweaking you may also get this working on other distributions and/or versions. Uninstall packages that may interfere with this setup: ``` diff --git a/var/www/common.php b/var/www/common.php index 129d459..b6045e3 100644 --- a/var/www/common.php +++ b/var/www/common.php @@ -23,7 +23,7 @@ const INDEX_MD5S=[ //MD5 sums of index.hosting.html files that should be considd const REQUIRE_APPROVAL=false; //require admin approval of new sites? true/false const ENABLE_SHELL_ACCESS=true; //allows users to login via ssh, when disabled only (s)ftp is allowed - run setup.php to migrate existing accounts const ADMIN_PASSWORD='MY_PASSWORD'; //password for admin interface -const SERVICE_INSTANCES=['1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; //one character per instance - run multiple tor+php-fpm instances for load balancing, remove all but one instance if you expect less than 100 accounts. Adding new instances is always possible at a later time, just removing one takes some manual cleanup for now - run setup.php after change +const SERVICE_INSTANCES=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's']; //one character per instance - run multiple tor+php-fpm instances for load balancing, remove all but one instance if you expect less than 200 accounts. - run setup.php after change const DISABLED_PHP_VERSIONS=[]; //php versions still installed on the system but no longer offered for new accounts const PHP_VERSIONS=[4 => '7.3']; //currently active php versions const DEFAULT_PHP_VERSION='7.3'; //default php version @@ -81,7 +81,6 @@ server { } } location /phpmyadmin { - root /usr/share; location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param DOCUMENT_ROOT /html; @@ -113,6 +112,7 @@ const MAX_NUM_USER_DOMAINS = 3; //maximum number of clearnet domains a user may const SKIP_USER_CHROOT_UPDATE = true; //skips updating user chroots when running setup.php const DEFAULT_QUOTA_SIZE = 10 * 1024 * 1024; //per user disk quota in kb - Defaults to 10 GB const DEFAULT_QUOTA_FILES = 100000; //per user file quota - by default allow no more than 100000 files +const NUM_GUARDS = 50; //number of tor guard relays to distribute the load on function get_onion_v2($pkey) : string { $keyData = openssl_pkey_get_details($pkey); @@ -312,36 +312,65 @@ function check_captcha_error() { return false; } -function rewrite_torrc(PDO $db, string $key){ -$torrc="ClientUseIPv6 1 +function rewrite_torrc(PDO $db, string $instance){ + $update_onion=$db->prepare('UPDATE onions SET private_key=? WHERE onion=?;'); + $torrc='ClientUseIPv6 1 ClientUseIPv4 1 SOCKSPort 0 MaxClientCircuitsPending 1024 -NumEntryGuards 15 -NumDirectoryGuards 15 -NumPrimaryGuards 15 -"; - $stmt=$db->prepare('SELECT onions.onion, users.system_account, onions.num_intros, onions.enable_smtp, onions.version, onions.max_streams, onions.enabled FROM onions LEFT JOIN users ON (users.id=onions.user_id) WHERE onions.instance = ? AND onions.enabled IN (1, -2) AND users.id NOT IN (SELECT user_id FROM new_account) AND users.todelete!=1;'); - $stmt->execute([$key]); - while($tmp=$stmt->fetch(PDO::FETCH_NUM)){ -if($tmp[6]==1){ - $socket=$tmp[1]; -}else{ - $socket='suspended'; -} - $torrc.="HiddenServiceDir /var/lib/tor-instances/$key/hidden_service_$tmp[0].onion -HiddenServiceNumIntroductionPoints $tmp[2] -HiddenServiceVersion $tmp[4] +NumEntryGuards '.NUM_GUARDS.' +NumDirectoryGuards '.NUM_GUARDS.' +NumPrimaryGuards '.NUM_GUARDS.' +'; + $stmt=$db->prepare('SELECT onions.onion, users.system_account, onions.num_intros, onions.enable_smtp, onions.version, onions.max_streams, onions.enabled, onions.private_key FROM onions LEFT JOIN users ON (users.id=onions.user_id) WHERE onions.instance = ? AND onions.enabled IN (1, -2) AND users.id NOT IN (SELECT user_id FROM new_account) AND users.todelete!=1;'); + $stmt->execute([$instance]); + while($tmp=$stmt->fetch(PDO::FETCH_ASSOC)){ + if(!file_exists("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion")){ + if($tmp['version']==2){ + //php openssl implementation has some issues, re-export using native openssl + $pkey=openssl_pkey_get_private($tmp['private_key']); + openssl_pkey_export($pkey, $exported); + openssl_pkey_free($pkey); + $priv_key=shell_exec('echo ' . escapeshellarg($exported) . ' | openssl rsa'); + //save hidden service + mkdir("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion", 0700); + file_put_contents("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/private_key", $priv_key); + chmod("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/private_key", 0600); + chown("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/", "_tor-$instance"); + chown("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/private_key", "_tor-$instance"); + chgrp("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/", "_tor-$instance"); + chgrp("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/private_key", "_tor-$instance"); + $update_onion->execute([$priv_key, $tmp['onion']]); + }elseif($tmp['version']==3){ + $priv_key=base64_decode($tmp['private_key']); + //save hidden service + mkdir("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion", 0700); + file_put_contents("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/hs_ed25519_secret_key", $priv_key); + chmod("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/hs_ed25519_secret_key", 0600); + chown("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/", "_tor-$instance"); + chown("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/hs_ed25519_secret_key", "_tor-$instance"); + chgrp("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/", "_tor-$instance"); + chgrp("/var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion/hs_ed25519_secret_key", "_tor-$instance"); + } + } + if($tmp['enabled']==1){ + $socket=$tmp['system_account']; + }else{ + $socket='suspended'; + } + $torrc.="HiddenServiceDir /var/lib/tor-instances/$instance/hidden_service_$tmp[onion].onion +HiddenServiceNumIntroductionPoints $tmp[num_intros] +HiddenServiceVersion $tmp[version] HiddenServiceMaxStreamsCloseCircuit 1 -HiddenServiceMaxStreams $tmp[5] +HiddenServiceMaxStreams $tmp[max_streams] HiddenServicePort 80 unix:/var/run/nginx/$socket "; - if($tmp[3]){ + if($tmp['enable_smtp']){ $torrc.="HiddenServicePort 25\n"; } } - file_put_contents("/etc/tor/instances/$key/torrc", $torrc); - exec("service tor@$key reload"); + file_put_contents("/etc/tor/instances/$instance/torrc", $torrc); + exec('systemctl reload '.escapeshellarg("tor@$instance")); } function private_key_to_onion(string $priv_key) : array { @@ -433,8 +462,27 @@ function ed25519_seckey_expand(string $seed) : string { function rewrite_nginx_config(PDO $db){ $nginx=''; + $rewrites = []; + // rewrite rules + $stmt = $db->query('SELECT user_id, regex, replacement, flag, ifnotexists FROM nginx_rewrites;'); + while($tmp = $stmt->fetch(PDO::FETCH_ASSOC)){ + if(!isset($rewrites[$tmp['user_id']])){ + $rewrites[$tmp['user_id']] = ''; + } + if($tmp['ifnotexists']){ + $rewrites[$tmp['user_id']] .= "if (!-e \$request_filename) {\n\t\t"; + } + $rewrites[$tmp['user_id']] .= "rewrite '$tmp[regex]' '$tmp[replacement]'"; + if(!empty($tmp['flag'])){ + $rewrites[$tmp['user_id']] .= " $tmp[flag]"; + } + $rewrites[$tmp['user_id']] .= ";\n\t"; + if($tmp['ifnotexists']){ + $rewrites[$tmp['user_id']] .= "}\n\t"; + } + } // onions - $stmt=$db->query("SELECT users.system_account, users.php, users.autoindex, onions.onion FROM users INNER JOIN onions ON (onions.user_id=users.id) WHERE onions.enabled IN (1, -2) AND users.id NOT IN (SELECT user_id FROM new_account) AND users.todelete!=1;"); + $stmt=$db->query("SELECT users.system_account, users.php, users.autoindex, onions.onion, users.id FROM users INNER JOIN onions ON (onions.user_id=users.id) WHERE onions.enabled IN (1, -2) AND users.id NOT IN (SELECT user_id FROM new_account) AND users.todelete!=1;"); while($tmp=$stmt->fetch(PDO::FETCH_ASSOC)){ if($tmp['php']>0){ $php_location=" @@ -456,7 +504,11 @@ function rewrite_nginx_config(PDO $db){ error_log /home/$tmp[system_account]/logs/error.log notice; disable_symlinks on from=/home/$tmp[system_account]; autoindex $autoindex; - location / { + "; + if(isset($rewrites[$tmp['id']])){ + $nginx .= $rewrites[$tmp['id']]; + } + $nginx .= "location / { try_files \$uri \$uri/ =404;$php_location } } @@ -464,7 +516,7 @@ function rewrite_nginx_config(PDO $db){ } // clearnet domains - $stmt=$db->query("SELECT users.system_account, users.php, users.autoindex, domains.domain FROM users INNER JOIN domains ON (domains.user_id=users.id) WHERE domains.enabled = 1 AND users.id NOT IN (SELECT user_id FROM new_account) AND users.todelete != 1;"); + $stmt=$db->query("SELECT users.system_account, users.php, users.autoindex, domains.domain, users.id FROM users INNER JOIN domains ON (domains.user_id=users.id) WHERE domains.enabled = 1 AND users.id NOT IN (SELECT user_id FROM new_account) AND users.todelete != 1;"); while($tmp=$stmt->fetch(PDO::FETCH_ASSOC)){ if($tmp['php']>0){ $php_location=" @@ -486,7 +538,11 @@ function rewrite_nginx_config(PDO $db){ error_log /home/$tmp[system_account]/logs/error.log notice; disable_symlinks on from=/home/$tmp[system_account]; autoindex $autoindex; - location / { + "; + if(isset($rewrites[$tmp['id']])){ + $nginx .= $rewrites[$tmp['id']]; + } + $nginx .= "location / { try_files \$uri \$uri/ =404;$php_location } } @@ -504,7 +560,7 @@ function rewrite_nginx_config(PDO $db){ "; } file_put_contents("/etc/nginx/streams-enabled/hosted_sites", $nginx); - exec("service nginx reload"); + exec('systemctl reload nginx'); } function rewrite_php_config(PDO $db, string $key){ @@ -529,7 +585,7 @@ listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = ondemand -pm.max_children = 75 +pm.max_children = 50 pm.process_idle_timeout = 10s; chroot = /home/$tmp[system_account] php_admin_value[memory_limit] = 256M @@ -540,7 +596,7 @@ php_admin_value[session.save_path] = /tmp "; } file_put_contents("/etc/php/$version/fpm/pool.d/$key/www.conf", $php); - exec("service php$version-fpm@$key restart"); + exec('systemctl restart '.escapeshellarg("php$version-fpm@$key")); } } @@ -602,7 +658,7 @@ function get_new_tor_instance(PDO $db){ } function add_user_onion(PDO $db, int $user_id, string $onion, string $priv_key, int $onion_version) { - $stmt=$db->prepare('INSERT INTO onions (user_id, onion, private_key, version, enabled, instance) VALUES (?, ?, ?, ?, 2, ?);'); + $stmt=$db->prepare('INSERT INTO onions (user_id, onion, private_key, version, enabled, enable_smtp, instance) VALUES (?, ?, ?, ?, 1, 0, ?);'); $stmt->execute([$user_id, $onion, $priv_key, $onion_version, get_new_tor_instance($db)]); } diff --git a/var/www/cron.php b/var/www/cron.php index 126ae13..75925d1 100644 --- a/var/www/cron.php +++ b/var/www/cron.php @@ -16,7 +16,6 @@ $db->query('UPDATE service_instances SET reload=0 WHERE reload=1;'); //add new accounts $del=$db->prepare("DELETE FROM new_account WHERE user_id=?;"); -$enable_onion=$db->prepare("UPDATE onions SET enabled=2 WHERE onion=?;"); $approval = REQUIRE_APPROVAL ? 'WHERE new_account.approved=1': ''; $stmt=$db->query("SELECT users.system_account, users.username, new_account.password, users.php, users.autoindex, users.id, onions.onion, users.instance FROM new_account INNER JOIN users ON (users.id=new_account.user_id) INNER JOIN onions ON (onions.user_id=users.id) $approval LIMIT 100;"); while($id=$stmt->fetch(PDO::FETCH_NUM)){ @@ -24,7 +23,6 @@ while($id=$stmt->fetch(PDO::FETCH_NUM)){ $system_account=$id[0]; $instance=$id[7]; $reload[$instance]=true; - $enable_onion->execute([$id[6]]); //add and manage rights of system user $shell = ENABLE_SHELL_ACCESS ? '/bin/bash' : '/usr/sbin/nologin'; exec('useradd -l -p ' . escapeshellarg($id[2]) . ' -g www-data -k /var/www/skel -m -s ' . escapeshellarg($shell) . ' ' . escapeshellarg($system_account)); @@ -44,42 +42,6 @@ while($id=$stmt->fetch(PDO::FETCH_NUM)){ $del->execute([$id[5]]); } -//add hidden services to tor -$update_onion=$db->prepare('UPDATE onions SET private_key=?, enabled=1 WHERE onion=?;'); -$stmt=$db->query('SELECT onion, private_key, version, instance FROM onions WHERE enabled=2;'); -$onions=$stmt->fetchAll(PDO::FETCH_NUM); -foreach($onions as $onion){ - $instance = $onion[3]; - $reload[$instance] = true; - if($onion[2]==2){ - //php openssl implementation has some issues, re-export using native openssl - $pkey=openssl_pkey_get_private($onion[1]); - openssl_pkey_export($pkey, $exported); - openssl_pkey_free($pkey); - $priv_key=shell_exec('echo ' . escapeshellarg($exported) . ' | openssl rsa'); - //save hidden service - mkdir("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion", 0700); - file_put_contents("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/private_key", $priv_key); - chmod("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/private_key", 0600); - chown("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/", "_tor-$instance"); - chown("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/private_key", "_tor-$instance"); - chgrp("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/", "_tor-$instance"); - chgrp("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/private_key", "_tor-$instance"); - $update_onion->execute([$priv_key, $onion[0]]); - }elseif($onion[2]==3){ - $priv_key=base64_decode($onion[1]); - //save hidden service - mkdir("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion", 0700); - file_put_contents("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/hs_ed25519_secret_key", $priv_key); - chmod("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/hs_ed25519_secret_key", 0600); - chown("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/", "_tor-$instance"); - chown("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/hs_ed25519_secret_key", "_tor-$instance"); - chgrp("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/", "_tor-$instance"); - chgrp("/var/lib/tor-instances/$instance/hidden_service_$onion[0].onion/hs_ed25519_secret_key", "_tor-$instance"); - $update_onion->execute([$onion[1], $onion[0]]); - } -} - //delete old accounts $del=$db->prepare("DELETE FROM users WHERE id=?;"); $stmt=$db->query("SELECT system_account, id, mysql_user, instance FROM users WHERE todelete=1 LIMIT 100;"); diff --git a/var/www/html/admin.php b/var/www/html/admin.php index 890a92e..99fcaea 100644 --- a/var/www/html/admin.php +++ b/var/www/html/admin.php @@ -146,13 +146,13 @@ if(empty($_SESSION['logged_in'])){ if($error=check_csrf_error()){ echo '
'.$error.'
'; }elseif(preg_match('~^([a-z2-7]{16}|[a-z2-7]{56})(\.onion)?$~', $onion, $match)){ - $stmt=$db->prepare('SELECT null FROM onions WHERE onion=?;'); + $stmt=$db->prepare('SELECT instance FROM onions WHERE onion=?;'); $stmt->execute([$match[1]]); - if($stmt->fetch(PDO::FETCH_NUM)){ + if($instance=$stmt->fetch(PDO::FETCH_NUM)){ $stmt=$db->prepare('UPDATE onions SET enabled=-2 WHERE onion=?;'); $stmt->execute([$match[1]]); echo "Successfully queued for suspension!
"; - enqueue_instance_reload($db, substr($match[1], 0, 1)); + enqueue_instance_reload($db, $instance[0]); }else{ echo "Onion address not hosted by us!
"; } @@ -179,7 +179,7 @@ if(empty($_SESSION['logged_in'])){ echo ''.$error.'
'; }elseif(preg_match('~^([a-z2-7]{16}|[a-z2-7]{56})(\.onion)?$~', $onion, $match)){ if(isset($_POST['num_intros'])){ - $stmt=$db->prepare('SELECT version FROM onions WHERE onion=?;'); + $stmt=$db->prepare('SELECT version, instance FROM onions WHERE onion=?;'); $stmt->execute([$match[1]]); if($onion=$stmt->fetch(PDO::FETCH_NUM)){ $stmt=$db->prepare('UPDATE onions SET enabled = ?, enable_smtp = ?, num_intros = ?, max_streams = ? WHERE onion=?;'); @@ -200,7 +200,7 @@ if(empty($_SESSION['logged_in'])){ $max_streams = 65535; } $stmt->execute([$enabled, $enable_smtp, $num_intros, $max_streams, $match[1]]); - enqueue_instance_reload($db, substr($match[1], 0, 1)); + enqueue_instance_reload($db, $onion[1]); echo "Changes successfully saved!
"; } } diff --git a/var/www/html/files.php b/var/www/html/files.php index 2001bf6..d53c53c 100644 --- a/var/www/html/files.php +++ b/var/www/html/files.php @@ -242,12 +242,14 @@ if(!empty($_FILES['files'])){ $files=$dirs=[]; $list=ftp_rawlist($ftp, '.'); -foreach($list as $file){ - preg_match('/^([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+(.*)$/', $file, $match); - if($match[0][0]==='d'){ - $dirs[$match[9]]=['name'=>"$match[9]/", 'mtime'=>strtotime("$match[6] $match[7] $match[8]"), 'size'=>'-']; - }else{ - $files[$match[9]]=['name'=>$match[9], 'mtime'=>ftp_mdtm($ftp, $match[9]), 'size'=>$match[5]]; +if(is_array($list)){ + foreach($list as $file){ + preg_match('/^([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+(.*)$/', $file, $match); + if($match[0][0]==='d'){ + $dirs[$match[9]]=['name'=>"$match[9]/", 'mtime'=>strtotime("$match[6] $match[7] $match[8]"), 'size'=>'-']; + }else{ + $files[$match[9]]=['name'=>$match[9], 'mtime'=>ftp_mdtm($ftp, $match[9]), 'size'=>$match[5]]; + } } } diff --git a/var/www/setup.php b/var/www/setup.php index 9b99741..1c923f3 100644 --- a/var/www/setup.php +++ b/var/www/setup.php @@ -30,6 +30,7 @@ if(!@$version=$db->query("SELECT value FROM settings WHERE setting='version';")) $db->exec("CREATE TABLE onions (user_id int(11) NULL, onion varchar(56) COLLATE latin1_bin NOT NULL PRIMARY KEY, private_key varchar(1000) COLLATE latin1_bin NOT NULL, version tinyint(1) NOT NULL, enabled tinyint(1) NOT NULL DEFAULT '1', num_intros tinyint(3) NOT NULL DEFAULT '3', enable_smtp tinyint(1) NOT NULL DEFAULT '1', max_streams tinyint(3) unsigned NOT NULL DEFAULT '20', instance char(1) NOT NULL DEFAULT '2', KEY user_id (user_id), KEY enabled (enabled), KEY instance(instance), CONSTRAINT onions_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE, CONSTRAINT instance_ibfk_1 FOREIGN KEY (instance) REFERENCES service_instances (id) ON DELETE RESTRICT ON UPDATE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin;"); $db->exec("CREATE TABLE domains (user_id int(11) NULL, domain varchar(255) COLLATE latin1_bin NOT NULL PRIMARY KEY, enabled tinyint(1) NOT NULL DEFAULT '1', KEY user_id (user_id), KEY enabled (enabled), CONSTRAINT domains_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin;"); $db->exec('CREATE TABLE disk_quota (user_id int(11) NOT NULL, quota_size int(10) unsigned NOT NULL, quota_files int(10) unsigned NOT NULL, updated tinyint(1) NOT NULL DEFAULT 1, KEY user_id (user_id), KEY updated (updated), CONSTRAINT disk_quota_ibfk_2 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin;'); + $db->exec('CREATE TABLE nginx_rewrites (id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id int(11) NOT NULL, `regex` varchar(255) NOT NULL, replacement varchar(255) NOT NULL, `flag` varchar(9) NOT NULL, ifnotexists tinyint(1) NOT NULL, CONSTRAINT nginx_rewrites_ibfk_2 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'); $db->exec('CREATE TABLE settings (setting varchar(50) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL PRIMARY KEY, value text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin;'); $stmt=$db->prepare("INSERT INTO settings (setting, value) VALUES ('version', ?);"); $stmt->execute([DBVERSION]); @@ -150,6 +151,10 @@ if(!@$version=$db->query("SELECT value FROM settings WHERE setting='version';")) $stmt = $db->prepare('INSERT INTO disk_quota (user_id, quota_size, quota_files) SELECT id, ?, ? FROM users;'); $stmt->execute([DEFAULT_QUOTA_SIZE, DEFAULT_QUOTA_FILES]); } + if($version<16){ + $db->exec('UPDATE onions SET enabled=1 WHERE enabled=2;'); + $db->exec('CREATE TABLE nginx_rewrites (id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id int(11) NOT NULL, `regex` varchar(255) NOT NULL, replacement varchar(255) NOT NULL, `flag` varchar(9) NOT NULL, ifnotexists tinyint(1) NOT NULL, CONSTRAINT nginx_rewrites_ibfk_2 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;'); + } $stmt=$db->prepare("UPDATE settings SET value=? WHERE setting='version';"); $stmt->execute([DBVERSION]); } @@ -253,7 +258,7 @@ php_admin_value[open_basedir] = /html/adminer:/tmp mkdir("/etc/php/$version/fpm/pool.d/", 0755, true); } file_put_contents("/etc/php/$version/fpm/pool.d/www.conf", $pool_config); - exec("service php$version-fpm@default reload"); + exec('systemctl reload '.escapeshellarg("php$version-fpm@default")); } echo "Updating chroots, this might take a while…\n"; exec('/var/www/setup_chroot.sh /var/www'); @@ -275,10 +280,32 @@ file_put_contents('/etc/nginx/streams-enabled/default', "server { listen unix:/var/www/var/run/mysqld/mysqld.sock; proxy_pass unix:/var/run/mysqld/mysqld.sock; }"); -exec("service nginx reload"); +exec('systemctl reload nginx'); $stmt=$db->prepare('INSERT IGNORE INTO service_instances (id) VALUES (?);'); foreach(SERVICE_INSTANCES as $key){ $stmt->execute([$key]); } +$stmt=$db->query('SELECT id FROM service_instances;'); +$update_users=$db->prepare('UPDATE users SET instance = (SELECT id FROM service_instances WHERE id !=? ORDER BY RAND() limit 1) WHERE instance=?;'); +$update_onions=$db->prepare('UPDATE onions SET instance = (SELECT id FROM service_instances WHERE id !=? ORDER BY RAND() limit 1) WHERE instance=?;'); +$drop_instance=$db->prepare('DELETE FROM service_instances WHERE id=?;'); +while($tmp=$stmt->fetch(PDO::FETCH_NUM)){ + if(!in_array($tmp[0], SERVICE_INSTANCES)){ + exec('systemctl stop '.escapeshellarg("tor@$tmp[0]")); + exec('systemctl disable '.escapeshellarg("tor@$tmp[0]")); + exec('rm -r '.escapeshellarg("/var/lib/tor-instances/$tmp[0]/")); + exec('rm -r '.escapeshellarg("/etc/tor/instances/$tmp[0]/")); + exec('userdel '.escapeshellarg("_tor-$tmp[0]")); + foreach(PHP_VERSIONS as $version){ + exec('systemctl stop '.escapeshellarg("php$version-fpm@$tmp[0]")); + exec('systemctl disable '.escapeshellarg("php$version-fpm@$tmp[0]")); + exec('rm -r '.escapeshellarg("/etc/php/$version/fpm/pool.d/$tmp[0]/")); + unlink("/etc/php/$version/fpm/php-fpm-".basename($tmp[0]).'.conf'); + } + $update_users->execute([$tmp[0], $tmp[0]]); + $update_onions->execute([$tmp[0], $tmp[0]]); + $drop_instance->execute($tmp); + } +} $db->exec('UPDATE service_instances SET reload=1;'); echo "Done - Database and files have been updated to the latest version :)\n"; diff --git a/var/www/setup_chroot.sh b/var/www/setup_chroot.sh index bf077db..7174444 100755 --- a/var/www/setup_chroot.sh +++ b/var/www/setup_chroot.sh @@ -15,33 +15,31 @@ function CHROOT_BINARY() { if [ "$LIB_FILES" != "" ]; then for LIB_FILE in $LIB_FILES; do LIB_DIRECTORY="$(dirname $LIB_FILE)" - test -d $CHROOT_DIRECTORY$LIB_DIRECTORY || mkdir -pm 0555 $CHROOT_DIRECTORY$LIB_DIRECTORY - diff $LIB_FILE $CHROOT_DIRECTORY$LIB_FILE > /dev/null 2>&1 || cp $LIB_FILE $CHROOT_DIRECTORY$LIB_FILE + mkdir -pm 0555 $CHROOT_DIRECTORY$LIB_DIRECTORY + cp $LIB_FILE $CHROOT_DIRECTORY$LIB_FILE chmod 0555 $CHROOT_DIRECTORY$LIB_FILE done fi if [ "$LDD_FILES" != "" ]; then for LDD_FILE in $LDD_FILES; do LDD_DIRECTORY="$(dirname $LDD_FILE)" - test -d $CHROOT_DIRECTORY$LDD_DIRECTORY || mkdir -pm 0555 $CHROOT_DIRECTORY${LDD_DIRECTORY} - diff $LDD_FILE $CHROOT_DIRECTORY$LDD_FILE > /dev/null 2>&1 || cp $LDD_FILE $CHROOT_DIRECTORY$LDD_FILE + mkdir -pm 0555 $CHROOT_DIRECTORY${LDD_DIRECTORY} + cp $LDD_FILE $CHROOT_DIRECTORY$LDD_FILE chmod 0555 $CHROOT_DIRECTORY$LDD_FILE done fi - diff $BINARY $CHROOT_DIRECTORY/$BINARY > /dev/null 2>&1 || cp $BINARY $CHROOT_DIRECTORY/$BINARY + cp $BINARY $CHROOT_DIRECTORY/$BINARY chmod 0555 $CHROOT_DIRECTORY/$BINARY } function CHROOT_FILE() { - diff $1 $CHROOT_DIRECTORY/$1 > /dev/null 2>&1 || cp $1 $CHROOT_DIRECTORY/$1 + cp $1 $CHROOT_DIRECTORY/$1 } function CHROOT_DIRECTORY() { - test -d $CHROOT_DIRECTORY/$1 || mkdir -pm 0555 $CHROOT_DIRECTORY/$1 - diff -r $1 $CHROOT_DIRECTORY/$1 > /dev/null 2>&1 || { - test ! -d $CHROOT_DIRECTORY/$1 || rm -rf $CHROOT_DIRECTORY/$1/ > /dev/null 2>&1 - cp -Rp $1 $CHROOT_DIRECTORY/$1 - } + mkdir -pm 0555 $CHROOT_DIRECTORY/$1 + rm -rf $CHROOT_DIRECTORY/$1/ > /dev/null 2>&1 + cp -Rp $1 $CHROOT_DIRECTORY/$1 } ### variables @@ -204,6 +202,9 @@ echo "root:x:0:0:root:/root:/bin/bash" > $CHROOT_DIRECTORY/etc/passwd echo "www-data:x:33:33::/var/www:/bin/bash" >> $CHROOT_DIRECTORY/etc/passwd echo "root:x:0:" > $CHROOT_DIRECTORY/etc/group echo "www-data:x:33:www-data" >> $CHROOT_DIRECTORY/etc/group +echo "export HOME=/" > $CHROOT_DIRECTORY/etc/profile.d/hosting.sh +echo "export HISTFILE=/.bash_history" >> $CHROOT_DIRECTORY/etc/profile.d/hosting.sh + # /dev devices test -e $CHROOT_DIRECTORY/dev/null || mknod -m 666 $CHROOT_DIRECTORY/dev/null c 1 3 test -e $CHROOT_DIRECTORY/dev/zero || mknod -m 666 $CHROOT_DIRECTORY/dev/zero c 1 5 @@ -235,4 +236,4 @@ done for BINARY in /usr/lib/php/*/*.so; do CHROOT_BINARY $BINARY done -diff $CHROOT_DIRECTORY/usr/bin/php7.3 $CHROOT_DIRECTORY/usr/bin/php > /dev/null 2>&1 || cp -r $CHROOT_DIRECTORY/usr/bin/php7.3 $CHROOT_DIRECTORY/usr/bin/php +cp -l $CHROOT_DIRECTORY/usr/bin/php7.3 $CHROOT_DIRECTORY/usr/bin/php