From e621e02f92f197164b780bfc2179577da4fb006a Mon Sep 17 00:00:00 2001 From: Florian Aders Date: Fri, 19 Feb 2016 17:35:44 +0100 Subject: [PATCH] Allow selecting new keysize, fixes #1594 Prepare database and cron for HSTS, refs #1593 Added option to re-use key and CSR for Let's Encrypt Signed-off-by: Florian Aders --- actions/admin/settings/131.ssl.php | 26 +++++++++++++++++++ install/froxlor.sql | 9 ++++++- install/lib/class.FroxlorInstall.php | 2 ++ .../updates/froxlor/0.9/update_0.9.inc.php | 16 ++++++++++++ lib/classes/ssl/class.lescript.php | 18 ++++++++----- lib/configfiles/gentoo.xml | 13 +++++----- lib/configfiles/jessie.xml | 10 ++++--- lib/configfiles/precise.xml | 10 ++++--- lib/configfiles/rhel_centos.xml | 5 ++-- lib/configfiles/trusty.xml | 14 +++++----- lib/configfiles/wheezy.xml | 14 +++++----- lib/version.inc.php | 2 +- lng/english.lng.php | 6 +++++ lng/german.lng.php | 22 ++++++++++------ scripts/jobs/cron_letsencrypt.php | 7 ++--- .../jobs/cron_tasks.inc.http.10.apache.php | 13 ++++++++++ .../jobs/cron_tasks.inc.http.20.lighttpd.php | 12 +++++++++ scripts/jobs/cron_tasks.inc.http.30.nginx.php | 12 +++++++++ 18 files changed, 163 insertions(+), 48 deletions(-) diff --git a/actions/admin/settings/131.ssl.php b/actions/admin/settings/131.ssl.php index e64ea768..90c7a928 100644 --- a/actions/admin/settings/131.ssl.php +++ b/actions/admin/settings/131.ssl.php @@ -108,6 +108,32 @@ return array( 'default' => 'Germany', 'save_method' => 'storeSettingField', ), + 'system_letsencryptchallengepath' => array( + 'label' => $lng['serversettings']['letsencryptchallengepath'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptchallengepath', + 'type' => 'string', + 'string_emptyallowed' => false, + 'default' => FROXLOR_INSTALL_DIR, + 'save_method' => 'storeSettingField', + ), + 'system_letsencryptkeysize' => array( + 'label' => $lng['serversettings']['letsencryptkeysize'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptkeysize', + 'type' => 'int', + 'int_min' => 2048, + 'default' => 4096, + 'save_method' => 'storeSettingField', + ), + 'system_letsencryptreuseold' => array( + 'label' => $lng['serversettings']['letsencryptreuseold'], + 'settinggroup' => 'system', + 'varname' => 'letsencryptreuseold', + 'type' => 'bool', + 'default' => false, + 'save_method' => 'storeSettingField', + ), ) ) ) diff --git a/install/froxlor.sql b/install/froxlor.sql index febce3ab..30e26fe3 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -251,6 +251,9 @@ CREATE TABLE `panel_domains` ( `mod_fcgid_maxrequests` int(4) default '-1', `ismainbutsubto` int(11) unsigned NOT NULL default '0', `letsencrypt` tinyint(1) NOT NULL default '0', + `hsts` varchar(10) NOT NULL default '0', + `hsts_sub` tinyint(1) NOT NULL default '0', + `hsts_preload` tinyint(1) NOT NULL default '1', PRIMARY KEY (`id`), KEY `customerid` (`customerid`), KEY `parentdomain` (`parentdomainid`), @@ -518,6 +521,9 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('system', 'letsencryptca', 'testing'), ('system', 'letsencryptcountrycode', 'DE'), ('system', 'letsencryptstate', 'Germany'), + ('system', 'letsencryptchallengepath', '/var/www/froxlor'), + ('system', 'letsencryptkeysize', '4096'), + ('system', 'letsencryptreuseold', 0), ('panel', 'decimal_places', '4'), ('panel', 'adminmail', 'admin@SERVERNAME'), ('panel', 'phpmyadmin_url', ''), @@ -548,7 +554,7 @@ INSERT INTO `panel_settings` (`settinggroup`, `varname`, `value`) VALUES ('panel', 'password_numeric', '0'), ('panel', 'password_special_char_required', '0'), ('panel', 'password_special_char', '!?<>§$%+#=@'), - ('panel', 'version', '0.9.35-dev4'); + ('panel', 'version', '0.9.35-dev5'); DROP TABLE IF EXISTS `panel_tasks`; @@ -832,6 +838,7 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` ( `ssl_key_file` mediumtext NOT NULL, `ssl_ca_file` mediumtext, `ssl_cert_chainfile` mediumtext, + `ssl_csr_file` mediumtext, `expirationdate` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM CHARSET=utf8 COLLATE=utf8_general_ci; diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index ef50aa2a..342c6c1c 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -472,6 +472,8 @@ class FroxlorInstall { $this->_updateSetting($upd_stmt, '/etc/nginx/nginx.pem', 'system', 'ssl_cert_file'); $this->_updateSetting($upd_stmt, '/var/run/nginx/', 'phpfpm', 'fastcgi_ipcdir'); } + + $this->_updateSetting($upd_stmt, dirname(dirname(dirname(__FILE__))), 'system', 'letsencryptchallengepath'); // insert the lastcronrun to be the installation date $this->_updateSetting($upd_stmt, time(), 'system', 'lastcronrun'); diff --git a/install/updates/froxlor/0.9/update_0.9.inc.php b/install/updates/froxlor/0.9/update_0.9.inc.php index e274457f..351510b0 100644 --- a/install/updates/froxlor/0.9/update_0.9.inc.php +++ b/install/updates/froxlor/0.9/update_0.9.inc.php @@ -3079,3 +3079,19 @@ if (isFroxlorVersion('0.9.35-dev3')) { updateToVersion('0.9.35-dev4'); } + + +if (isFroxlorVersion('0.9.35-dev4')) { + + showUpdateStep("Adding more Let's Encrypt settings"); + Settings::AddNew("system.letsencryptchallengepath", FROXLOR_INSTALL_DIR); + Settings::AddNew("system.letsencryptkeysize", '4096'); + Settings::AddNew("system.letsencryptreuseold", 0); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` ADD `ssl_csr_file` MEDIUMTEXT AFTER `ssl_cert_chainfile`;"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `hsts` VARCHAR(10) NOT NULL DEFAULT '0' AFTER `letsencrypt`"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `hsts_sub` TINYINT(1) NOT NULL DEFAULT '0' AFTER `hsts`"); + Database::query("ALTER TABLE `".TABLE_PANEL_DOMAINS."` ADD `hsts_preload` TINYINT(1) NOT NULL DEFAULT '1' AFTER `hsts_sub`"); + lastStepStatus(0); + + updateToVersion('0.9.35-dev5'); +} diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index f1134a19..0f12c98e 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -75,7 +75,7 @@ class lescript } } - public function signDomains(array $domains, $domainkey = null) + public function signDomains(array $domains, $domainkey = null, $csr = null) { if (!$this->accountKey) { @@ -117,7 +117,7 @@ class lescript // 2. saving authentication token for web verification // --------------------------------------------------- - $directory = FROXLOR_INSTALL_DIR.'/.well-known/acme-challenge'; + $directory = Settings::Get('system.letsencryptchallengepath').'/.well-known/acme-challenge'; $tokenPath = $directory.'/'.$challenge['token']; if(!file_exists($directory) && !@mkdir($directory, 0755, true)) { @@ -190,7 +190,7 @@ class lescript // ---------------------- // generate private key for domain if not exist - if(empty($domainkey)) { + if(empty($domainkey) || Settings::Get('system.letsencryptreuseold') == 0) { $keys = $this->generateKey(); $domainkey = $keys['private']; } @@ -199,11 +199,15 @@ class lescript $privateDomainKey = openssl_pkey_get_private($domainkey); $this->client->getLastLinks(); + + if (empty($csrfile) || Settings::Get('system.letsencryptreuseold') == 0) { + $csr = $this->generateCSR($privateDomainKey, $domains); + } // request certificates creation $result = $this->signedRequest( "/acme/new-cert", - array('resource' => 'new-cert', 'csr' => $this->generateCSR($privateDomainKey, $domains)) + array('resource' => 'new-cert', 'csr' => $csr) ); if ($this->client->getLastCode() !== 201) { throw new \RuntimeException("Invalid response code: ".$this->client->getLastCode().", ".json_encode($result)); @@ -249,7 +253,7 @@ class lescript $chain = implode("\n", $certificates); $this->log("Done, returning new certificates and key"); - return array('fullchain' => $fullchain, 'crt' => $crt, 'chain' => $chain, 'key' => $domainkey); + return array('fullchain' => $fullchain, 'crt' => $crt, 'chain' => $chain, 'key' => $domainkey, 'csr' => $csr); } private function parsePemFromBody($body) @@ -281,7 +285,7 @@ class lescript 'HOME = . RANDFILE = $ENV::HOME/.rnd [ req ] -default_bits = 4096 +default_bits = ' . Settings::Get('system.letsencryptkeysize') . ' default_keyfile = privkey.pem distinguished_name = req_distinguished_name req_extensions = v3_req @@ -320,7 +324,7 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); { $res = openssl_pkey_new(array( "private_key_type" => OPENSSL_KEYTYPE_RSA, - "private_key_bits" => 4096, + "private_key_bits" => Settings::Get('system.letsencryptkeysize'), )); if(!openssl_pkey_export($res, $privateKey)) { diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 77e0c038..f0bd4723 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -66,8 +66,8 @@ +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Order allow,deny Allow from all @@ -96,8 +96,8 @@ Alias "/.well-known/acme-challenge" "{{const.FROXLOR_INSTALL_DIR}}/.well-known/a +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Require all granted ]]> @@ -126,6 +126,7 @@ server.modules = ( "mod_auth", "mod_fastcgi", "mod_cgi", + "mod_setenv", "mod_accesslog" ) @@ -168,7 +169,7 @@ fastcgi.server = ( ) ) -alias.url += ("/.well-known/acme-challenge/" => "{{const.FROXLOR_INSTALL_DIR}}/.well-known/acme-challenge/") +alias.url += ("/.well-known/acme-challenge/" => "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") ]]> @@ -265,7 +266,7 @@ fastcgi_param REDIRECT_STATUS 200; + @@ -69,8 +70,8 @@ +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Require all granted ]]> @@ -89,6 +90,7 @@ server.modules = ( "mod_compress", "mod_redirect", "mod_rewrite", + "mod_setenv", ) server.document-root = "/var/www" @@ -107,7 +109,7 @@ static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) compress.cache-dir = "/var/cache/lighttpd/compress/" compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) -alias.url += ("/.well-known/acme-challenge/" => "{{const.FROXLOR_INSTALL_DIR}}/.well-known/acme-challenge/") +alias.url += ("/.well-known/acme-challenge/" => "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") # default listening port for IPv6 falls back to the IPv4 port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port @@ -286,7 +288,7 @@ fastcgi_param REDIRECT_STATUS 200; + @@ -67,8 +68,8 @@ +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Order allow,deny Allow from all @@ -97,6 +98,7 @@ server.modules = ( "mod_auth", "mod_fastcgi", "mod_cgi", + "mod_setenv", "mod_accesslog" ) @@ -136,7 +138,7 @@ fastcgi.server = ( ) ) -alias.url += ("/.well-known/acme-challenge/" => "{{const.FROXLOR_INSTALL_DIR}}/.well-known/acme-challenge/") +alias.url += ("/.well-known/acme-challenge/" => "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") #### external configuration files ## mimetype mapping @@ -245,7 +247,7 @@ fastcgi_param REDIRECT_STATUS 200; + @@ -49,8 +50,8 @@ //service[@type='http']/general/commands +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Require all granted ]]> diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index 330b2699..d5640c40 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -41,6 +41,7 @@ + @@ -67,8 +68,8 @@ +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Order Deny,Allow Deny from All @@ -97,8 +98,8 @@ Alias "/.well-known/acme-challenge" "{{const.FROXLOR_INSTALL_DIR}}/.well-known/a +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Require all granted ]]> @@ -126,6 +127,7 @@ server.modules = ( "mod_auth", "mod_fastcgi", "mod_cgi", + "mod_setenv", "mod_accesslog" ) @@ -165,7 +167,7 @@ fastcgi.server = ( ) ) -alias.url += ("/.well-known/acme-challenge/" => "{{const.FROXLOR_INSTALL_DIR}}/.well-known/acme-challenge/") +alias.url += ("/.well-known/acme-challenge/" => "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") #### external configuration files ## mimetype mapping @@ -274,7 +276,7 @@ fastcgi_param REDIRECT_STATUS 200; + @@ -67,8 +68,8 @@ +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Order Deny,Allow Deny from All @@ -97,8 +98,8 @@ Alias "/.well-known/acme-challenge" "{{const.FROXLOR_INSTALL_DIR}}/.well-known/a +Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge" + Require all granted ]]> @@ -116,6 +117,7 @@ server.modules = ( "mod_alias", "mod_compress", "mod_redirect", + "mod_setenv", "mod_rewrite", ) @@ -135,7 +137,7 @@ static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) compress.cache-dir = "/var/cache/lighttpd/compress/" compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) -alias.url += ("/.well-known/acme-challenge/" => "{{const.FROXLOR_INSTALL_DIR}}/.well-known/acme-challenge/") +alias.url += ("/.well-known/acme-challenge/" => "{{settings.system.letsencryptchallengepath}}/.well-known/acme-challenge/") # default listening port for IPv6 falls back to the IPv4 port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port @@ -314,7 +316,7 @@ fastcgi_param REDIRECT_STATUS 200; ATTENTION:Let's Encrypt is still in beta"; $lng['serversettings']['letsencryptstate']['title'] = "Let's Encrypt state"; $lng['serversettings']['letsencryptstate']['description'] = "State used to generate Let's Encrypt certificates.
ATTENTION:Let's Encrypt is still in beta"; +$lng['serversettings']['letsencryptchallengepath']['title'] = "Path for Let's Encrypt challenges"; +$lng['serversettings']['letsencryptchallengepath']['description'] = "Directory where the Let's Encrypt challenges should be offered from via a global alias.
ATTENTION:Let's Encrypt is still in beta"; +$lng['serversettings']['letsencryptkeysize']['title'] = "Key size for new Let's Encrypt certificates"; +$lng['serversettings']['letsencryptkeysize']['description'] = "Size of the key in Bits for new Let's Encrypt certificates.
ATTENTION:Let's Encrypt is still in beta"; +$lng['serversettings']['letsencryptreuseold']['title'] = "Re-use Let's Encrypt key / CSR"; +$lng['serversettings']['letsencryptreuseold']['description'] = "If activated, the same key and CSR will be used for every renew, otherwise a new key / CSR will be generated every time.
ATTENTION:Let's Encrypt is still in beta"; $lng['domains']['ssl_redirect_temporarilydisabled'] = "
The SSL redirect is temporarily deactivated while a new Let's Encrypt certificate is generated. It will be activated again after the certificate was generated."; // Autoupdate diff --git a/lng/german.lng.php b/lng/german.lng.php index 91e84a23..70166614 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1584,20 +1584,26 @@ $lng['admin']['mod_fcgid_umask']['title'] = 'Umask (Standard: 022)'; // Added for let's encrypt $lng['admin']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; -$lng['admin']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlänger.
ACHTUNG:Wenn Wildcards aktiviert sind, wird diese Option automatisch deaktiviert. Dieses Feature befindet sich noch im Test.'; +$lng['admin']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
ACHTUNG:Wenn Wildcards aktiviert sind, wird diese Option automatisch deaktiviert. Dieses Feature befindet sich noch im Test.'; $lng['customer']['letsencrypt']['title'] = 'Benutze Let\'s Encrypt'; -$lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
ACHTUNG:Dieses Feature befindet sich noch im Test.'; -$lng['error']['sslredirectonlypossiblewithsslipport'] = 'Die Nutzung von Let\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.'; +$lng['customer']['letsencrypt']['description'] = 'Holt ein kostenloses Zertifikat von Let\'s Encrypt. Das Zertifikat wird automatisch erstellt und verlängert.
ACHTUNG:Dieses Feature befindet sich noch im Test.'; +$lng['error']['sslredirectonlypossiblewithsslipport'] = 'Die Nutzung von Let\'s Encrypt ist nur möglich, wenn die Domain mindestens eine IP/Port - Kombination mit aktiviertem SSL zugewiesen hat.'; $lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann (noch) nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; $lng['panel']['letsencrypt'] = 'Benutzt Let\'s encrypt'; $lng['crondesc']['cron_letsencrypt'] = 'aktualisiert Let\'s Encrypt Zertifikate'; $lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt Umgebung"; -$lng['serversettings']['letsencryptca']['description'] = "Let's Encrypt - Umgebung, welche genutzt wird um Zertifikate zu bestellen.
ATTENTION:Let's Encrypt befindet sich noch im Test"; -$lng['serversettings']['letsencryptcountrycode']['title'] = "Let's Encrypt Ländercode"; -$lng['serversettings']['letsencryptcountrycode']['description'] = "2 - stelliger Ländercode, welcher benutzt wird um Let's Encrypt - Zertifikate zu bestellen.
ATTENTION:Let's Encrypt befindet sich noch im Test"; +$lng['serversettings']['letsencryptca']['description'] = "Let's Encrypt - Umgebung, welche genutzt wird um Zertifikate zu bestellen.
ACHTUNG:Let's Encrypt befindet sich noch im Test"; +$lng['serversettings']['letsencryptcountrycode']['title'] = "Let's Encrypt Ländercode"; +$lng['serversettings']['letsencryptcountrycode']['description'] = "2 - stelliger Ländercode, welcher benutzt wird um Let's Encrypt - Zertifikate zu bestellen.
ACHTUNG:Let's Encrypt befindet sich noch im Test"; $lng['serversettings']['letsencryptstate']['title'] = "Let's Encrypt Bundesland"; -$lng['serversettings']['letsencryptstate']['description'] = "Bundesland, welches benutzt wird um Let's Encrypt - Zertifikate zu bestellen.
ATTENTION:Let's Encrypt befindet sich noch im Test"; -$lng['domains']['ssl_redirect_temporarilydisabled'] = "
Die SSL-Umleitung ist, während ein neues Let's Encrypt - Zertifikat erstellt wird, temporär deaktiviert. Die Umleitung wird nach der Zertifikatserstellung wieder aktiviert."; +$lng['serversettings']['letsencryptstate']['description'] = "Bundesland, welches benutzt wird um Let's Encrypt - Zertifikate zu bestellen.
ACHTUNG:Let's Encrypt befindet sich noch im Test"; +$lng['serversettings']['letsencryptchallengepath']['title'] = "Verzeichnis für Let's Encrypt challenges"; +$lng['serversettings']['letsencryptchallengepath']['description'] = "Let's Encrypt challenges werden aus diesem Verzeichnis über einen globalen Alias ausgeliefert.
ACHTUNG:Let's Encrypt befindet sich noch im Test"; +$lng['serversettings']['letsencryptkeysize']['title'] = "Schlüsselgröße für neue Let's Encrypt Zertifikate"; +$lng['serversettings']['letsencryptkeysize']['description'] = "Größe des Schlüssels in Bit für neue Let's Encrypt Zertifikate.
ACHTUNG:Let's Encrypt befindet sich noch im Test"; +$lng['serversettings']['letsencryptreuseold']['title'] = "Let's Encrypt Schlüssel / CSR wiederverwenden"; +$lng['serversettings']['letsencryptreuseold']['description'] = "Wenn dies aktiviet ist, werden der alte Schlüssel und CSR bei jeder Verlängerung verwendet, andernfalls wird ein neues Paar generiert.
ACHTUNG:Let's Encrypt befindet sich noch im Test"; +$lng['domains']['ssl_redirect_temporarilydisabled'] = "
Die SSL-Umleitung ist, während ein neues Let's Encrypt - Zertifikat erstellt wird, temporär deaktiviert. Die Umleitung wird nach der Zertifikatserstellung wieder aktiviert."; // Added for Termination-date $lng['domains']['termination_date'] = 'Kündigungsdatum'; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 7ac89a98..dd3f8ffe 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -21,14 +21,14 @@ $cronlog->logAction(CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificates"); $certificates_stmt = Database::query(" - SELECT domssl.`id`, domssl.`domainid`, domssl.expirationdate, domssl.`ssl_cert_file`, domssl.`ssl_key_file`, domssl.`ssl_ca_file`, dom.`domain`, dom.`iswildcarddomain`, dom.`wwwserveralias`, + SELECT domssl.`id`, domssl.`domainid`, domssl.expirationdate, domssl.`ssl_cert_file`, domssl.`ssl_key_file`, domssl.`ssl_ca_file`, domssl.`ssl_csr_file`, dom.`domain`, dom.`iswildcarddomain`, dom.`wwwserveralias`, dom.`documentroot`, dom.`id` as 'domainid', dom.`ssl_redirect`, cust.`leprivatekey`, cust.`lepublickey`, cust.customerid FROM `".TABLE_PANEL_CUSTOMERS."` as cust, `".TABLE_PANEL_DOMAINS."` dom LEFT JOIN `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` domssl ON (dom.id = domssl.domainid) WHERE dom.customerid = cust.customerid AND dom.letsencrypt = 1 AND (domssl.expirationdate < DATE_ADD(NOW(), INTERVAL 30 DAY) OR domssl.expirationdate IS NULL) "); $updcert_stmt = Database::prepare(" - REPLACE INTO `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` SET `id` = :id, `domainid` = :domainid, `ssl_cert_file` = :crt, `ssl_key_file` = :key, `ssl_ca_file` = :ca, `ssl_cert_chainfile` = :fullchain, expirationdate = :expirationdate + REPLACE INTO `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` SET `id` = :id, `domainid` = :domainid, `ssl_cert_file` = :crt, `ssl_key_file` = :key, `ssl_ca_file` = :ca, `ssl_cert_chainfile` = :fullchain, `ssl_csr_file` = :csr, expirationdate = :expirationdate "); $upddom_stmt = Database::prepare(" @@ -71,7 +71,7 @@ while ($certrow = $certificates_stmt->fetch(PDO::FETCH_ASSOC)) { $le->initAccount($certrow); // Request the new certificate (old key may be used) - $return = $le->signDomains($domains, $certrow['ssl_key_file']); + $return = $le->signDomains($domains, $certrow['ssl_key_file'], $certrow['ssl_csr_file']); // We are interessted in the expirationdate $newcert = openssl_x509_parse($return['crt']); @@ -84,6 +84,7 @@ while ($certrow = $certificates_stmt->fetch(PDO::FETCH_ASSOC)) { 'key' => $return['key'], 'ca' => $return['chain'], 'fullchain' => $return['fullchain'], + 'csr' => $return['csr'], 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) ) ); diff --git a/scripts/jobs/cron_tasks.inc.http.10.apache.php b/scripts/jobs/cron_tasks.inc.http.10.apache.php index 4891f5b8..ca6fe141 100644 --- a/scripts/jobs/cron_tasks.inc.http.10.apache.php +++ b/scripts/jobs/cron_tasks.inc.http.10.apache.php @@ -811,6 +811,19 @@ class apache extends HttpConfigBase { if ($domain['ssl_cert_chainfile'] != '') { $vhost_content .= ' SSLCertificateChainFile ' . makeCorrectFile($domain['ssl_cert_chainfile']) . "\n"; } + + if ($domain['hsts'] > 0) { + $vhost_content .= ' ' . "\n"; + $vhost_content .= ' Header always set Strict-Transport-Security "max-age=' . $domain['hsts']; + if ($domain['hsts_sub'] == 1) { + $vhost_content .= '; includeSubdomains'; + } + if ($domain['hsts_preload'] == 1) { + $vhost_content .= '; preload'; + } + $vhost_content .= '"' . "\n"; + $vhost_content .= ' ' . "\n"; + } } else { diff --git a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php index 0b3ec71d..121f1c26 100644 --- a/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php +++ b/scripts/jobs/cron_tasks.inc.http.20.lighttpd.php @@ -518,6 +518,18 @@ class lighttpd extends HttpConfigBase { if ($domain['ssl_ca_file'] != '') { $ssl_settings.= 'ssl.ca-file = "' . makeCorrectFile($domain['ssl_ca_file']) . '"' . "\n"; } + + if ($domain['hsts'] > 0) { + + $vhost_content .= '$HTTP["scheme"] == "https" { setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=' . $domain['hsts']; + if ($domain['hsts_sub'] == 1) { + $vhost_content .= '; includeSubdomains'; + } + if ($domain['hsts_preload'] == 1) { + $vhost_content .= '; preload'; + } + $vhost_content .= '") }' . "\n"; + } } } return $ssl_settings; diff --git a/scripts/jobs/cron_tasks.inc.http.30.nginx.php b/scripts/jobs/cron_tasks.inc.http.30.nginx.php index 82f77b61..4bff60a1 100644 --- a/scripts/jobs/cron_tasks.inc.http.30.nginx.php +++ b/scripts/jobs/cron_tasks.inc.http.30.nginx.php @@ -591,6 +591,18 @@ class nginx extends HttpConfigBase { $sslsettings.= "\t" . 'ssl_client_certificate ' . makeCorrectFile($domain_or_ip['ssl_ca_file']) . ';' . "\n"; } } + + if ($domain['hsts'] > 0) { + + $vhost_content .= 'add_header Strict-Transport-Security "max-age=' . $domain['hsts']; + if ($domain['hsts_sub'] == 1) { + $vhost_content .= '; includeSubdomains'; + } + if ($domain['hsts_preload'] == 1) { + $vhost_content .= '; preload'; + } + $vhost_content .= '";' . "\n"; + } } }