From 54987a9ce57f4186940c2e00422cf10b3b0ff8c7 Mon Sep 17 00:00:00 2001
From: Michael Seevogel <git@michaelseevogel.de>
Date: Wed, 7 Oct 2020 15:35:05 +0200
Subject: [PATCH] try to detect the correct OpenSSL version and activate TLS
 1.3 if available for Nginx systems

---
 server/conf/nginx_vhost.conf.master           |   7 +-
 .../100-monitor_needs_restarting.inc.php      | 243 ++++++++++++++++++
 server/lib/classes/system.inc.php             |  21 ++
 server/plugins-available/nginx_plugin.inc.php |   4 +
 4 files changed, 274 insertions(+), 1 deletion(-)
 create mode 100644 server/lib/classes/cron.d/100-monitor_needs_restarting.inc.php

diff --git a/server/conf/nginx_vhost.conf.master b/server/conf/nginx_vhost.conf.master
index bfa94f8fb3..f6addcc44d 100644
--- a/server/conf/nginx_vhost.conf.master
+++ b/server/conf/nginx_vhost.conf.master
@@ -18,7 +18,12 @@ server {
         listen <tmpl_var name='ip_address'>:<tmpl_var name='proxy_protocol_https'> ssl proxy_protocol;
 </tmpl_if>
 </tmpl_if>
-		ssl_protocols TLSv1.2;
+
+<tmpl_if name='openssl_version' op='>=' value='1.1.1' format='version'>
+<tmpl_var name="ssl_comment">ssl_protocols TLSv1.3 TLSv1.2;
+<tmpl_else>
+<tmpl_var name="ssl_comment">ssl_protocols TLSv1.2;
+</tmpl_if>
 		# ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
 		# ssl_prefer_server_ciphers on;
 <tmpl_if name='ipv6_enabled'>
diff --git a/server/lib/classes/cron.d/100-monitor_needs_restarting.inc.php b/server/lib/classes/cron.d/100-monitor_needs_restarting.inc.php
new file mode 100644
index 0000000000..cab5f68edb
--- /dev/null
+++ b/server/lib/classes/cron.d/100-monitor_needs_restarting.inc.php
@@ -0,0 +1,243 @@
+<?php
+
+/*
+Copyright (c) 2013, Marius Cramer, pixcept KG
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+class cronjob_monitor_system_update extends cronjob {
+
+	// job schedule
+	protected $_schedule = '0 * * * *';
+	protected $_run_at_new = true;
+
+	private $_tools = null;
+
+	/* this function is optional if it contains no custom code */
+	public function onPrepare() {
+		global $app;
+
+		parent::onPrepare();
+	}
+
+	/* this function is optional if it contains no custom code */
+	public function onBeforeRun() {
+		global $app;
+
+		return parent::onBeforeRun();
+	}
+
+	public function onRunJob() {
+		global $app, $conf;
+
+		$app->uses('getconf');
+		$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
+		if($server_config['monitor_system_updates'] == 'n') return;
+		
+		/* used for all monitor cronjobs */
+		$app->load('monitor_tools');
+		$this->_tools = new monitor_tools();
+		/* end global section for monitor cronjobs */
+
+		/* the id of the server as int */
+		$server_id = intval($conf['server_id']);
+
+		/** The type of the data */
+
+
+		$type = 'system_update';
+
+		/* This monitoring is only available on Debian or Ubuntu */
+		if (file_exists('/etc/debian_version')) {
+
+			/*
+			 * first update the "apt database"
+			 */
+			shell_exec('while fuser /var/lib/apt/lists/lock >/dev/null 2>&1 ; do sleep 2; done; apt-get update');
+
+			/*
+			 * Then test the upgrade.
+			 * if there is any output, then there is a needed update
+			 */
+			$aptData = shell_exec('while fuser /var/lib/dpkg/lock >/dev/null 2>&1 || fuser /var/lib/apt/lists/lock >/dev/null 2>&1 ; do sleep 2; done; apt-get -s -qq dist-upgrade');
+			if ($aptData == '') {
+				/* There is nothing to update! */
+				$state = 'ok';
+			} else {
+				/*
+				 * There is something to update! this is in most cases not critical, so we can
+				 * do a system-update once a month or so...
+				 */
+				$state = 'info';
+			}
+
+			/*
+			 * Fetch the output
+			 */
+			$data['output'] = $aptData;
+		} elseif (file_exists('/etc/gentoo-release')) {
+
+			/*
+			 * first update the portage tree
+			 */
+
+			// In keeping with gentoo's rsync policy, don't update to frequently (every four hours - taken from http://www.gentoo.org/doc/en/source_mirrors.xml)
+			$do_update = true;
+			if (file_exists('/usr/portage/metadata/timestamp.chk')) {
+				$datetime = file_get_contents('/usr/portage/metadata/timestamp.chk');
+				$datetime = trim($datetime);
+
+				$dstamp = strtotime($datetime);
+				if ($dstamp) {
+					$checkat = $dstamp + 14400; // + 4hours
+					if (mktime() < $checkat) {
+						$do_update = false;
+					}
+				}
+			}
+
+			if ($do_update) {
+				shell_exec('emerge --sync --quiet');
+			}
+
+			/*
+			 * Then test the upgrade.
+			 * if there is any output, then there is a needed update
+			 */
+			$emergeData = shell_exec('glsa-check -t affected');
+			if ($emergeData == '') {
+				/* There is nothing to update! */
+				$state = 'ok';
+				$data['output'] = 'No unapplied GLSA\'s found on the system.';
+			} else {
+				/* There is something to update! */
+				$state = 'info';
+				$data['output'] = shell_exec('glsa-check -pv --nocolor affected 2>/dev/null');
+			}
+		} elseif (file_exists('/etc/SuSE-release')) {
+
+			/*
+			 * update and find the upgrade.
+			 * if there is any output, then there is a needed update
+			 */
+			$aptData = shell_exec('zypper -q lu');
+			if ($aptData == '') {
+				/* There is nothing to update! */
+				$state = 'ok';
+			} else {
+				/*
+				 * There is something to update! this is in most cases not critical, so we can
+				 * do a system-update once a month or so...
+				 */
+				$state = 'info';
+			}
+
+			/*
+			 * Fetch the output
+			 */
+			$data['output'] = shell_exec('zypper lu');
+
+		} elseif(file_exists('/etc/redhat-release')) {
+                        /*
+                         * update and find the upgrade.
+                         * if there is any output, then there is a needed update
+                         */
+
+			/* try to figure out the default package manager first */
+                        if(file_exists('/usr/bin/dnf') && (is_link('/usr/bin/yum'))) {
+                                $rhPkgMgr = 'dnf';
+                        } elseif(file_exists('/usr/bin/dnf') && (!file_exists('/usr/bin/yum'))) {
+                                $rhPkgMgr = 'dnf';
+                        } else {
+                                $rhPkgMgr = 'yum';
+                        }
+
+			$aptData = shell_exec($rhPkgMgr. ' -q list updates');
+
+                        if ($aptData == '') {
+                                /* There is nothing to update! */
+                                $state = 'ok';
+                        } else {
+                                /*
+                                 * There is something to update! this is in most cases not critical, so we can
+                                 * do a system-update once a month or so...
+                                 */
+                                $state = 'info';
+                        }
+
+                        /*
+                         * Fetch the output
+			 */
+
+                        $data['output'] = shell_exec($rhPkgMgr. ' -q list updates');
+            
+	        } else {
+			/*
+			 * It is not Debian/Ubuntu, so there is no data and no state
+			 *
+			 * no_state, NOT unknown, because "unknown" is shown as state
+			 * inside the GUI. no_state is hidden.
+			 *
+			 * We have to write NO DATA inside the DB, because the GUI
+			 * could not know, if there is any dat, or not...
+			 */
+			$state = 'no_state';
+			$data['output'] = '';
+		}
+
+		$res = array();
+		$res['server_id'] = $server_id;
+		$res['type'] = $type;
+		$res['data'] = $data;
+		$res['state'] = $state;
+
+		//* Ensure that output is encoded so that it does not break the serialize
+		//$res['data']['output'] = htmlentities($res['data']['output']);
+		$res['data']['output'] = htmlentities($res['data']['output'], ENT_QUOTES, 'UTF-8');
+
+		/*
+		 * Insert the data into the database
+		 */
+		$sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
+			'VALUES (?, ?, UNIX_TIMESTAMP(), ?, ?)';
+		$app->dbmaster->query($sql, $res['server_id'], $res['type'], serialize($res['data']), $res['state']);
+
+		/* The new data is written, now we can delete the old one */
+		$this->_tools->delOldRecords($res['type'], $res['server_id']);
+
+		parent::onRunJob();
+	}
+
+	/* this function is optional if it contains no custom code */
+	public function onAfterRun() {
+		global $app;
+
+		parent::onAfterRun();
+	}
+
+}
+
+?>
diff --git a/server/lib/classes/system.inc.php b/server/lib/classes/system.inc.php
index 32afb9943c..bcaef1f2c4 100644
--- a/server/lib/classes/system.inc.php
+++ b/server/lib/classes/system.inc.php
@@ -2087,6 +2087,27 @@ class system{
 		}
 	}
 
+        function getopensslversion($get_minor = false) {
+                global $app;
+                if($this->is_installed('openssl')) $cmd = 'openssl version';
+                else {
+			$app->log("Could not check OpenSSL version, openssl not found.", LOGLEVEL_DEBUG);
+                        return '1.0.1';
+                }
+                exec($cmd, $output, $return_var);
+                if($return_var != 0 || !$output[0]) {
+			$app->log("Could not check OpenSSL version, openssl did not return any data.", LOGLEVEL_WARN);
+                        return '1.0.1';
+                }
+                if(preg_match('/OpenSSL\s*(\d+)(\.(\d+)(\.(\d+))*)?(\D|$)/i', $output[0], $matches)) {
+			return $matches[1] . (isset($matches[3]) ? '.' . $matches[3] : '') . (isset($matches[5]) && $get_minor == true ? '.' . $matches[5] : '');
+                } else {
+			$app->log("Could not check OpenSSL version, did not find version string in openssl output.", LOGLEVEL_WARN);
+			return '1.0.1';
+                }
+
+        }
+
 	function getapacheversion($get_minor = false) {
 		global $app;
 
diff --git a/server/plugins-available/nginx_plugin.inc.php b/server/plugins-available/nginx_plugin.inc.php
index c361d3f62f..bdc2c0e276 100644
--- a/server/plugins-available/nginx_plugin.inc.php
+++ b/server/plugins-available/nginx_plugin.inc.php
@@ -1621,6 +1621,10 @@ class nginx_plugin {
 		// set logging variable
 		$vhost_data['logging'] = $web_config['logging'];
 
+		$app->log("Found OpenSSL version: " . $app->system->getopensslversion($get_minor = true), LOGLEVEL_DEBUG);
+
+		$vhost_data['openssl_version'] = $app->system->getopensslversion($get_minor = true);
+		
 		$tpl->setVar($vhost_data);
 
 		$server_alias = array();
-- 
GitLab