From 0f38bd24497516c38b7af319e5af4dddbccff8f4 Mon Sep 17 00:00:00 2001 From: Daniel Winzen Date: Tue, 1 Jan 2019 02:24:22 +0100 Subject: [PATCH] Improved privilege separation --- README.md | 6 +- etc/postfix-clearnet/canonical | 4 +- etc/postfix/canonical | 5 +- etc/postfix/main.cf | 16 +- etc/postfix/sender_login_maps | 2 +- etc/postfix/sql/alias.cf | 2 +- etc/systemd/system/php7.3-fpm@default.service | 1 + var/www/common.php | 100 +++++++- var/www/cron.php | 103 +------- var/www/html/home.php | 2 +- var/www/html/index.php | 7 +- var/www/html/register.php | 2 +- var/www/setup.php | 236 +++++++++--------- var/www/skel/www/index.hosting.html | 2 +- 14 files changed, 238 insertions(+), 250 deletions(-) diff --git a/README.md b/README.md index 77fe95b..f1e0177 100644 --- a/README.md +++ b/README.md @@ -128,8 +128,8 @@ D. > select dovecot Create a mysql user with all permissions for our hosting management: ``` mysql -CREATE USER 'hosting'@'localhost' IDENTIFIED BY 'MY_PASSWORD'; -GRANT ALL PRIVILEGES ON *.* TO 'hosting'@'localhost' WITH GRANT OPTION; +CREATE USER 'hosting'@'%' IDENTIFIED BY 'MY_PASSWORD'; +GRANT ALL PRIVILEGES ON *.* TO 'hosting'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES; quit ``` @@ -156,4 +156,4 @@ Final step is to reboot wait about 5 minutes for all services to start and check Live demo: ---------- -If you want to see the setup in action or create your own site on my server, you can visit my [TOR hidden service](http://dhosting4okcs22v.onion) or via [my clearnet proxy](https://hosting.danwin1210.me) if you don't have TOR installed. +If you want to see the setup in action or create your own site on my server, you can visit my [TOR hidden service](http://dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion) or via [my clearnet proxy](https://hosting.danwin1210.me) if you don't have TOR installed. diff --git a/etc/postfix-clearnet/canonical b/etc/postfix-clearnet/canonical index f7039c8..53a98aa 100644 --- a/etc/postfix-clearnet/canonical +++ b/etc/postfix-clearnet/canonical @@ -1,4 +1,2 @@ -/@tt3j2x4k5ycaa5zt.onion/ @danwin1210.me -/@torbox3uiot6wchz.onion/ @torbox.danwin1210.me -/@dhosting4okcs22v.onion/ @hosting.danwin1210.me +/@dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion/ @hosting.danwin1210.me diff --git a/etc/postfix/canonical b/etc/postfix/canonical index 0707f80..327e38f 100644 --- a/etc/postfix/canonical +++ b/etc/postfix/canonical @@ -1,7 +1,8 @@ -/@localhost/ @dhosting4okcs22v.onion +/@localhost/ @dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion +/@dhosting4okcs22v.onion/ @dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion /@danwin1210.me/ @tt3j2x4k5ycaa5zt.onion /@torbox.danwin1210.me/ @torbox3uiot6wchz.onion -/@hosting.danwin1210.me/ @dhosting4okcs22v.onion +/@hosting.danwin1210.me/ @dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion /@lelantos.org/ @lelantoss7bcnwbv.onion /@mail2tor.com/ @mail2torx3jqgcpm.onion /@mail2tor.onion/ @mail2torx3jqgcpm.onion diff --git a/etc/postfix/main.cf b/etc/postfix/main.cf index d3d9c04..bffacb2 100644 --- a/etc/postfix/main.cf +++ b/etc/postfix/main.cf @@ -1,11 +1,5 @@ # See /usr/share/postfix/main.cf.dist for a commented, more complete version - -# Debian specific: Specifying a file name will cause the first -# line of that file to be used as the name. The Debian default -# is /etc/mailname. -#myorigin = /etc/mailname - smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) biff = no @@ -27,17 +21,17 @@ smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. -myhostname = dhosting4okcs22v.onion +myhostname = dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases -myorigin = dhosting4okcs22v.onion -mydestination = dhosting4okcs22v.onion localhost dhosting +myorigin = dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion +mydestination = dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion localhost dhosting mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all inet_protocols = all -relay_domains = !dhosting4okcs22v.onion onion lelantos.org mail2tor.com anoninbox.net anonplus.org o3mail.org volatile.ch danwin1210.me bitmai.la volatile.bz bitmessage.ch elude.in secmail.pro vfemail.net anonymail.tech +relay_domains = !dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion onion lelantos.org mail2tor.com anoninbox.net anonplus.org o3mail.org volatile.ch danwin1210.me bitmai.la volatile.bz bitmessage.ch elude.in secmail.pro vfemail.net anonymail.tech home_mailbox = Maildir/ canonical_maps = proxy:mysql:/etc/postfix/sql/alias.cf regexp:/etc/postfix/canonical ignore_mx_lookup_error = yes @@ -46,7 +40,7 @@ message_drop_headers = bcc content-length resent-bcc return-path x-mailer receiv smtpd_sasl_auth_enable = yes smtpd_sasl_security_options = noanonymous -smtpd_sasl_local_domain = dhosting4okcs22v.onion +smtpd_sasl_local_domain = dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion smtpd_recipient_limit = 10 smtpd_sender_login_maps = regexp:/etc/postfix/sender_login_maps smtpd_sender_restrictions = reject_sender_login_mismatch, permit_sasl_authenticated diff --git a/etc/postfix/sender_login_maps b/etc/postfix/sender_login_maps index 15a7fbc..2fa5823 100644 --- a/etc/postfix/sender_login_maps +++ b/etc/postfix/sender_login_maps @@ -1 +1 @@ -/(.*)@dhosting4okcs22v.onion/ $1@dhosting4okcs22v.onion +/(.*)@dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion/ $1@dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion diff --git a/etc/postfix/sql/alias.cf b/etc/postfix/sql/alias.cf index fc865b4..31e17f3 100644 --- a/etc/postfix/sql/alias.cf +++ b/etc/postfix/sql/alias.cf @@ -2,4 +2,4 @@ user = hosting password = MY_PASSWORD hosts = localhost dbname = hosting -query = SELECT '%d@dhosting4okcs22v.onion' FROM users WHERE '%d' = system_account +query = SELECT '%d@dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion' FROM users WHERE '%d' = system_account diff --git a/etc/systemd/system/php7.3-fpm@default.service b/etc/systemd/system/php7.3-fpm@default.service index 60fe78b..591faf3 100644 --- a/etc/systemd/system/php7.3-fpm@default.service +++ b/etc/systemd/system/php7.3-fpm@default.service @@ -22,6 +22,7 @@ ProtectKernelModules=true ProtectControlGroups=true LockPersonality=true SystemCallArchitectures=native +BindPaths=/var/www/ BindPaths=/var/log/ BindPaths=/var/run/php/ BindPaths=/run/php/ diff --git a/var/www/common.php b/var/www/common.php index 3d8e9b9..a4d59d0 100644 --- a/var/www/common.php +++ b/var/www/common.php @@ -1,15 +1,15 @@ ['sftp'=>22, 'ftp'=>21, 'pop3'=>'110', 'imap'=>'143', 'smtp'=>'25'], +'dhosting4xxoydyaivckq7tsmtgi4wfs3flpeyitekkmqwu4v4r46syd.onion'=>['sftp'=>22, 'ftp'=>21, 'pop3'=>'110', 'imap'=>'143', 'smtp'=>'25'], 'hosting.danwin1210.me'=>['sftp'=>222, 'ftp'=>21, 'pop3'=>'1995', 'imap'=>'1993', 'smtp'=>'1465'] ]; const EMAIL_TO=''; //Send email notifications about new registrations to this address @@ -44,6 +44,7 @@ opcache.revalidate_path=1 opcache.save_comments=1 opcache.optimization_level=0xffffffff opcache.validate_permission=1 +opcache.validate_root=1 '; const NGINX_DEFAULT = 'server { listen unix:/var/run/nginx/suspended backlog=2048; @@ -62,21 +63,31 @@ server { try_files $uri $uri/ =404; location ~ \.php$ { include snippets/fastcgi-php.conf; - fastcgi_pass unix:/var/run/php/php7.3-fpm.sock; + fastcgi_param SCRIPT_FILENAME html/$fastcgi_script_name; + fastcgi_pass unix:/var/run/php/7.3-hosting; + } + } + location /squirrelmail { + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; + fastcgi_pass unix:/var/run/php/7.3-squirrelmail; } } location /phpmyadmin { root /usr/share; location ~ \.php$ { include snippets/fastcgi-php.conf; - fastcgi_pass unix:/run/php/php7.3-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; + fastcgi_pass unix:/run/php/7.3-phpmyadmin; } } location /adminer { root /usr/share/adminer; location ~ \.php$ { include snippets/fastcgi-php.conf; - fastcgi_pass unix:/run/php/php7.3-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; + fastcgi_pass unix:/run/php/7.3-adminer; } } location /externals/jush/ { @@ -293,7 +304,7 @@ NumEntryGuards 6 NumDirectoryGuards 6 NumPrimaryGuards 6 "; - $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.onion LIKE ? AND onions.enabled IN (1, -2) AND users.id NOT IN (SELECT user_id FROM new_account);'); + $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.onion LIKE ? 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){ @@ -372,3 +383,76 @@ function ed25519_seckey_expand(string $seed) : string { $sk[31] = chr(ord($sk[31]) | 64); return $sk; } + +function rewrite_nginx_config(PDO $db){ + $nginx=''; + $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;"); + while($tmp=$stmt->fetch(PDO::FETCH_ASSOC)){ + if($tmp['php']>0){ + $php_location=" + location ~ [^/]\.php(/|\$) { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/run/php/$tmp[system_account]; + }"; + }else{ + $php_location=''; + } + $autoindex = $tmp['autoindex'] ? 'on' : 'off'; + $nginx.="server { + listen [::]:80; + listen unix:/var/run/nginx/$tmp[system_account]; + root /home/$tmp[system_account]/www; + server_name $tmp[onion].onion *.$tmp[onion].onion; + access_log /var/log/nginx/access_$tmp[system_account].log custom buffer=4k flush=1m; + access_log /home/$tmp[system_account]/logs/access.log custom buffer=4k flush=1m; + error_log /var/log/nginx/error_$tmp[system_account].log notice; + error_log /home/$tmp[system_account]/logs/error.log notice; + disable_symlinks on from=/home/$tmp[system_account]; + autoindex $autoindex; + location / { + try_files \$uri \$uri/ =404;$php_location + } +} +"; + + file_put_contents("/etc/nginx/sites-enabled/hosted_sites", $nginx); + exec("service nginx reload"); + } +} + +function rewrite_php_config(PDO $db, string $key){ + $stmt=$db->prepare("SELECT system_account FROM users WHERE system_account LIKE ? AND php=? AND todelete!=1 AND id NOT IN (SELECT user_id FROM new_account);"); + foreach(array_replace(PHP_VERSIONS, DISABLED_PHP_VERSIONS) as $php_key => $version){ + $stmt->execute(["$key%", $php_key]); + $php = "[www] +user = www-data +group = www-data +listen = /run/php/$version-$key +listen.owner = www-data +listen.group = www-data +pm = ondemand +pm.max_children = 8 +"; + while($tmp=$stmt->fetch(PDO::FETCH_ASSOC)){ + $php.='['.$tmp['system_account']."] +user = $tmp[system_account] +group = www-data +listen = /run/php/$tmp[system_account] +listen.owner = www-data +listen.group = www-data +listen.mode = 0660 +pm = ondemand +pm.max_children = 20 +pm.process_idle_timeout = 10s; +chroot = /home/$tmp[system_account] +php_admin_value[memory_limit] = 256M +php_admin_value[disable_functions] = exec,link,passthru,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_waitpid,pcntl_wait,pcntl_wexitstatus,pcntl_wifcontinued,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,popen,posix_ctermid,posix_getgrgid,posix_getgrnam,posix_getpgid,posix_getpwnam,posix_getpwuid,posix_getrlimit,posix_getsid,posix_kill,posix_setegid,posix_seteuid,posix_setgid,posix_setpgid,posix_setrlimit,posix_setuid,posix_ttyname,posix_uname,proc_open,putenv,shell_exec,socket_listen,socket_create_listen,socket_bind,stream_socket_server,symlink,system +php_admin_value[upload_tmp_dir] = /tmp +php_admin_value[soap.wsdl_cache_dir] = /tmp +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"); + } +} diff --git a/var/www/cron.php b/var/www/cron.php index e1491b8..09c9739 100644 --- a/var/www/cron.php +++ b/var/www/cron.php @@ -27,9 +27,8 @@ while($id=$stmt->fetch(PDO::FETCH_NUM)){ $enable_onion->execute([$id[6]]); //add and manage rights of system user exec('useradd -l -p ' . escapeshellarg($id[2]) . ' -g www-data -k /var/www/skel -m -s /usr/sbin/nologin ' . escapeshellarg($system_account)); - chown("/home/$system_account", 'root'); - chgrp("/home/$system_account", 'www-data'); - chmod("/home/$system_account", 0550); + exec('/var/www/setup_chroot.sh ' . escapeshellarg("/home/$system_account")); + exec('grep ' . escapeshellarg($system_account) . ' /etc/passwd >> ' . escapeshellarg("/home/$system_account/etc/passwd")); foreach(['.ssh', 'data', 'Maildir', 'tmp'] as $dir){ mkdir("/home/$system_account/$dir", 0700); chown("/home/$system_account/$dir", $system_account); @@ -40,69 +39,6 @@ while($id=$stmt->fetch(PDO::FETCH_NUM)){ chown("/home/$system_account/$dir", $system_account); chgrp("/home/$system_account/$dir", 'www-data'); } - -//configuration for services - -if($id[3]>0){ -$php_location=" - location ~ [^/]\.php(/|\$) { - include snippets/fastcgi-php.conf; - fastcgi_pass unix:/run/php/$system_account; - } -"; -}else{ - $php_location=''; -} -if($id[4]){ - $autoindex='on'; -}else{ - $autoindex='off'; -} - -$nginx="server { - listen [::]:80; - listen unix:/var/run/nginx/$system_account; - root /home/$system_account/www; - server_name $onion.onion *.$onion.onion; - access_log /var/log/nginx/access_$system_account.log custom buffer=8k flush=1m; - access_log /home/$system_account/logs/access.log custom buffer=8k flush=1m; - error_log /var/log/nginx/error_$system_account.log notice; - error_log /home/$system_account/logs/error.log notice; - disable_symlinks on from=/home/$system_account; - autoindex $autoindex; - location / { - try_files \$uri \$uri/ =404;$php_location - } -} -"; - -$php="[$system_account] -user = $system_account -group = www-data -listen = /run/php/$system_account -listen.owner = www-data -listen.group = www-data -listen.mode = 0660 -pm = ondemand -pm.max_children = 20 -pm.process_idle_timeout = 10s; -php_admin_value[sendmail_path] = '/usr/bin/php /var/www/sendmail_wrapper.php \"$system_account <$system_account@" . ADDRESS . ">\" | /usr/sbin/sendmail -t -i' -php_admin_value[memory_limit] = 256M -php_admin_value[disable_functions] = exec,link,passthru,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_waitpid,pcntl_wait,pcntl_wexitstatus,pcntl_wifcontinued,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,popen,posix_ctermid,posix_getgrgid,posix_getgrnam,posix_getpgid,posix_getpwnam,posix_getpwuid,posix_getrlimit,posix_getsid,posix_kill,posix_setegid,posix_seteuid,posix_setgid,posix_setpgid,posix_setrlimit,posix_setuid,posix_ttyname,posix_uname,proc_open,putenv,shell_exec,socket_listen,socket_create_listen,socket_bind,stream_socket_server,symlink,system -php_admin_value[open_basedir] = /home/$system_account -php_admin_value[upload_tmp_dir] = /home/$system_account/tmp -php_admin_value[soap.wsdl_cache_dir] = /home/$system_account/tmp -php_admin_value[session.save_path] = /home/$system_account/tmp -"; - - //save configuration files - file_put_contents("/etc/nginx/sites-enabled/$system_account", $nginx); - foreach(PHP_VERSIONS as $key=>$version){ - if($id[3]==$key){ - file_put_contents("/etc/php/$version/fpm/pool.d/$firstchar/$system_account.conf", $php); - break; - } - } //remove from to-add queue $del->execute([$id[5]]); } @@ -151,30 +87,6 @@ $mark_onions=$db->prepare('UPDATE onions SET enabled=-1 WHERE user_id=? AND enab foreach($accounts as $account){ $firstchar=substr($account[0], 0, 1); $reload[$firstchar]=true; - //delete config files - foreach(DISABLED_PHP_VERSIONS as $v){ - // new naming schema - if(file_exists("/etc/php/$v/fpm/pool.d/$firstchar/$account[0].conf")){ - unlink("/etc/php/$v/fpm/pool.d/$firstchar/$account[0].conf"); - } - // old naming schema - if(file_exists("/etc/php/$v/fpm/pool.d/$firstchar/".substr($account[0], 0, 16).".conf")){ - unlink("/etc/php/$v/fpm/pool.d/$firstchar/".substr($account[0], 0, 16).".conf"); - } - } - foreach(PHP_VERSIONS as $v){ - // new naming schema - if(file_exists("/etc/php/$v/fpm/pool.d/$firstchar/$account[0].conf")){ - unlink("/etc/php/$v/fpm/pool.d/$firstchar/$account[0].conf"); - } - // old naming schema - if(file_exists("/etc/php/$v/fpm/pool.d/$firstchar/".substr($account[0], 0, 16).".conf")){ - unlink("/etc/php/$v/fpm/pool.d/$firstchar/".substr($account[0], 0, 16).".conf"); - } - } - if(file_exists("/etc/nginx/sites-enabled/$account[0]")){ - unlink("/etc/nginx/sites-enabled/$account[0]"); - } $mark_onions->execute([$account[1]]); } @@ -194,19 +106,12 @@ foreach($onions as $onion){ $del_onions->execute([$onion[0]]); } - - //reload services if(!empty($reload)){ - exec('service nginx reload'); - foreach(DISABLED_PHP_VERSIONS as $version){ - exec("service php$version-fpm reload"); - } + rewrite_nginx_config($db); } foreach($reload as $key => $val){ - foreach(PHP_VERSIONS as $version){ - exec("service php$version-fpm@$key restart"); - } + rewrite_php_config($db, $key); rewrite_torrc($db, $key); } diff --git a/var/www/html/home.php b/var/www/html/home.php index dcb38c1..faad6f7 100644 --- a/var/www/html/home.php +++ b/var/www/html/home.php @@ -77,7 +77,7 @@ echo 'DatabaseHostUser'; $stmt=$db->prepare('SELECT mysql_database FROM mysql_databases WHERE user_id=?;'); $stmt->execute([$user['id']]); while($mysql=$stmt->fetch(PDO::FETCH_ASSOC)){ - echo "$mysql[mysql_database]localhost$user[mysql_user]"; + echo "$mysql[mysql_database]127.0.0.1$user[mysql_user]"; } echo ''; echo '

Change MySQL password

'; diff --git a/var/www/html/index.php b/var/www/html/index.php index 21d4770..5aabcd5 100644 --- a/var/www/html/index.php +++ b/var/www/html/index.php @@ -19,6 +19,7 @@ if(isset($_SERVER['HTTP_HOST']) && preg_match('/danwin1210\.(i2p|me)$/', $_SERVE

Hosting - Info

Info | Register | Login | List of hosted sites | FAQ

+

After the hack that took place on November 15th, the hosting is finally back. There are just a few more things that need to be done before I can enable account registration. Due to a temporary loss of motivation in mid-december I'm behind schedule by about 2 weeks as initially planned, but new year, new opportunity. Registrations will open soon, once the last necessary changes are done, stay tuned.

Here you can get yourself a hosting account on my server.

What you will get: