diff --git a/admin_customers.php b/admin_customers.php index dd7add11..0c96941c 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -940,7 +940,8 @@ if ($page == 'customers' WHERE `id` = :defaultip "); $default_ips = Settings::Get('system.defaultip'); - $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset(explode(',', $default_ips)))); + $default_ips = explode(',', $default_ips); + $srv_ip = Database::pexecute_first($srv_ip_stmt, array('defaultip' => reset($default_ips))); $replace_arr = array( 'FIRSTNAME' => $firstname, diff --git a/admin_domains.php b/admin_domains.php index 51de4672..be9f508e 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -245,6 +245,8 @@ if ($page == 'domains' || $page == 'overview') { 'domainid' => $id )); + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + $log->logAction(ADM_ACTION, LOG_INFO, "deleted domain/subdomains (#" . $result['id'] . ")"); updateCounters(); inserttask('1'); @@ -672,10 +674,6 @@ if ($page == 'domains' || $page == 'overview') { $issubof = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - if ($domain == '') { standard_error(array( 'stringisempty', @@ -843,6 +841,9 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { Database::pexecute($ins_stmt, $ins_data); } } + + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + $log->logAction(ADM_ACTION, LOG_INFO, "added domain '" . $domain . "'"); inserttask('1'); @@ -1472,10 +1473,6 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { $issubof = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - if ($serveraliasoption != '1' && $serveraliasoption != '2') { $serveraliasoption = '0'; } @@ -1802,6 +1799,15 @@ elseif (Settings::Get('system.validate_domain') && ! validateDomain($domain)) { } } } + if ($result['aliasdomain'] != $aliasdomain) { + // trigger when domain id for alias destination has changed: both for old and new destination + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } else + if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } $log->logAction(ADM_ACTION, LOG_INFO, "edited domain #" . $id); redirectTo($filename, array( diff --git a/customer_domains.php b/customer_domains.php index 28f4833a..daccf9f5 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -171,7 +171,7 @@ if ($page == 'overview') { eval("echo \"" . getTemplate("domains/domainlist") . "\";"); } elseif ($action == 'delete' && $id != 0) { - $stmt = Database::prepare("SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid` FROM `" . TABLE_PANEL_DOMAINS . "` + $stmt = Database::prepare("SELECT `id`, `customerid`, `domain`, `documentroot`, `isemaildomain`, `parentdomainid`, `aliasdomain` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid AND `id` = :id" ); @@ -197,6 +197,8 @@ if ($page == 'overview') { } } + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + $log->logAction(USR_ACTION, LOG_INFO, "deleted subdomain '" . $idna_convert->decode($result['domain']) . "'"); $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :customerid @@ -290,6 +292,7 @@ if ($page == 'overview') { ORDER BY `d`.`domain` ASC;" ); $aliasdomain_check = Database::pexecute_first($aliasdomain_stmt, array("id" => $aliasdomain, "customerid" => $userinfo['customerid'])); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); } if (isset($_POST['url']) && $_POST['url'] != '' && validateUrl($idna_convert->encode($_POST['url']))) { @@ -342,11 +345,6 @@ if ($page == 'overview') { } } - if ($aliasdomain != 0 && $letsencrypt != 0) - { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - // Temporarily deactivate ssl_redirect until Let's Encrypt certificate was generated if ($ssl_redirect > 0 && $letsencrypt == 1) { $ssl_redirect = 2; @@ -610,11 +608,6 @@ if ($page == 'overview') { $letsencrypt = '0'; } - if ($aliasdomain != 0 && $letsencrypt != 0) - { - standard_error('letsencryptdoesnotworkwithaliasdomains'); - } - // We can't enable let's encrypt for wildcard - domains if ($iswildcarddomain == '1' && $letsencrypt == '1') { standard_error('nowildcardwithletsencrypt'); @@ -677,6 +670,17 @@ if ($page == 'overview') { "id" => $id ); Database::pexecute($stmt, $params); + + if ($result['aliasdomain'] != $aliasdomain) { + // trigger when domain id for alias destination has changed: both for old and new destination + triggerLetsEncryptCSRForAliasDestinationDomain($result['aliasdomain'], $log); + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } else + if ($result['wwwserveralias'] != $wwwserveralias || $result['letsencrypt'] != $letsencrypt) { + // or when wwwserveralias or letsencrypt was changed + triggerLetsEncryptCSRForAliasDestinationDomain($aliasdomain, $log); + } + inserttask('1'); // Using nameserver, insert a task which rebuilds the server config diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 189d2333..3bf8b6af 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -62,16 +62,23 @@ class lescript $keys = $this->generateKey(); // Only store the accountkey in production, in staging always generate a new key if (Settings::Get('system.letsencryptca') == 'production') { - $upd_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private WHERE `customerid` = :customerid; - "); - Database::pexecute($upd_stmt, array( - 'public' => $keys['public'], - 'private' => $keys['private'], - 'customerid' => $certrow['customerid'] - )); + $upd_stmt = Database::prepare( + "UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `lepublickey` = :public, `leprivatekey` = :private " . + "WHERE `customerid` = :customerid;"); + Database::pexecute($upd_stmt, + array( + 'public' => $keys['public'], + 'private' => $keys['private'], + 'customerid' => $certrow['customerid'] + )); } $this->accountKey = $keys['private']; + + $response = $this->postNewReg(); + if ($this->client->getLastCode() != 201) { + throw new \RuntimeException("Account not initialized, probably due to rate limiting. Whole response: " . $response); + } + $this->postNewReg(); $this->log('New account certificate registered'); } else { @@ -83,7 +90,7 @@ class lescript public function signDomains(array $domains, $domainkey = null, $csr = null) { if (! $this->accountKey) { - throw new \RuntimeException("Account not initiated"); + throw new \RuntimeException("Account not initialized"); } $this->log('Starting certificate generation process for domains'); @@ -101,13 +108,14 @@ class lescript $this->log("Requesting challenge for $domain"); - $response = $this->signedRequest("/acme/new-authz", array( - "resource" => "new-authz", - "identifier" => array( - "type" => "dns", - "value" => $domain - ) - )); + $response = $this->signedRequest("/acme/new-authz", + array( + "resource" => "new-authz", + "identifier" => array( + "type" => "dns", + "value" => $domain + ) + )); // if response is not an array but a string, it's most likely a server-error, e.g. // ErrorAn error occurred while processing your request. @@ -121,9 +129,10 @@ class lescript } // choose http-01 challenge only - $challenge = array_reduce($response['challenges'], function ($v, $w) { - return $v ? $v : ($w['type'] == 'http-01' ? $w : false); - }); + $challenge = array_reduce($response['challenges'], + function ($v, $w) { + return $v ? $v : ($w['type'] == 'http-01' ? $w : false); + }); if (! $challenge) throw new RuntimeException("HTTP Challenge for $domain is not available. Whole response: " . json_encode($response)); @@ -145,8 +154,7 @@ class lescript "e" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["e"]), "kty" => "RSA", "n" => Base64UrlSafeEncoder::encode($accountKeyDetails["rsa"]["n"]) - ) - ; + ); $payload = $challenge['token'] . '.' . Base64UrlSafeEncoder::encode(hash('sha256', json_encode($header), true)); file_put_contents($tokenPath, $payload); @@ -174,12 +182,13 @@ class lescript $this->log("Sending request to challenge"); // send request to challenge - $result = $this->signedRequest($challenge['uri'], array( - "resource" => "challenge", - "type" => "http-01", - "keyAuthorization" => $payload, - "token" => $challenge['token'] - )); + $result = $this->signedRequest($challenge['uri'], + array( + "resource" => "challenge", + "type" => "http-01", + "keyAuthorization" => $payload, + "token" => $challenge['token'] + )); // waiting loop // we wait for a maximum of 30 seconds to avoid endless loops @@ -218,9 +227,7 @@ class lescript $this->client->getLastLinks(); - if (empty($csrfile) || Settings::Get('system.letsencryptreuseold') == 0) { - $csr = $this->generateCSR($privateDomainKey, $domains); - } + $csr = $this->generateCSR($privateDomainKey, $domains); // request certificates creation $result = $this->signedRequest("/acme/new-cert", array( @@ -306,7 +313,8 @@ class lescript $tmpConfPath = $tmpConfMeta["uri"]; // workaround to get SAN working - fwrite($tmpConf, 'HOME = . + fwrite($tmpConf, + 'HOME = . RANDFILE = $ENV::HOME/.rnd [ req ] default_bits = ' . Settings::Get('system.letsencryptkeysize') . ' @@ -320,15 +328,16 @@ basicConstraints = CA:FALSE subjectAltName = ' . $san . ' keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); - $csr = openssl_csr_new(array( - "CN" => $domain, - "ST" => Settings::Get('system.letsencryptstate'), - "C" => Settings::Get('system.letsencryptcountrycode'), - "O" => "Unknown" - ), $privateKey, array( - "config" => $tmpConfPath, - "digest_alg" => "sha256" - )); + $csr = openssl_csr_new( + array( + "CN" => $domain, + "ST" => Settings::Get('system.letsencryptstate'), + "C" => Settings::Get('system.letsencryptcountrycode'), + "O" => "Unknown" + ), $privateKey, array( + "config" => $tmpConfPath, + "digest_alg" => "sha256" + )); if (! $csr) throw new \RuntimeException("CSR couldn't be generated! " . openssl_error_string()); @@ -343,10 +352,11 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment'); private function generateKey() { - $res = openssl_pkey_new(array( - "private_key_type" => OPENSSL_KEYTYPE_RSA, - "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize') - )); + $res = openssl_pkey_new( + array( + "private_key_type" => OPENSSL_KEYTYPE_RSA, + "private_key_bits" => (int) Settings::Get('system.letsencryptkeysize') + )); if (! openssl_pkey_export($res, $privateKey)) { throw new \RuntimeException("Key export failed!"); diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 3ab1c2b5..27050f9b 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -40,14 +40,14 @@ - - //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} @@ -4022,7 +4022,7 @@ aliases: files - + /etc/insserv/overrides diff --git a/lib/configfiles/precise.xml b/lib/configfiles/precise.xml index 54b8fd38..885c3522 100644 --- a/lib/configfiles/precise.xml +++ b/lib/configfiles/precise.xml @@ -40,8 +40,6 @@ - - @@ -49,6 +47,8 @@ default="true"> //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} diff --git a/lib/configfiles/rhel_centos.xml b/lib/configfiles/rhel_centos.xml index b43e5a03..9057fba5 100644 --- a/lib/configfiles/rhel_centos.xml +++ b/lib/configfiles/rhel_centos.xml @@ -40,8 +40,6 @@ - - diff --git a/lib/configfiles/trusty.xml b/lib/configfiles/trusty.xml index fd961f62..770c93b0 100644 --- a/lib/configfiles/trusty.xml +++ b/lib/configfiles/trusty.xml @@ -40,8 +40,6 @@ - - @@ -49,6 +47,8 @@ default="true"> //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} @@ -83,6 +83,8 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath} //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} diff --git a/lib/configfiles/wheezy.xml b/lib/configfiles/wheezy.xml index 782c41fb..c3824096 100644 --- a/lib/configfiles/wheezy.xml +++ b/lib/configfiles/wheezy.xml @@ -40,8 +40,6 @@ - - @@ -49,6 +47,8 @@ default="true"> //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} @@ -83,6 +83,8 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath} //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} diff --git a/lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php b/lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php new file mode 100644 index 00000000..cda8ac98 --- /dev/null +++ b/lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php @@ -0,0 +1,34 @@ + (2016-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Functions + * + */ + +function triggerLetsEncryptCSRForAliasDestinationDomain($aliasDestinationDomainID, $log) +{ + if (isset($aliasDestinationDomainID) && $aliasDestinationDomainID > 0) { + $log->logAction(ADM_ACTION, LOG_INFO, "LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID); + $upd_stmt = Database::prepare( + "UPDATE + `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` + SET + `expirationdate` = null + WHERE + domainid = :domainid + "); + Database::pexecute($upd_stmt, array( + 'domainid' => $aliasDestinationDomainID + )); + } +} diff --git a/lng/english.lng.php b/lng/english.lng.php index 43538921..bb2043e5 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1936,7 +1936,6 @@ $lng['customer']['letsencrypt']['title'] = 'Use Let\'s Encrypt'; $lng['customer']['letsencrypt']['description'] = 'Get a free certificate from Let\'s Encrypt. The certificate will be created and renewed automatically.
ATTENTION: This feature is still in beta.'; $lng['error']['sslredirectonlypossiblewithsslipport'] = 'Using Let\'s Encrypt is only possible when the domain has at least one ssl-enabled IP/port combination assigned.'; $lng['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt cannot (yet) handle wildcard-domains. Please set the ServerAlias to WWW or disable it completely'; -$lng['error']['letsencryptdoesnotworkwithaliasdomains'] = "Usage of Let's Encrypt is not possible for aliasdomains at the moment. Please disable Let's Encrypt or AliasDomain"; $lng['panel']['letsencrypt'] = 'Using Let\'s encrypt'; $lng['crondesc']['cron_letsencrypt'] = 'updating Let\'s Encrypt certificates'; $lng['serversettings']['letsencryptca']['title'] = "Let's Encrypt environment"; @@ -1949,8 +1948,8 @@ $lng['serversettings']['letsencryptchallengepath']['title'] = "Path for Let's En $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['serversettings']['letsencryptreuseold']['title'] = "Re-use Let's Encrypt key"; +$lng['serversettings']['letsencryptreuseold']['description'] = "If activated, the same key will be used for every renew, otherwise a new key will be generated every time.
ATTENTION: Let's Encrypt is still in beta"; $lng['serversettings']['leenabled']['title'] = "Enable Let's Encrypt"; $lng['serversettings']['leenabled']['description'] = "If activated, customers are able to let froxlor automatically generate and renew Let's Encrypt ssl-certificates for domains with a ssl IP/port.

Please remember that you need to go through the webserver-configuration when eabled because this feature needs a special configuration."; $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."; diff --git a/lng/german.lng.php b/lng/german.lng.php index 70e483ad..36be1a7c 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1590,7 +1590,6 @@ $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['error']['nowildcardwithletsencrypt'] = 'Let\'s Encrypt kann (noch) nicht mit Wildcard-Domains umgehen. Bitte den ServerAlias auf WWW setzen oder deaktivieren'; -$lng['error']['letsencryptdoesnotworkwithaliasdomains'] = "Die Nutzung von Let's Encrypt ist mit AliasDomains derzeit nicht möglich. Bitte Let's Encrypt oder AliasDomain 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"; @@ -1603,8 +1602,8 @@ $lng['serversettings']['letsencryptchallengepath']['title'] = "Verzeichnis für $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 aktiviert 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['serversettings']['letsencryptreuseold']['title'] = "Let's Encrypt Schlüssel wiederverwenden"; +$lng['serversettings']['letsencryptreuseold']['description'] = "Wenn dies aktiviert ist, wird der alte Schlüssel bei jeder Verlängerung verwendet, andernfalls wird ein neues Paar generiert.
ACHTUNG: Let's Encrypt befindet sich noch im Test"; $lng['serversettings']['leenabled']['title'] = "Let's Encrypt verwenden"; $lng['serversettings']['leenabled']['description'] = "Wenn dies aktiviert ist, können Kunden durch Froxlor automatisch generierte und verlängerbare Let's Encrypt SSL-Zertifikate für Domains mit SSL IP/port nutzen.

Bitte die Webserver-Konfiguration beachten wenn aktiviert, da dieses Feature eine spezielle Konfiguration benötigt."; $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."; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 1848480f..8fe3d5af 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -1,5 +1,4 @@ logAction(CRON_ACTION, LOG_INFO, "Updating Let's Encrypt certificates" if (! extension_loaded('curl')) { $cronlog->logAction(CRON_ACTION, LOG_ERR, "Let's Encrypt requires the php cURL extension to be installed."); - exit; + exit(); } -$certificates_stmt = Database::query(" - 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, cust.loginname - 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) -"); +$certificates_stmt = Database::query( + " + 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.`wwwserveralias`, + dom.`documentroot`, + dom.`id` AS 'domainid', + dom.`ssl_redirect`, + cust.`leprivatekey`, + cust.`lepublickey`, + cust.`customerid`, + cust.`loginname` + FROM + `" . TABLE_PANEL_CUSTOMERS . "` AS cust, + `" . TABLE_PANEL_DOMAINS . "` AS dom + LEFT JOIN + `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` AS domssl ON + dom.`id` = domssl.`domainid` + WHERE + dom.`customerid` = cust.`customerid` + AND dom.`letsencrypt` = 1 + AND dom.`aliasdomain` IS NULL + AND dom.`iswildcarddomain` = 0 + 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` = :chain, `ssl_csr_file` = :csr, expirationdate = :expirationdate -"); +$aliasdomains_stmt = Database::prepare( + " + SELECT + dom.`id` as domainid, + dom.`domain`, + dom.`wwwserveralias` + FROM `" . TABLE_PANEL_DOMAINS . "` AS dom + WHERE + dom.`aliasdomain` = :id + AND dom.`letsencrypt` = 1 + AND dom.`iswildcarddomain` = 0 + "); -$upddom_stmt = Database::prepare(" - UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid -"); +$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` = :chain, + `ssl_csr_file` = :csr, + `expirationdate` = :expirationdate + "); + +$upddom_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid"); $changedetected = 0; $certrows = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); @@ -56,25 +106,27 @@ foreach ($certrows as $certrow) { if ($certrow['ssl_redirect'] != 2) { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); - if ($certrow['ssl_cert_file']) { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt using old key / SAN for " . $certrow['domain']); - // Parse the old certificate - $x509data = openssl_x509_parse($certrow['ssl_cert_file']); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $certrow['domain']); + $domains = array( + $certrow['domain'] + ); + // add www. to SAN list + if ($certrow['wwwserveralias'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: www." . $certrow['domain']); + $domains[] = 'www.' . $certrow['domain']; + } - // We are interessted in the old SAN - data - $san = explode(', ', $x509data['extensions']['subjectAltName']); - $domains = array(); - foreach ($san as $dnsname) { - $domains[] = substr($dnsname, 4); - } - } else { - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating new key / SAN for " . $certrow['domain']); - $domains = array( - $certrow['domain'] - ); - // Add www. for SAN - if ($certrow['wwwserveralias'] == 1) { - $domains[] = 'www.' . $certrow['domain']; + // add alias domains (and possibly www.) to SAN list + Database::pexecute($aliasdomains_stmt, array( + 'id' => $certrow['domainid'] + )); + $aliasdomains = $aliasdomains_stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($aliasdomains as $aliasdomain) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $aliasdomain['domain']); + $domains[] = $aliasdomain['domain']; + if ($aliasdomain['wwwserveralias'] == 1) { + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: www." . $aliasdomain['domain']); + $domains[] = 'www.' . $aliasdomain['domain']; } } @@ -92,16 +144,17 @@ foreach ($certrows as $certrow) { $newcert = openssl_x509_parse($return['crt']); // Store the new data - Database::pexecute($updcert_stmt, array( - 'id' => $certrow['id'], - 'domainid' => $certrow['domainid'], - 'crt' => $return['crt'], - 'key' => $return['key'], - 'ca' => $return['chain'], - 'chain' => $return['chain'], - 'csr' => $return['csr'], - 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) - )); + Database::pexecute($updcert_stmt, + array( + 'id' => $certrow['id'], + 'domainid' => $certrow['domainid'], + 'crt' => $return['crt'], + 'key' => $return['key'], + 'ca' => $return['chain'], + 'chain' => $return['chain'], + 'csr' => $return['csr'], + 'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) + )); if ($certrow['ssl_redirect'] == 3) { Database::pexecute($upddom_stmt, array( @@ -113,10 +166,12 @@ foreach ($certrows as $certrow) { $changedetected = 1; } catch (Exception $e) { - $cronlog->logAction(CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); + $cronlog->logAction(CRON_ACTION, LOG_ERR, + "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ": " . $e->getMessage()); } } else { - $cronlog->logAction(CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); + $cronlog->logAction(CRON_ACTION, LOG_WARNING, + "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect"); } }