From 2856951fba5db327c5710391ea24e231406127ae Mon Sep 17 00:00:00 2001
From: Hj Ahmad Rasyid Hj Ismail <ahrasis@gmail.com>
Date: Tue, 8 Sep 2020 13:14:09 +0200
Subject: [PATCH] Update installer_base.lib.php to get LE SSL certs for the
 server via certbot or acme.sh before openssl self-signed method upon new
 installation or existing update; and extend it to other available services
 (postfix, pure-ftpd-mysql), with additional dhparam pem file, if none exists.

---
 install/install.php                      |   6 +
 install/lib/installer_base.lib.php       | 270 +++++++++++++++++++++--
 install/uninstall.php                    |   3 +
 install/update.php                       |   6 +
 server/scripts/letsencrypt_post_hook.sh  |  45 ++++
 server/scripts/letsencrypt_pre_hook.sh   |  46 ++++
 server/scripts/letsencrypt_renew_hook.sh |  47 ++++
 7 files changed, 404 insertions(+), 19 deletions(-)
 create mode 100644 server/scripts/letsencrypt_post_hook.sh
 create mode 100644 server/scripts/letsencrypt_pre_hook.sh
 create mode 100644 server/scripts/letsencrypt_renew_hook.sh

diff --git a/install/install.php b/install/install.php
index 9dff3facf2..57d00269fd 100644
--- a/install/install.php
+++ b/install/install.php
@@ -574,6 +574,12 @@ if($install_mode == 'standard' || strtolower($inst->simple_query('Install ISPCon
 	$inst->install_ispconfig_interface = false;
 }
 
+// Create SSL certs for non-webserver(s)?
+if(!file_exists('/usr/local/ispconfig/interface/ssl/ispserver.crt')) {
+    if(strtolower($inst->simple_query('Do you want to create SSL certs for your server?', array('y', 'n'), 'y')) == 'y')
+        $inst->make_ispconfig_ssl_cert();
+}
+
 $inst->install_ispconfig();
 
 //* Configure DBServer
diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php
index 70ce20c7c8..ff0df827a0 100644
--- a/install/lib/installer_base.lib.php
+++ b/install/lib/installer_base.lib.php
@@ -1,7 +1,7 @@
 <?php
 
 /*
-Copyright (c) 2007-2010, Till Brehm, projektfarm Gmbh
+Copyright (c) 2007-2019, Till Brehm, projektfarm GmbH
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -2672,34 +2672,254 @@ class installer_base {
 			if(!@is_link($vhost_conf_enabled_dir.'/000-apps.vhost')) {
 				symlink($vhost_conf_dir.'/apps.vhost', $vhost_conf_enabled_dir.'/000-apps.vhost');
 			}
+		}
+	}
 
+	private function curl_request($url, $use_ipv6 = false) {
+		$set_headers = [
+			'Connection: Close',
+			'User-Agent: ISPConfig/3',
+			'Accept: */*'
+		];
+
+		$ch = curl_init($url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
+        curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $set_headers);
+		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+		curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
+
+		if($use_ipv6) {
+			if(defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V6')) {
+				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
+			}
+		} else {
+			if(defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) {
+				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+			}
 		}
+
+		$response = curl_exec($ch);
+		curl_close($ch);
+
+		return $response;
 	}
 
 	public function make_ispconfig_ssl_cert() {
-		global $conf,$autoinstall;
+		global $conf, $autoinstall;
 
-		$install_dir = $conf['ispconfig_install_dir'];
+		//* Get hostname from user entry or shell command */
+		if($conf['hostname'] !== 'localhost' && $conf['hostname'] !== '') {
+			$hostname = $conf['hostname'];
+		} else {
+			$hostname = exec('hostname -f');
+		}
+
+		// Check dns a record exist and its ip equal to server public ip
+		$svr_ip4 = $this->curl_request('https://ispconfig.org/remoteip.php', false);
+		$svr_ip6 = $this->curl_request('https://ispconfig.org/remoteip.php', true);
+
+		if(function_exists('idn_to_ascii')) {
+			if(defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46') && constant('IDNA_NONTRANSITIONAL_TO_ASCII')) {
+				$hostname = idn_to_ascii($hostname, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+			} else {
+				$hostname = idn_to_ascii($hostname);
+			}
+		}
+		$dns_ips = array();
+		if (checkdnsrr($hostname, 'A')) {
+			$dnsa=dns_get_record($hostname, DNS_A);
+			if($dnsa) {
+				foreach ($dnsa as $rec) {
+					$dns_ips[] = $rec['ip'];
+				}
+			}
+		}
+		if (checkdnsrr($hostname, 'AAAA')) {
+			$dnsaaaa=dns_get_record($hostname, DNS_AAAA);
+			if($dnsaaaa) {
+				foreach ($dnsaaaa as $rec) {
+					$dns_ips[] = $rec['ip'];
+				}
+			}
+		}
+
+		// Request for certs if no LE SSL folder for server fqdn exist
+		$le_live_dir = '/etc/letsencrypt/live/' . $hostname;
+		if (!@is_dir($le_live_dir) && (
+				($svr_ip4 && in_array($svr_ip4, $dns_ips)) || ($svr_ip6 && in_array($svr_ip6, $dns_ips))
+			)) {
+
+			// This script is needed earlier to check and open http port 80 or standalone might fail
+			// Make executable and temporary symlink latest letsencrypt pre, post and renew hook script before install
+			if(file_exists(dirname(getcwd()) . '/server/scripts/letsencrypt_pre_hook.sh')) {
+				symlink(dirname(getcwd()) . '/server/scripts/letsencrypt_pre_hook.sh', '/usr/local/bin/letsencrypt_pre_hook.sh');
+			}
+			if(file_exists(dirname(getcwd()) . '/server/scripts/letsencrypt_post_hook.sh')) {
+				symlink(dirname(getcwd()) . '/server/scripts/letsencrypt_post_hook.sh', '/usr/local/bin/letsencrypt_post_hook.sh');
+			}
+			if(file_exists(dirname(getcwd()) . '/server/scripts/letsencrypt_renew_hook.sh')) {
+				symlink(dirname(getcwd()) . '/server/scripts/letsencrypt_renew_hook.sh', '/usr/local/bin/letsencrypt_renew_hook.sh');
+			}
+			chown('/usr/local/bin/letsencrypt_pre_hook.sh', 'root');
+			chown('/usr/local/bin/letsencrypt_post_hook.sh', 'root');
+			chown('/usr/local/bin/letsencrypt_renew_hook.sh', 'root');
+			chmod('/usr/local/bin/letsencrypt_pre_hook.sh', 0700);
+			chmod('/usr/local/bin/letsencrypt_post_hook.sh', 0700);
+			chmod('/usr/local/bin/letsencrypt_renew_hook.sh', 0700);
 
-		$ssl_crt_file = $install_dir.'/interface/ssl/ispserver.crt';
-		$ssl_csr_file = $install_dir.'/interface/ssl/ispserver.csr';
-		$ssl_key_file = $install_dir.'/interface/ssl/ispserver.key';
+			// Check http port 80 status as it cannot be determined at post hook stage
+			$port80_status=exec('true &>/dev/null </dev/tcp/127.0.0.1/80 && echo open || echo close');
+
+			// Set pre-, post- and renew hook
+			$pre_hook = "--pre-hook \"letsencrypt_pre_hook.sh\"";
+			$renew_hook = "  --renew-hook \"letsencrypt_renew_hook.sh\"";
+			if($port80_status == 'close') {
+				$post_hook = " --post-hook \"letsencrypt_post_hook.sh\"";
+				$hook = $pre_hook . $post_hook . $renew_hook;
+			} else {
+				$hook = $pre_hook . $renew_hook;
+			}
+
+			// Get the default LE client name and version
+			$le_client = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
+			$le_client = reset($le_client);
+
+			// Check for Neilpang acme.sh as well
+			$acme = explode("\n", shell_exec('which /usr/local/ispconfig/server/scripts/acme.sh /root/.acme.sh/acme.sh'));
+			$acme = reset($acme);
+
+			// Attempt to use Neilpang acme.sh first, as it is now the preferred LE client
+			if (is_executable($acme)) {
+
+				if($conf['nginx']['installed'] == true) {
+					exec("$acme --issue --nginx -d $hostname $renew_hook");
+				} elseif($conf['apache']['installed'] == true) {
+					exec("$acme --issue --apache -d $hostname $renew_hook");
+				}
+				// Else, it is not webserver, so we use standalone
+				else {
+					exec("$acme --issue --standalone -d $hostname $hook");
+				}
 
-		if(!@is_dir($install_dir.'/interface/ssl')) mkdir($install_dir.'/interface/ssl', 0755, true);
+				// Define LE certs name and path, then install them
+				if (!@is_dir($le_live_dir)) mkdir($le_live_dir, 0755, true);
+				$acme_cert = "--cert-file $le_live_dir/cert.pem";
+				$acme_key = "--key-file $le_live_dir/privkey.pem";
+				$acme_ca = "--ca-file $le_live_dir/chain.pem";
+				$acme_chain = "--fullchain-file $le_live_dir/fullchain.pem";
+				exec("$acme --install-cert -d $hostname $acme_cert $acme_key $acme_ca $acme_chain");
+
+			// Else, we attempt to use the official LE certbot client certbot
+			} else {
+
+				//  But only if it is otherwise available
+				if(is_executable($le_client)) {
+
+					// Get its version info due to be used for webroot arguement issues
+					$le_info = exec($le_client . ' --version  2>&1', $ret, $val);
+					if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $le_info, $matches)) {
+						$le_version = $matches[2];
+					}
+
+					// Define certbot commands
+					$acme_version = '--server https://acme-v0' . (($le_version >=0.22) ? '2' : '1') . '.api.letsencrypt.org/directory';
+					$certonly = 'certonly --agree-tos --non-interactive --expand --rsa-key-size 4096';
+
+					// If this is a webserver
+					if($conf['nginx']['installed'] == true)
+						exec("$le_client $certonly $acme_version --nginx --email postmaster@$hostname $renew_hook");
+					elseif($conf['apache']['installed'] == true)
+						exec("$le_client $certonly $acme_version --apache --email postmaster@$hostname $renew_hook");
+					// Else, it is not webserver, so we use standalone
+					else
+						exec("$le_client $certonly $acme_version --standalone --email postmaster@$hostname -d $hostname $hook");
+				}
+			}
+		}
+
+		//* Define and check ISPConfig SSL folder */
+		$ssl_dir = $conf['ispconfig_install_dir'].'/interface/ssl';
+		if(!@is_dir($ssl_dir)) mkdir($ssl_dir, 0755, true);
+
+		$ssl_crt_file = $ssl_dir.'/ispserver.crt';
+		$ssl_csr_file = $ssl_dir.'/ispserver.csr';
+		$ssl_key_file = $ssl_dir.'/ispserver.key';
+		$ssl_pem_file = $ssl_dir.'/ispserver.pem';
+
+		$date = new DateTime();
+
+		// If the LE SSL certs for this hostname exists
+		if (is_dir($le_live_dir) && in_array($svr_ip, $dns_ips)) {
+
+			// Backup existing ispserver ssl files
+			if (file_exists($ssl_crt_file)) rename($ssl_crt_file, $ssl_crt_file . '-' .$date->format('YmdHis') . '.bak');
+			if (file_exists($ssl_key_file)) rename($ssl_key_file, $ssl_key_file . '-' .$date->format('YmdHis') . '.bak');
+			if (file_exists($ssl_pem_file)) rename($ssl_pem_file, $ssl_pem_file . '-' .$date->format('YmdHis') . '.bak');
+
+			// Create symlink to LE fullchain and key for ISPConfig
+			symlink($le_live_dir.'/fullchain.pem', $ssl_crt_file);
+			symlink($le_live_dir.'/privkey.pem', $ssl_key_file);
 
-		$ssl_pw = substr(md5(mt_rand()), 0, 6);
-		exec("openssl genrsa -des3 -passout pass:$ssl_pw -out $ssl_key_file 4096");
-		if(AUTOINSTALL){
-			exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -subj '/C=".escapeshellcmd($autoinstall['ssl_cert_country'])."/ST=".escapeshellcmd($autoinstall['ssl_cert_state'])."/L=".escapeshellcmd($autoinstall['ssl_cert_locality'])."/O=".escapeshellcmd($autoinstall['ssl_cert_organisation'])."/OU=".escapeshellcmd($autoinstall['ssl_cert_organisation_unit'])."/CN=".escapeshellcmd($autoinstall['ssl_cert_common_name'])."' -key $ssl_key_file -out $ssl_csr_file");
 		} else {
-			exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -out $ssl_csr_file");
+
+			// We can still use the old self-signed method
+			$ssl_pw = substr(md5(mt_rand()), 0, 6);
+			exec("openssl genrsa -des3 -passout pass:$ssl_pw -out $ssl_key_file 4096");
+			if(AUTOINSTALL){
+				exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -subj '/C=".escapeshellcmd($autoinstall['ssl_cert_country'])."/ST=".escapeshellcmd($autoinstall['ssl_cert_state'])."/L=".escapeshellcmd($autoinstall['ssl_cert_locality'])."/O=".escapeshellcmd($autoinstall['ssl_cert_organisation'])."/OU=".escapeshellcmd($autoinstall['ssl_cert_organisation_unit'])."/CN=".escapeshellcmd($autoinstall['ssl_cert_common_name'])."' -key $ssl_key_file -out $ssl_csr_file");
+			} else {
+				exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -out $ssl_csr_file");
+			}
+			exec("openssl req -x509 -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -in $ssl_csr_file -out $ssl_crt_file -days 3650");
+			exec("openssl rsa -passin pass:$ssl_pw -in $ssl_key_file -out $ssl_key_file.insecure");
+			rename($ssl_key_file, $ssl_key_file.'.secure');
+			rename($ssl_key_file.'.insecure', $ssl_key_file);
 		}
-		exec("openssl req -x509 -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -in $ssl_csr_file -out $ssl_crt_file -days 3650");
-		exec("openssl rsa -passin pass:$ssl_pw -in $ssl_key_file -out $ssl_key_file.insecure");
-		rename($ssl_key_file, $ssl_key_file.'.secure');
-		rename($ssl_key_file.'.insecure', $ssl_key_file);
 
-		exec('chown -R root:root /usr/local/ispconfig/interface/ssl');
+		// Build ispserver.pem file and chmod it
+		exec("cat $ssl_key_file $ssl_crt_file > $ssl_pem_file; chmod 600 $ssl_pem_file");
+
+		// Extend LE SSL certs to postfix
+		if ($conf['postfix']['installed'] == true && strtolower($this->simple_query('Symlink ISPConfig LE SSL certs to postfix?', array('y', 'n'), 'y')) == 'y') {
+
+			// Define folder, file(s)
+			$cf = $conf['postfix'];
+			$postfix_dir = $cf['config_dir'];
+			if(!is_dir($postfix_dir)) $this->error("The postfix configuration directory '$postfix_dir' does not exist.");
+			$smtpd_crt = $postfix_dir.'/smtpd.cert';
+			$smtpd_key = $postfix_dir.'/smtpd.key';
+
+			// Backup existing postfix ssl files
+			if (file_exists($smtpd_crt)) rename($smtpd_crt, $smtpd_crt . '-' .$date->format('YmdHis') . '.bak');
+			if (file_exists($smtpd_key)) rename($smtpd_key, $smtpd_key . '-' .$date->format('YmdHis') . '.bak');
+
+			// Create symlink to ISPConfig SSL files
+			symlink($ssl_crt_file, $smtpd_crt);
+			symlink($ssl_key_file, $smtpd_key);
+		}
+
+		// Extend LE SSL certs to pureftpd
+		if ($conf['pureftpd']['installed'] == true && strtolower($this->simple_query('Symlink ISPConfig LE SSL certs to pureftpd? Creating dhparam file takes some times.', array('y', 'n'), 'y')) == 'y') {
+
+			// Define folder, file(s)
+			$pureftpd_dir = '/etc/ssl/private';
+			if(!is_dir($pureftpd_dir)) mkdir($pureftpd_dir, 0755, true);
+			$pureftpd_pem = $pureftpd_dir.'/pure-ftpd.pem';
+
+			// Backup existing pureftpd ssl files
+			if (file_exists($pureftpd_pem)) rename($pureftpd_pem, $pureftpd_pem . '-' .$date->format('YmdHis') . '.bak');
+
+			// Create symlink to ISPConfig SSL files
+			symlink($ssl_pem_file, $pureftpd_pem);
+			if (!file_exists("$pureftpd_dir/pure-ftpd-dhparams.pem"))
+				exec("cd $pureftpd_dir; openssl dhparam -out dhparam2048.pem 2048; ln -sf dhparam2048.pem pure-ftpd-dhparams.pem");
+		}
+
+		exec("chown -R root:root $ssl_dir");
 
 	}
 
@@ -3122,6 +3342,20 @@ class installer_base {
 		if(!is_link('/usr/local/bin/ispconfig_update_from_dev.sh')) symlink($install_dir.'/server/scripts/ispconfig_update.sh', '/usr/local/bin/ispconfig_update_from_dev.sh');
 		if(!is_link('/usr/local/bin/ispconfig_update.sh')) symlink($install_dir.'/server/scripts/ispconfig_update.sh', '/usr/local/bin/ispconfig_update.sh');
 
+		// Make executable then unlink and symlink letsencrypt pre, post and renew hook scripts
+		chown($install_dir.'/server/scripts/letsencrypt_pre_hook.sh', 'root');
+		chown($install_dir.'/server/scripts/letsencrypt_post_hook.sh', 'root');
+		chown($install_dir.'/server/scripts/letsencrypt_renew_hook.sh', 'root');
+		chmod($install_dir.'/server/scripts/letsencrypt_pre_hook.sh', 0700);
+		chmod($install_dir.'/server/scripts/letsencrypt_post_hook.sh', 0700);
+		chmod($install_dir.'/server/scripts/letsencrypt_renew_hook.sh', 0700);
+		if(is_link('/usr/local/bin/letsencrypt_pre_hook.sh')) unlink('/usr/local/bin/letsencrypt_pre_hook.sh');
+		if(is_link('/usr/local/bin/letsencrypt_post_hook.sh')) unlink('/usr/local/bin/letsencrypt_post_hook.sh');
+		if(is_link('/usr/local/bin/letsencrypt_renew_hook.sh')) unlink('/usr/local/bin/letsencrypt_renew_hook.sh');
+		symlink($install_dir.'/server/scripts/letsencrypt_pre_hook.sh', '/usr/local/bin/letsencrypt_pre_hook.sh');
+		symlink($install_dir.'/server/scripts/letsencrypt_post_hook.sh', '/usr/local/bin/letsencrypt_post_hook.sh');
+		symlink($install_dir.'/server/scripts/letsencrypt_renew_hook.sh', '/usr/local/bin/letsencrypt_renew_hook.sh');
+
 		//* Make the logs readable for the ispconfig user
 		if(@is_file('/var/log/mail.log')) exec('chmod +r /var/log/mail.log');
 		if(@is_file('/var/log/mail.warn')) exec('chmod +r /var/log/mail.warn');
@@ -3462,5 +3696,3 @@ class installer_base {
 	}
 
 }
-
-?>
diff --git a/install/uninstall.php b/install/uninstall.php
index fdac79d61e..37cbe05f86 100644
--- a/install/uninstall.php
+++ b/install/uninstall.php
@@ -88,6 +88,9 @@ if($do_uninstall == 'yes') {
 	exec('rm -rf /usr/local/ispconfig');
 
 	// Delete various other files
+	@unlink("/usr/local/bin/letsencrypt_post_hook.sh");
+	@unlink("/usr/local/bin/letsencrypt_pre_hook.sh");
+	@unlink("/usr/local/bin/letsencrypt_renew_hook.sh");
 	@unlink("/usr/local/bin/ispconfig_update.sh");
 	@unlink("/usr/local/bin/ispconfig_update_from_svn.sh");
 	@unlink("/var/spool/mail/ispconfig");
diff --git a/install/update.php b/install/update.php
index 3b3cf969ef..cfb3d454d5 100644
--- a/install/update.php
+++ b/install/update.php
@@ -534,6 +534,12 @@ if ($inst->install_ispconfig_interface) {
 	}
 }
 
+// Create SSL certs for non-webserver(s)?
+if(!file_exists('/usr/local/ispconfig/interface/ssl/ispserver.crt')) {
+    if(strtolower($inst->simple_query('Do you want to create SSL certs for your server?', array('y', 'n'), 'y')) == 'y')
+        $inst->make_ispconfig_ssl_cert();
+}
+
 $inst->install_ispconfig();
 
 // Cleanup
diff --git a/server/scripts/letsencrypt_post_hook.sh b/server/scripts/letsencrypt_post_hook.sh
new file mode 100644
index 0000000000..02653f79a1
--- /dev/null
+++ b/server/scripts/letsencrypt_post_hook.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides: LETSENCRYPT POST HOOK SCRIPT
+# Required-Start:  $local_fs $network
+# Required-Stop:  $local_fs
+# Default-Start:  2 3 4 5
+# Default-Stop:  0 1 6
+# Short-Description: LETSENCRYPT POST HOOK SCRIPT
+# Description:  To force close http port 80 if it is by default closed, to be used by letsencrypt client standlone command
+### END INIT INFO
+
+## If you need a custom hook file, create a file with the same name in
+## /usr/local/ispconfig/server/conf-custom/scripts/
+if [[ -e "/usr/local/ispconfig/server/conf-custom/scripts/letsencrypt_post_hook.sh" ]] ; then
+	. /usr/local/ispconfig/server/conf-custom/scripts/letsencrypt_post_hook.sh && exit 0 || exit 1;
+fi
+
+# You can add support to other firewall
+
+# For RHEL, Centos or derivatives
+if which yum &> /dev/null 2>&1 ; then
+    # Check if web server software is installed, start it if any
+    if [ rpm -q nginx ]; then service nginx start
+    elif [ rpm -q httpd ]; then service httpd start
+    # If using firewalld
+    elif [ rpm -q firewalld ] && [ `firewall-cmd --state` = running ]; then
+        firewall-cmd --zone=public --permanent --remove-service=http
+        firewall-cmd --reload
+    # If using UFW
+    else; if [ rpm -q ufw ]; then ufw --force enable && ufw deny http; fi
+    fi
+# For Debian, Ubuntu or derivatives
+elif apt-get -v >/dev/null 2>&1 ; then
+    # Check if web server software is installed, stop it if any
+    if [ $(dpkg-query -W -f='${Status}' nginx 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service nginx start
+    elif [ $(dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service apache2 start
+    # If using UFW
+    else; if [ $(dpkg-query -W -f='${Status}' ufw 2>/dev/null | grep -c "ok installed") -eq 1 ]; then ufw --force enable && ufw deny http; fi
+    fi
+# Try iptables as a final attempt
+else
+    iptables -D INPUT  -p tcp  --dport 80    -j ACCEPT
+    service iptables save
+fi
\ No newline at end of file
diff --git a/server/scripts/letsencrypt_pre_hook.sh b/server/scripts/letsencrypt_pre_hook.sh
new file mode 100644
index 0000000000..56f246e803
--- /dev/null
+++ b/server/scripts/letsencrypt_pre_hook.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides: LETSENCRYPT PRE HOOK SCRIPT
+# Required-Start:  $local_fs $network
+# Required-Stop:  $local_fs
+# Default-Start:  2 3 4 5
+# Default-Stop:  0 1 6
+# Short-Description: LETSENCRYPT PRE HOOK SCRIPT
+# Description:  To force open http port 80 to be used by letsencrypt client standlone command
+### END INIT INFO
+
+## If you need a custom hook file, create a file with the same name in
+## /usr/local/ispconfig/server/conf-custom/scripts/
+if [[ -e "/usr/local/ispconfig/server/conf-custom/scripts/letsencrypt_pre_hook.sh" ]] ; then
+	. /usr/local/ispconfig/server/conf-custom/scripts/letsencrypt_pre_hook.sh && exit 0 || exit 1 ;
+fi
+
+# You can add support to other firewall
+
+# For RHEL, Centos or derivatives
+if which yum &> /dev/null 2>&1 ; then
+    # Check if web server software is installed, stop it if any
+    if [ rpm -q nginx ]; then service nginx stop; fi
+    if [ rpm -q httpd ]; then service httpd stop; fi
+    # If using firewalld
+    if [ rpm -q firewalld ] && [ `firewall-cmd --state` = running ]; then
+        firewall-cmd --zone=public --permanent --add-service=http
+        firewall-cmd --reload
+    fi
+    # If using UFW
+    if [ rpm -q ufw ]; then ufw --force enable && ufw allow http; fi
+
+# For Debian, Ubuntu or derivatives
+elif apt-get -v >/dev/null 2>&1 ; then
+    # Check if web server software is installed, stop it if any
+    if [ $(dpkg-query -W -f='${Status}' nginx 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service nginx stop; fi
+    if [ $(dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service apache2 stop; fi
+    # If using UFW
+    if [ $(dpkg-query -W -f='${Status}' ufw 2>/dev/null | grep -c "ok installed") -eq 1 ]; then ufw --force enable && ufw allow http; fi
+    
+# Try iptables as a final attempt
+else
+    iptables -I INPUT  -p tcp  --dport 80    -j ACCEPT
+    service iptables save
+fi
diff --git a/server/scripts/letsencrypt_renew_hook.sh b/server/scripts/letsencrypt_renew_hook.sh
new file mode 100644
index 0000000000..0a71f30d01
--- /dev/null
+++ b/server/scripts/letsencrypt_renew_hook.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides: LETSENCRYPT RENEW HOOK SCRIPT
+# Required-Start:  $local_fs $network
+# Required-Stop:  $local_fs
+# Default-Start:  2 3 4 5
+# Default-Stop:  0 1 6
+# Short-Description: LETSENCRYPT RENEW HOOK SCRIPT
+# Description:  Taken from LE4ISPC code. To be used to update ispserver.pem automatically after ISPConfig LE SSL certs are renewed and to reload / restart important ISPConfig server services
+### END INIT INFO
+
+## If you need a custom hook file, create a file with the same name in
+## /usr/local/ispconfig/server/conf-custom/scripts/
+if [[ -e "/usr/local/ispconfig/server/conf-custom/scripts/letsencrypt_renew_hook.sh" ]] ; then
+	. /usr/local/ispconfig/server/conf-custom/scripts/letsencrypt_renew_hook.sh && exit 0 || exit 1;
+fi
+
+lelive=/etc/letsencrypt/live/$(hostname -f); if [ -d "$lelive" ]; then
+    cd /usr/local/ispconfig/interface/ssl; ibak=ispserver.*.bak; ipem=ispserver.pem; icrt=ispserver.crt; ikey=ispserver.key
+    if ls $ibak 1> /dev/null 2>&1; then rm $ibak; fi
+    if [ -e "$ipem" ]; then mv $ipem $ipem-$(date +"%y%m%d%H%M%S").bak; cat $ikey $icrt > $ipem; chmod 600 $ipem; fi
+    pureftpdpem=/etc/ssl/private/pure-ftpd.pem; if [ -e "$pureftpdpem" ]; then chmod 600 $pureftpdpem; fi
+    # For Red Hat, Centos or derivatives
+    if which yum &> /dev/null 2>&1 ; then
+        if [ rpm -q pure-ftpd ]; then service pure-ftpd restart; fi
+        if [ rpm -q monit ]; then service monit restart; fi
+        if [ rpm -q postfix ]; then service postfix restart; fi
+        if [ rpm -q dovecot ]; then service dovecot restart; fi
+        if [ rpm -q mysql-server ]; then service mysqld restart; fi
+        if [ rpm -q mariadb-server ]; then service mariadb restart; fi
+        if [ rpm -q MariaDB-server ]; then service mysql restart; fi
+        if [ rpm -q nginx ]; then service nginx restart; fi
+        if [ rpm -q httpd ]; then service httpd restart; fi
+    # For Debian, Ubuntu or derivatives
+    elif apt-get -v >/dev/null 2>&1 ; then
+        if [ $(dpkg-query -W -f='${Status}' pure-ftpd-mysql 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service pure-ftpd-mysql restart; fi
+        if [ $(dpkg-query -W -f='${Status}' monit 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service monit restart; fi
+        if [ $(dpkg-query -W -f='${Status}' postfix 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service postfix restart; fi
+        if [ $(dpkg-query -W -f='${Status}' dovecot-imapd 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service dovecot restart; fi
+        if [ $(dpkg-query -W -f='${Status}' mysql 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service mysql restart; fi
+        if [ $(dpkg-query -W -f='${Status}' mariadb 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service mysql restart; fi
+        if [ $(dpkg-query -W -f='${Status}' nginx 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service nginx restart; fi
+        if [ $(dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -c "ok installed") -eq 1 ]; then service apache2 restart; fi
+    else
+    fi
+else echo `/bin/date` "Your Lets Encrypt SSL certs path for your ISPConfig server FQDN is missing.$line" >> /var/log/ispconfig/ispconfig.log; fi
\ No newline at end of file
-- 
GitLab