&1'; $result = $this->exec($cmd); $version = false; $matches = array(); if(preg_match_all('/^ii\s+\S+\s+(\S+)(?:\s|$)/m', $result, $matches, PREG_SET_ORDER)) { for($i = 0; $i < count($matches); $i++) { $tmp_version = $matches[$i][1]; if(!$version || ISPProtectFunctions::version_compare($version, $tmp_version, '<')) { $version = $tmp_version; } } } return $version; } public function getPackageAlias($package) { switch($package) { case 'libssl': $package = 'libssl[0-9]*'; break; case 'kernel': $package = 'linux-image-[0-9]*'; break; } return $package; } public function getUpdateCommand($mode = 'update') { $cmd = false; if($mode == 'prepare') { $cmd = 'DEBIAN_FRONTEND="noninteractive" apt-get update -qq -y'; } elseif($mode == 'update') { // for updating all updateable packages $cmd = 'DEBIAN_FRONTEND="noninteractive" apt-get dist-upgrade -qq -y'; } elseif($mode == 'install' || $mode == 'partly_update') { // for installing / updating specific packages $cmd = 'DEBIAN_FRONTEND="noninteractive" apt-get install -qq -y'; $cmd .= ' '; } $cmd = 'while fuser /var/lib/dpkg/lock >/dev/null 2>&1 || fuser /var/lib/apt/lists/lock >/dev/null 2>&1 ; do sleep 2; done; ' . $cmd . ' 2>&1'; return $cmd; } public function getUpdatePackageRegex() { $regex = '^\w+\s+(?P\S+)\s+(?:\[(?P\S+)\]\s*)?(?:\((?P\S+))?(?:\s|$)'; return $regex; } public function getInstallPackageRegex($mode = '') { if($mode == 'oldversion') { $regex = '(?P\S+)\s+(?:(?P\d\S+)\s+)?\(.*\.deb'; } elseif($mode == 'newversion') { $regex = '(?:^|\s+)(?P\S+)\s+\((?P\d\S*)\)\s+'; } else { $regex = ''; // not on debian! } return $regex; } public function getRestartServiceCommand($service, $command = 'restart') { if($command != 'start' && $command != 'stop' && $command != 'status') { $command = 'restart'; } switch($service) { case 'apache': $service = 'apache2'; break; case 'pureftpd': $service = 'pure-ftpd-mysql'; break; } return 'service ' . escapeshellarg($service) . ' ' . $command . ' 2>&1'; } protected function updateMySQLConfig($mysql_root_pw) { ISPConfigLog::info('Writing MySQL config files.', true); $this->replaceContents('/etc/mysql/debian.cnf', array('/^password\s*=.*$/m' => 'password = ' . $mysql_root_pw)); $this->replaceContents('/etc/mysql/mariadb.conf.d/50-server.cnf', array('/^bind-address/m' => '#bind-address', '/^sql-mode\s*=.*?$/m' => 'sql-mode = "NO_ENGINE_SUBSTITUTION"'), true, 'mysqld'); } protected function getPackagesToInstall($section) { if($section === 'mail') { $packages = array( 'dnsutils', 'resolvconf', 'clamav', 'clamav-daemon', 'unzip', 'bzip2', 'arj', 'nomarch', 'lzop', 'cabextract', 'apt-listchanges', 'libnet-ldap-perl', 'libauthen-sasl-perl', 'clamav-docs', 'daemon', 'libio-string-perl', 'libio-socket-ssl-perl', 'libnet-ident-perl', 'zip', 'libnet-dns-perl', 'libdbd-mysql-perl', 'bind9', 'dnsutils' ); if(ISPConfig::shallInstall('mail')) { $packages[] = 'spamassassin'; if(ISPConfig::wantsAmavis()) { $packages[] = 'amavisd-new'; } else { $packages[] = 'rspamd'; $packages[] = 'redis-server'; } $packages[] = 'postgrey'; } } return $packages; } protected function getApacheModulesToEnable() { $modules = array('suexec', 'rewrite', 'ssl', 'actions', 'include', 'dav_fs', 'dav', 'auth_digest', 'cgi', 'headers', 'proxy_fcgi', 'alias'); return $modules; } protected function setDefaultPHP() { ISPConfigLog::info('Settings default system php version.', true); $cmd = 'update-alternatives --set php /usr/bin/php7.0 ; update-alternatives --set php-cgi /usr/bin/php-cgi7.0'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } protected function installPHPMyAdmin($mysql_root_pw) { if(!ISPConfig::shallInstall('web') || !ISPConfig::shallInstall('pma')) { return; } $cmd = 'APP_PASS="' . ISPConfigFunctions::generatePassword(15) . '"' . "\n"; $cmd .= 'ROOT_PASS="' . $mysql_root_pw . '"' . "\n"; $cmd .= 'APP_DB_PASS="' . ISPConfigFunctions::generatePassword(15) . '"' . "\n"; $cmd .= 'echo "phpmyadmin phpmyadmin/dbconfig-install boolean true" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "phpmyadmin phpmyadmin/app-password-confirm password $APP_PASS" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "phpmyadmin phpmyadmin/mysql/admin-user string root" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "phpmyadmin phpmyadmin/mysql/admin-pass password $ROOT_PASS" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "phpmyadmin phpmyadmin/mysql/app-pass password $APP_DB_PASS" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "phpmyadmin phpmyadmin/reconfigure-webserver multiselect apache2" | debconf-set-selections 2>&1' . "\n"; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $this->installPackages('phpmyadmin'); } protected function fixDbconfigCommon() { ISPConfigLog::info('Fixing dbconfig-common if neccessary'); $replacements = array( '/_dbc_nodb="yes" dbc_mysql_exec/' => '_dbc_nodb="yes"; dbc_mysql_exec' ); $this->replaceContents('/usr/share/dbconfig-common/internal/mysql', $replacements, false); } protected function setPHPTimezone() { if(!is_file('/etc/timezone')) { return; } $tz = trim(file_get_contents('/etc/timezone')); if(!in_array($tz, timezone_identifiers_list())) { return; } // set in all php inis $ini_files = array( '/etc/php/5.6/cgi/php.ini', '/etc/php/5.6/cli/php.ini', '/etc/php/5.6/fpm/php.ini', '/etc/php/5.6/apache2/php.ini', '/etc/php/7.0/cgi/php.ini', '/etc/php/7.0/cli/php.ini', '/etc/php/7.0/fpm/php.ini', '/etc/php/7.0/apache2/php.ini', '/etc/php/7.1/cgi/php.ini', '/etc/php/7.1/cli/php.ini', '/etc/php/7.1/fpm/php.ini', '/etc/php/7.1/apache2/php.ini', '/etc/php/7.2/cgi/php.ini', '/etc/php/7.2/cli/php.ini', '/etc/php/7.2/fpm/php.ini', '/etc/php/7.2/apache2/php.ini', '/etc/php/7.3/cgi/php.ini', '/etc/php/7.3/cli/php.ini', '/etc/php/7.3/fpm/php.ini', '/etc/php/7.3/apache2/php.ini', '/etc/php/7.4/cgi/php.ini', '/etc/php/7.4/cli/php.ini', '/etc/php/7.4/fpm/php.ini', '/etc/php/7.4/apache2/php.ini' ); $replace = array( '/^;?\s*date\.timezone\s+=.*$/' => 'date.timezone = ' . $tz ); foreach($ini_files as $ini) { if(is_file($ini)) { $this->replaceContents($ini, $replace); } } } protected function configureApt() { // enable contrib and non-free ISPConfigLog::info('Enabling contrib and non-free repositories.', true); $replacements = array( '/^(deb.*\s+main)\s*$/' => '$1 contrib non-free' ); $this->replaceContents('/etc/apt/sources.list', $replacements); } protected function addSuryRepo() { ISPConfigLog::info('Activating sury php repository.', true); $cmd = 'wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg >/dev/null 2>&1 ; echo "deb https://packages.sury.org/php/ $(lsb_release -c -s) main" > /etc/apt/sources.list.d/php.list'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } protected function getFail2BanJail() { $jk_jail = '[pure-ftpd] enabled = true port = ftp filter = pure-ftpd logpath = /var/log/syslog maxretry = 3 [dovecot] enabled = true filter = dovecot logpath = /var/log/mail.log maxretry = 5 [postfix-sasl] enabled = true port = smtp filter = postfix-sasl logpath = /var/log/mail.log maxretry = 3'; return $jk_jail; } protected function installMailman($host_name) { if(!ISPConfig::shallInstall('mail') || !ISPConfig::shallInstall('mailman')) { return; } ISPConfigLog::info('Installing Mailman', true); $cmd = 'echo "mailman mailman/site_languages multiselect de (German), en (English)" | debconf-set-selections 2>&1' . "\n"; if(isset($_GET['lang']) && $_GET['lang'] === 'de') { $cmd .= 'echo "mailman mailman/default_server_language select de (German)" | debconf-set-selections 2>&1'; } else { $cmd .= 'echo "mailman mailman/default_server_language select en (English)" | debconf-set-selections 2>&1'; } $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $package = 'mailman'; $this->installPackages($package); $listpw = ''; if(!is_dir('/var/lib/mailman/lists/mailman')) { $listpw = ISPConfigFunctions::generatePassword(12); $cmd = 'newlist -q -e ' . escapeshellarg($host_name) . ' mailman ' . escapeshellarg('root@' . $host_name) . ' ' . escapeshellarg($listpw); $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } $add_content = '## mailman mailing list mailman: "|/var/lib/mailman/mail/mailman post mailman" mailman-admin: "|/var/lib/mailman/mail/mailman admin mailman" mailman-bounces: "|/var/lib/mailman/mail/mailman bounces mailman" mailman-confirm: "|/var/lib/mailman/mail/mailman confirm mailman" mailman-join: "|/var/lib/mailman/mail/mailman join mailman" mailman-leave: "|/var/lib/mailman/mail/mailman leave mailman" mailman-owner: "|/var/lib/mailman/mail/mailman owner mailman" mailman-request: "|/var/lib/mailman/mail/mailman request mailman" mailman-subscribe: "|/var/lib/mailman/mail/mailman subscribe mailman" mailman-unsubscribe: "|/var/lib/mailman/mail/mailman unsubscribe mailman"'; $fp = fopen('/etc/aliases', 'r+'); if(!$fp) { throw new ISPConfigOSException('Opening /etc/aliases failed.'); } $found = false; while(!feof($fp)) { $line = trim(fgets($fp)); if($line === '## mailman mailing list') { $found = true; break; } } if($found === false) { fseek($fp, SEEK_END); fwrite($fp, "\n\n" . $add_content); } fclose($fp); $cmd = 'newaliases'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { if(!is_link('/etc/apache2/conf-enabled/mailman.conf') && !is_file('/etc/apache2/conf-enabled/mailman.conf')) { symlink('/etc/mailman/apache.conf', '/etc/apache2/conf-enabled/mailman.conf'); } } $this->restartService('postfix'); $this->restartService('mailman'); if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { $this->restartService('apache2'); } return $listpw; } protected function installPackages($packages) { if(is_string($packages)) { $packages = array($packages); } ISPConfigLog::info('Installing packages ' . implode(', ', $packages), true); $result = parent::installPackages($packages); if($result !== false) { ISPConfigLog::info('Installed packages ' . implode(', ', $packages), true); } else { throw new ISPConfigOSException('Installing packages failed.'); } return $result; } public function runPerfectSetup() { $log_filename = 'setup-' . strftime('%Y%m%d%H%M%S', time()) . '.log'; ISPConfigLog::setLogFile($log_filename); if(is_file('/usr/local/ispconfig/server/lib/config.inc.php')) { ISPConfigLog::error('The server already has ISPConfig installed. Aborting.', true); return false; } $this->configureApt(); $this->updatePackageList(); ISPConfigLog::info('Updating packages', true); $cmd = $this->getUpdateCommand('update'); $result = $this->exec($cmd); if($result !== false) { ISPConfigLog::info('Updated packages', true); } else { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } try { $this->beforePackageInstall(); } catch (Exception $ex) { throw $ex; } $packages = array( 'ssh', 'openssh-server', 'nano', 'vim-nox', 'ntp', 'lsb-release', 'apt-transport-https', 'ca-certificates', 'wget', 'git' ); $this->installPackages($packages); if(ISPConfig::shallInstall('mail') && !ISPConfig::wantsAmavis()) { ISPConfigLog::info('Activating rspamd repository.', true); $cmd = 'wget -O - "https://rspamd.com/apt-stable/gpg.key" 2>/dev/null | apt-key add - 2>/dev/null ; echo "deb http://rspamd.com/apt-stable/ $(lsb_release -c -s) main" > /etc/apt/sources.list.d/rspamd.list'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } if(ISPConfig::wantsPHP() !== 'system') { $this->addSuryRepo(); } $this->updatePackageList(); ISPConfigLog::info('Updating packages (after enabling 3rd party repos).', true); $cmd = $this->getUpdateCommand('update'); $result = $this->exec($cmd); if($result !== false) { ISPConfigLog::info('Updated packages', true); } else { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } /*$hostname_changed = false; ISPConfigLog::info('Setting hostname to ' . $host_name, true); $dotpos = strpos($host_name, '.'); if($dotpos !== false) { $short_hostname = substr($host_name, 0, $dotpos); } else { $short_hostname = ''; } $hosts_entry = $this->ip_address . "\t" . $host_name . ($short_hostname ? ' ' . $short_hostname : ''); if(is_file('/etc/cloud/templates/hosts.tmpl')) { $use_hosts_file = '/etc/cloud/templates/hosts.tmpl'; } else { $use_hosts_file = '/etc/hosts'; } $content = file_get_contents($use_hosts_file); if(preg_match('/^\s*' . preg_quote($this->ip_address, '/') . ' (.*?)$/m', $content, $matches)) { ISPConfigLog::info('Hostname is currently set to ' . $matches[1]); $content = str_replace($matches[0], $hosts_entry, $content); if($matches[0] != $hosts_entry) { $hostname_changed = true; } } else { ISPConfigLog::info('Hostname not found in hosts file.'); $content .= "\n" . $hosts_entry; $hostname_changed = true; } file_put_contents($use_hosts_file, $content); $content = trim(file_get_contents('/etc/hostname')); if($content != $short_hostname) { ISPConfigLog::info('/etc/hostname is currently set to ' . $content, true); $hostname_changed = true; file_put_contents('/etc/hostname', $short_hostname); } ISPConfigLog::info('Hostname saved.', true); if($hostname_changed) { ISPConfigLog::info('Rebooting server.', true); $ok = $this->exec('shutdown -r now >/dev/null 2>&1', array(0, 255)); if($ok === false) { throw new ISPConfigOSException('Command for server reboot failed.'); } $ok = $this->waitForReboot(30, 1200); if(!$ok) { throw new ISPConfigOSException('Timeout waiting for server to come up.'); } ISPConfigLog::info('Server online again.', true); }*/ ISPConfigLog::info('Checking hostname.', true); $host_name = false; $cmd = 'hostname -f 2>&1'; $check = $this->exec($cmd); if($check === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } else { $host_name = $check; }/* elseif(trim($check) !== $host_name) { ISPConfigLog::warn('Hostname mismatch: ' . $check . ' != ' . $host_name); }*/ $cmd = 'hostname 2>&1'; $check = $this->exec($cmd); if($check === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); }/* elseif(trim($check) !== $short_hostname) { ISPConfigLog::warn('Short hostname mismatch: ' . $check . ' != ' . $short_hostname); }*/ if($host_name == '') { ISPConfigLog::error('Could not read the host name of your server. Please check it is correctly set.', true); throw new ISPConfigOSException('Invalid host name or host name not found.'); } elseif(substr_count($host_name, '.') < 2) { ISPConfigLog::error('The host name ' . $host_name . ' of your server is no fully qualified domain name (xyz.domain.com). Please check it is correctly set.', true); throw new ISPConfigOSException('Host name is no FQDN.'); } $cmd = 'readlink /bin/sh 2>&1'; $check = trim($this->exec($cmd)); if($check === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } elseif($check !== 'bash') { //debconf-show dash ISPConfigLog::info('Default shell is currently ' . $check . '.', true); ISPConfigLog::info('Setting bash as default shell.', true); $cmd = 'echo "dash dash/sh boolean false" | debconf-set-selections && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure dash 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $cmd = 'readlink /bin/sh 2>&1'; $check = trim($this->exec($cmd)); ISPConfigLog::info('Default shell is now ' . $check . '.', true); } $cmd = 'echo "postfix postfix/mailname string ' . $host_name . '" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "postfix postfix/main_mailer_type select Internet Site" | debconf-set-selections 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $packages = array( 'dbconfig-common', 'postfix', 'postfix-mysql', 'postfix-doc', 'mariadb-client', 'mariadb-server', 'openssl', 'getmail4', 'rkhunter', 'binutils', 'sudo' ); $this->installPackages($packages); if(ISPConfig::shallInstall('mail')) { $packages = array( 'dovecot-imapd', 'dovecot-pop3d', 'dovecot-mysql', 'dovecot-sieve', 'dovecot-managesieved', 'dovecot-lmtpd' ); $this->installPackages($packages); } ISPConfigLog::info('Generating mySQL password.', true); // generate random password $mysql_root_pw = ISPConfigFunctions::generatePassword(20); $escaped_pw = preg_replace('/[\'\\\\]/', '\\$1', $mysql_root_pw); $queries = array( 'DELETE FROM mysql.user WHERE User=\'\';', 'DELETE FROM mysql.user WHERE User=\'root\' AND Host NOT IN (\'localhost\', \'127.0.0.1\', \'::1\');', 'DROP DATABASE IF EXISTS test;', 'DELETE FROM mysql.db WHERE Db=\'test\' OR Db=\'test\\_%\';', 'UPDATE mysql.user SET Password=PASSWORD(\'' . $escaped_pw . '\') WHERE User=\'root\';', 'UPDATE mysql.user SET plugin = \'mysql_native_password\' WHERE User=\'root\';', 'FLUSH PRIVILEGES;' ); foreach($queries as $query) { $cmd = 'mysql --defaults-file=/etc/mysql/debian.cnf -e ' . escapeshellarg($query) . ' 2>&1'; $result = $this->exec($cmd); if($result === false) { ISPConfigLog::warn('Query ' . $query . ' failed.', true); } } $this->updateMySQLConfig($mysql_root_pw); if(ISPConfig::shallInstall('mail')) { ISPConfigLog::info('Configuring postfix.', true); $entries = array( array( 'first_line' => '/^submission\s+inet/', 'last_line' => '/^[a-z]/', 'skip_last_line' => true, 'search' => '/^\s+-o/' ), array( 'first_line' => '/^smtps\s+inet/', 'last_line' => '/^[a-z]/', 'skip_last_line' => true, 'search' => '/^\s+-o/' ) ); $this->commentLines('/etc/postfix/master.cf', $entries); $entries = array( array( 'first_line' => '/^#?submission\s+inet/', 'last_line' => null, 'search' => null, 'add_lines' => array( ' -o syslog_name=postfix/submission', ' -o smtpd_tls_security_level=encrypt', ' -o smtpd_sasl_auth_enable=yes', ' -o smtpd_client_restrictions=permit_sasl_authenticated,reject' ) ), array( 'first_line' => '/^#?smtps\s+inet/', 'last_line' => null, 'search' => null, 'add_lines' => array( ' -o syslog_name=postfix/smtps', ' -o smtpd_tls_wrappermode=yes', ' -o smtpd_sasl_auth_enable=yes', ' -o smtpd_client_restrictions=permit_sasl_authenticated,reject' ) ) ); $this->uncommentLines('/etc/postfix/master.cf', $entries); } ISPConfigLog::info('Restarting postfix', true); $this->restartService('postfix'); $replacements = array( '/^mysql\s+soft\s+nofile\s+.*/' => 'mysql soft nofile 65535', '/^mysql\s+hard\s+nofile\s+.*/' => 'mysql hard nofile 65535' ); $this->replaceContents('/etc/security/limits.conf', $replacements, true); if(!is_dir('/etc/systemd/system/mysql.service.d/')) { mkdir('/etc/systemd/system/mysql.service.d/', 0777, true); } $replacements = array( '/^\s*LimitNOFILE\s*=.*?$/m' => 'LimitNOFILE=infinity' ); $this->replaceContents('/etc/systemd/system/mysql.service.d/limits.conf', $replacements, true, 'Service'); $this->exec('systemctl daemon-reload 2>&1'); $this->restartService('mysql'); $packages = $this->getPackagesToInstall('mail'); $this->installPackages($packages); if(ISPConfig::shallInstall('mail') && !ISPConfig::wantsAmavis()) { ISPConfigLog::info('Stopping Rspamd.', true); $this->stopService('rspamd'); } ISPConfigLog::info('(Re)starting Bind.', true); $this->restartService('bind9'); ISPConfigLog::info('Disabling spamassassin daemon.', true); $this->stopService('spamassassin'); $this->exec('systemctl disable spamassassin 2>&1'); $this->afterPackageInstall('mail'); //$cmd = 'sudo -u unbound unbound-anchor -a /var/lib/unbound/root.key'; /*$result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $this->restartService('unbound'); */ if(!is_dir('/etc/resolvconf/resolv.conf.d')) { mkdir('/etc/resolvconf/resolv.conf.d', 0755); } $this->addLines('/etc/resolvconf/resolv.conf.d/head', 'nameserver 127.0.0.1', false); $cmd = 'resolvconf -u 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } ISPConfigLog::info('Checking local dns resolver.', true); $cmd = 'nslookup denic.de | grep Server'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } elseif(strpos($result, '127.0.0.1') === false) { ISPConfigLog::warn('Unexpected resolver response: ' . $result, true); } if(ISPConfig::shallInstall('web')) { $this->stopService('apache2'); $this->stopService('nginx'); if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { $packages = array( 'apache2', 'apache2-doc', 'apache2-utils', 'libapache2-mod-fcgid', 'apache2-suexec-pristine', 'libapache2-mod-php', 'libapache2-mod-python', 'libapache2-mod-passenger' ); } elseif(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_NGINX) { $packages = array( 'nginx-full', 'fcgiwrap' ); } $this->installPackages($packages); if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_NGINX) { $this->stopService('apache2'); $cmd = 'systemctl disable apache2'; $this->exec($cmd); // ignore if this fails $this->startService('nginx'); } } $packages = array( 'php-pear', 'php-memcache', 'php-imagick', 'php-gettext', 'mcrypt', 'imagemagick', 'libruby', 'memcached', 'php-apcu' ); if(ISPConfig::wantsPHP() === 'system') { $php_versions = array($this->getSystemPHPVersion()); } else { $php_versions = array( '5.6', '7.0', '7.1', '7.2', '7.3', '7.4' ); } $php_modules = array( 'common', 'gd', 'mysql', 'imap', 'cli', 'mcrypt', 'curl', 'intl', 'pspell', 'recode', 'sqlite3', 'tidy', 'xmlrpc', 'xsl', 'zip', 'mbstring', 'soap', 'opcache' ); if(ISPConfig::shallInstall('web')) { $php_modules[] = 'cgi'; $php_modules[] = 'fpm'; } foreach($php_versions as $curver) { $packages[] = 'php' . $curver; reset($php_modules); foreach($php_modules as $curmod) { if(version_compare($curver, '7.2', '>=') && in_array($curmod, array('mcrypt'), true)) { continue; } if(version_compare($curver, '7.4', '>=') && in_array($curmod, array('recode'), true)) { continue; } $packages[] = 'php' . $curver . '-' . $curmod; } } $this->installPackages($packages); if(ISPConfig::shallInstall('web') && ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { ISPConfigLog::info('Enabling apache modules.', true); $modules = $this->getApacheModulesToEnable(); $cmd = 'a2enmod ' . implode(' ', $modules) . ' 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } try { $this->setPHPTimezone(); $this->setDefaultPHP(); } catch (Exception $ex) { throw $ex; } foreach($php_versions as $curver) { $this->restartService('php' . $curver . '-fpm'); } try{ $this->installPHPMyAdmin($mysql_root_pw); } catch(Exception $ex) { throw $ex; } if(ISPConfig::shallInstall('web') && ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { ISPConfigLog::info('HTTPoxy config.', true); $httpoxy = '' . "\n" . ' RequestHeader unset Proxy early' . "\n" . ''; file_put_contents('/etc/apache2/conf-available/httpoxy.conf', $httpoxy); $cmd = 'a2enconf httpoxy 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $this->restartService('apache2'); } ISPConfigLog::info('Installing letsencrypt (acme.sh).', true); $cmd = 'cd /tmp ; wget -O - https://get.acme.sh 2>/dev/null | sh 2>/dev/null'; $result = $this->exec($cmd); if($result === false) { ISPConfigLog::warn('Installing letsencrypt failed.', true); } else { ISPConfigLog::info('Letsencrypt installed.', true); } $mailman_password = ''; if(ISPConfig::shallInstall('mailman')) { $mailman_password = $this->installMailman($host_name); } $packages = array( 'quota', 'quotatool', 'haveged', 'geoip-database', 'libclass-dbi-mysql-perl', 'libtimedate-perl', 'build-essential', 'autoconf', 'automake', 'libtool', 'flex', 'bison', 'debhelper', 'binutils' ); $this->installPackages($packages); if(ISPConfig::shallInstall('quota')) { // check kernel if it is virtual $check = $this->getPackageVersion('linux-image-virtual'); if($check) { ISPConfigLog::info('Installing extra quota package for virtual kernel.', true); $this->installPackages('linux-image-extra-virtual'); // check kernel version from dpkg vs version running $check = $this->getPackageVersion('linux-image-extra-virtual'); $running_version = php_uname('r'); if(!is_dir('/lib/modules/' . $running_version . '/kernel/fs/quota/') || !is_file('/lib/modules/' . $running_version . '/kernel/fs/quota/quota_v2.ko')) { $running_version = preg_replace('/^([0-9\.]+(?:-\d+)?)(?:-.*?)?$/', '$1', $running_version); try { $this->installPackages('linux-image-extra-virtual=' . $running_version . '*'); } catch (Exception $ex) { // ignore it } // check if quota module is available if(!$this->exec('modinfo quota_v1 quota_v2 2>&1')) { ISPConfigLog::error('The running kernel version (' . $running_version . ') does not match your installed kernel modules (' . $check . '). Currently there is no quota available! Please reboot your server to load the new kernel and run the autoinstaller again or start it with --no-quota to disable quota completely.', true); throw new ISPConfigOSException('Installation aborted due to missing dependencies.'); } } ISPConfigLog::info('Enabling quota modules for virtual kernel.', true); $cmd = 'modprobe quota_v2 quota_v1 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Enabling quota modules failed.'); } } ISPConfigLog::info('Adding quota to fstab.', true); $replacements = array( '/^(\S+\s+\/\s+ext\d)\s+(\S+)\s+(\d\s+\d)\s*$/m' => array( 'replace' => '$1 $2,usrjquota=quota.user,grpjquota=quota.group,jqfmt=vfsv0 $3', 'ifnot' => 'usrjquota=' ) ); $this->replaceContents('/etc/fstab', $replacements); $cmd = 'mount -o remount / 2>&1 && quotaoff -avug 2>&1 && quotacheck -avugm 2>&1 && quotaon -avug 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } if(ISPConfig::shallInstall('web')) { $cmd = 'echo "pure-ftpd-common pure-ftpd/standalone-or-inetd select standalone" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "pure-ftpd-common pure-ftpd/virtualchroot boolean true" | debconf-set-selections 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $packages = array( 'pure-ftpd-common', 'pure-ftpd-mysql', 'webalizer', 'awstats' ); $this->installPackages($packages); ISPConfigLog::info('Enabling TLS for pureftpd', true); if(!is_dir('/etc/pure-ftpd/conf')) { mkdir('/etc/pure-ftpd/conf', 0755); } file_put_contents('/etc/pure-ftpd/conf/TLS', '1'); if(!is_dir('/etc/ssl/private')) { mkdir('/etc/ssl/private', 0755, true); } $ssl_subject = '/C=DE/ST=None/L=None/O=IT/CN=' . $host_name; $cmd = 'openssl req -x509 -nodes -days 7300 -newkey rsa:2048 -subj ' . escapeshellarg($ssl_subject) . ' -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem > /dev/null 2>&1'; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } chmod('/etc/ssl/private/pure-ftpd.pem', 0600); // set passive port range if needed $ftp_ports = ISPConfig::getFTPPassivePorts(); if($ftp_ports) { file_put_contents('/etc/pure-ftpd/conf/PassivePortRange', $ftp_ports['from'] . ' ' . $ftp_ports['to']); } $this->restartService('pure-ftpd-mysql'); ISPConfigLog::info('Disabling awstats cron.', true); $entries = array( array( 'first_line' => '/.*/', 'last_line' => '/####nomatch###/', 'search' => '/.*/' ) ); $this->commentLines('/etc/cron.d/awstats', $entries); $cmd = 'cd /tmp ; ( wget -O jailkit-2.20.tar.gz "http://olivier.sessink.nl/jailkit/jailkit-2.20.tar.gz" > /dev/null 2>&1 && tar xzf jailkit-2.20.tar.gz 2>&1 ) && ( cd jailkit-2.20 ; echo 5 > debian/compat ; ./debian/rules binary 2>&1 ) && ( cd /tmp ; dpkg -i jailkit_2.20-1_*.deb 2>&1 ; rm -rf jailkit-2.20* )'; $result = $this->exec($cmd, array(), 3); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } $packages = array( 'fail2ban' ); if(ISPConfig::shallInstall('firewall')) { $packages[] = 'ufw'; } $this->installPackages($packages); $jk_jail = $this->getFail2BanJail(); file_put_contents('/etc/fail2ban/jail.local', $jk_jail); unset($jk_jail); $this->restartService('fail2ban'); $this->fixDbconfigCommon(); if(ISPConfig::shallInstall('mail') && ISPConfig::shallInstall('roundcube')) { ISPConfigLog::info('Installing roundcube.', true); $cmd = 'APP_PASS="' . ISPConfigFunctions::generatePassword(15) . '"' . "\n"; $cmd .= 'ROOT_PASS="' . $mysql_root_pw . '"' . "\n"; $cmd .= 'APP_DB_PASS="' . ISPConfigFunctions::generatePassword(15) . '"' . "\n"; $cmd .= 'echo "roundcube-core roundcube/dbconfig-install boolean true" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "roundcube-core roundcube/database-type select mysql" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "roundcube-core roundcube/mysql/admin-user string root" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "roundcube-core roundcube/mysql/admin-pass password $ROOT_PASS" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "roundcube-core roundcube/mysql/app-pass password $APP_DB_PASS" | debconf-set-selections 2>&1' . "\n"; $cmd .= 'echo "roundcube-core roundcube/reconfigure-webserver multiselect apache2" | debconf-set-selections 2>&1' . "\n"; $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } $packages = array( 'roundcube', 'roundcube-core', 'roundcube-mysql', 'roundcube-plugins' ); $this->installPackages($packages); $replacements = array( '/^\s*\$config\s*\[["\']default_host["\']\]\s*=.*$/m' => '$config[\'default_host\'] = \'localhost\';', '/^\s*\$config\s*\[["\']smtp_server["\']\]\s*=.*$/m' => '$config[\'smtp_server\'] = \'%h\';', '/^\s*\$config\s*\[["\']smtp_user["\']\]\s*=.*$/m' => '$config[\'smtp_user\'] = \'%u\';', '/^\s*\$config\s*\[["\']smtp_pass["\']\]\s*=.*$/m' => '$config[\'smtp_pass\'] = \'%p\';' ); $result = $this->replaceContents('/etc/roundcube/config.inc.php', $replacements); if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { $replacements = array( '/^\s*#*\s*Alias\s+\/roundcube\s+\/var\/lib\/roundcube\s*$/m' => 'Alias /webmail /var/lib/roundcube' ); $result = $this->replaceContents('/etc/apache2/conf-enabled/roundcube.conf', $replacements); } elseif(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_NGINX) { symlink('/usr/share/roundcube', '/usr/share/squirrelmail'); } } if(ISPConfig::shallInstall('web')) { if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { $this->restartService('apache2'); } else { $this->restartService('nginx'); } } ISPConfigLog::info('Installing ISPConfig3.', true); $ispconfig_admin_pw = ISPConfigFunctions::generatePassword(15); if(!ISPConfig::wantsInteractive()) { $autoinstall = '[install] language=' . (isset($_GET['lang']) && $_GET['lang'] === 'de' ? 'de' : 'en') . ' install_mode=expert hostname=' . $host_name . ' mysql_hostname=localhost mysql_port=3306 mysql_root_user=root mysql_root_password=' . $mysql_root_pw . ' mysql_database=dbispconfig mysql_charset=utf8 http_server=' . (ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE ? 'apache' : 'nginx') . ' ispconfig_port=8080 ispconfig_use_ssl=y ispconfig_admin_password=' . $ispconfig_admin_pw . ' [ssl_cert] ssl_cert_country=DE ssl_cert_state=None ssl_cert_locality=None ssl_cert_organisation=None ssl_cert_organisation_unit=IT ssl_cert_common_name=' . $host_name . ' ssl_cert_email= [expert] mysql_ispconfig_user=ispconfig mysql_ispconfig_password=' . ISPConfigFunctions::generatePassword(15) . ' join_multiserver_setup=n mysql_master_hostname= mysql_master_root_user= mysql_master_root_password= mysql_master_database= configure_mail=' . (ISPConfig::shallInstall('mail') ? 'y' : 'n') . ' configure_jailkit=' . (ISPConfig::shallInstall('web') ? 'y' : 'n') . ' configure_ftp=' . (ISPConfig::shallInstall('web') ? 'y' : 'n') . ' configure_dns=' . (ISPConfig::shallInstall('dns') ? 'y' : 'n') . ' configure_apache=' . (ISPConfig::shallInstall('web') && ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE ? 'y' : 'n') . ' configure_nginx=' . (ISPConfig::shallInstall('web') && ISPConfig::$WEBSERVER === ISPC_WEBSERVER_NGINX ? 'y' : 'n') . ' configure_firewall=' . (ISPConfig::shallInstall('firewall') ? 'y' : 'n') . ' configure_webserver=' . (ISPConfig::shallInstall('web') ? 'y' : 'n') . ' install_ispconfig_web_interface=' . (ISPConfig::shallInstall('web') ? 'y' : 'n') . ' [update] do_backup=yes mysql_root_password=' . $mysql_root_pw . ' mysql_master_hostname= mysql_master_root_user= mysql_master_root_password= mysql_master_database= reconfigure_permissions_in_master_database=no reconfigure_services=yes ispconfig_port=8080 create_new_ispconfig_ssl_cert=no reconfigure_crontab=yes ; These are for service-detection (defaulting to old behaviour where alle changes were automatically accepted) svc_detect_change_mail_server=yes svc_detect_change_web_server=yes svc_detect_change_dns_server=yes svc_detect_change_xmpp_server=yes svc_detect_change_firewall_server=yes svc_detect_change_vserver_server=yes svc_detect_change_db_server=yes'; file_put_contents('/tmp/ispconfig.autoinstall.ini', $autoinstall); $ai_argument = '--autoinstall=/tmp/ispconfig.autoinstall.ini'; } else { $ai_argument = ''; } if(ISPConfig::wantsInteractive()) { ISPConfigLog::info('Your MySQL root password is: ' . $mysql_root_pw, true); } $cmd = 'cd /tmp ; rm -rf ispconfig3_install 2>&1'; if(ISPConfig::getISPConfigChannel() === 'dev') { $cmd .= ' ; wget -O ispconfig.tar.gz "https://git.ispconfig.org/ispconfig/ispconfig3/-/archive/stable-3.1/ispconfig3-stable-3.1.tar.gz" >/dev/null 2>&1 ; tar xzf ispconfig.tar.gz ; mv ispconfig3-stable-3.1 ispconfig3_install'; } else { $cmd .= ' ; wget -O ispconfig.tar.gz "https://www.ispconfig.org/downloads/ISPConfig-3-stable.tar.gz" >/dev/null 2>&1 ; tar xzf ispconfig.tar.gz'; } $cmd .= ' ; cd ispconfig3_install ; cd install ; php -q install.php ' . $ai_argument . ' 2>&1 ; cd /tmp ; rm -rf ispconfig3_install 2>&1'; if(ISPConfig::wantsInteractive()) { $result = $this->passthru($cmd); } else { $result = $this->exec($cmd); } if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } if(!ISPConfig::wantsInteractive() && is_file('/tmp/ispconfig.autoinstall.ini')) { unlink('/tmp/ispconfig.autoinstall.ini'); } if(ISPConfig::shallInstall('web')) { ISPConfigLog::info('Adding php versions to ISPConfig.', true); $server_id = 0; $ispc_config = ISPConfigConnector::getLocalConfig(); if(!$ispc_config || !isset($ispc_config['server_id']) || !$ispc_config['server_id']) { throw new ISPConfigOSException('Could not read ISPConfig settings file.'); } $server_id = $ispc_config['server_id']; foreach($php_versions as $curver) { $qry = 'INSERT IGNORE INTO `dbispconfig`.`server_php` (`sys_userid`, `sys_groupid`, `sys_perm_user`, `sys_perm_group`, `sys_perm_other`, `server_id`, `client_id`, `name`, `php_fastcgi_binary`, `php_fastcgi_ini_dir`, `php_fpm_init_script`, `php_fpm_ini_dir`, `php_fpm_pool_dir`, `active`) VALUES (1, 1, \'riud\', \'riud\', \'\', ' . intval($server_id) . ', 0, \'PHP ' . $curver . '\', \'/usr/bin/php-cgi' . $curver . '\', \'/etc/php/' . $curver . '/cgi/php.ini\', \'/etc/init.d/php' . $curver . '-fpm\', \'/etc/php/' . $curver . '/fpm/php.ini\', \'/etc/php/' . $curver . '/fpm/pool.d\', \'y\')'; $cmd = 'mysql --defaults-file=/etc/mysql/debian.cnf -e ' . escapeshellarg($qry); $result = $this->exec($cmd); if($result === false) { throw new ISPConfigOSException('Command ' . $cmd . ' failed.'); } } } $this->restartService('clamav-daemon'); if(ISPConfig::shallInstall('mail')) { if(ISPConfig::wantsAmavis()) { $this->restartService('amavis'); } else { $this->startService('rspamd'); } } ISPConfigLog::info('Checking all services are running.', true); $check_services = array( 'mysql', 'clamav-daemon', 'postfix', 'bind9', ); if(ISPConfig::shallInstall('web')) { $check_services[] = 'pureftpd'; if(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_APACHE) { $check_services[] = 'apache2'; } elseif(ISPConfig::$WEBSERVER === ISPC_WEBSERVER_NGINX) { $check_services[] = 'nginx'; } } if(ISPConfig::shallInstall('mail')) { if(!ISPConfig::wantsAmavis()) { $check_services[] = 'rspamd'; $check_services[] = 'redis-server'; } else { $check_services[] = 'amavis'; } $check_services[] = 'dovecot'; } foreach($check_services as $service) { $status = $this->isServiceRunning($service); ISPConfigLog::info($service . ': ' . ($status ? 'OK' : 'FAILED'), true); if(!$status) { ISPConfigLog::warn($service . ' seems not to be running!', true); } } ISPConfigLog::info('Installation ready.', true); if(ISPConfig::shallInstall('mailman') && $mailman_password != '') { ISPConfigLog::info('Your Mailman password is: ' . $mailman_password, true); } if(ISPConfig::shallInstall('web') && !ISPConfig::wantsInteractive()) { ISPConfigLog::info('Your ISPConfig admin password is: ' . $ispconfig_admin_pw, true); } ISPConfigLog::info('Your MySQL root password is: ' . $mysql_root_pw, true); return true; } protected function getSystemPHPVersion() { return '7.0'; } }