From bb9331904efa80cae5563f8e57aa1614236c9f4a Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Tue, 10 May 2016 11:55:03 +0200 Subject: [PATCH 1/9] (really) fix PHP notice #2048 Only variables should be passed by reference, thx to baudetail Signed-off-by: Michael Kaufmann (d00p) --- admin_customers.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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, From da785500cc14f52017e10523464cac17b4269ed7 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Sat, 14 May 2016 18:39:18 +0200 Subject: [PATCH 2/9] remove invalid self-closing tag as it produces php-notices and was just added for design reasons Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 3ab1c2b5..1bbb713a 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -4022,7 +4022,7 @@ aliases: files - + /etc/insserv/overrides From 1d4211a5ceb2f70f771fa8b829c19b34fd8a111d Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 16 May 2016 17:20:49 +0200 Subject: [PATCH 3/9] remove wrong a2* commands for rhel/centos Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/rhel_centos.xml | 2 -- 1 file changed, 2 deletions(-) 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 @@ - - From 0ae0178b4ca13e124f53cb9d07f18da0c5a7bca3 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:23:31 +0200 Subject: [PATCH 4/9] LE: PSR-2 formatting --- lib/classes/ssl/class.lescript.php | 86 ++++++++++++++++-------------- scripts/jobs/cron_letsencrypt.php | 36 +++++++------ 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 189d2333..9a2b2b94 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -62,14 +62,15 @@ 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']; $this->postNewReg(); @@ -101,13 +102,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 +123,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 +148,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 +176,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 @@ -306,7 +309,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 +324,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 +348,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/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 1848480f..3c7808d7 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(" +$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) "); -$updcert_stmt = Database::prepare(" +$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 "); @@ -92,16 +93,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 +115,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"); } } From 712aebb8641877d81135c08e476d3ca3b52182ec Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Sun, 15 May 2016 14:25:37 +0200 Subject: [PATCH 5/9] LE: improve SQL readability --- scripts/jobs/cron_letsencrypt.php | 55 +++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 3c7808d7..0ee4722e 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -29,20 +29,55 @@ if (! extension_loaded('curl')) { $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) -"); + 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 . "` 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 ( + 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 -"); + 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 -"); +$upddom_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid"); $changedetected = 0; $certrows = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); From 001f10f74e0d7b8c37f152129e4e0916ec329d8b Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:28:23 +0200 Subject: [PATCH 6/9] LE: catch error due to rate-limited account registration and fix bad english in log message --- lib/classes/ssl/class.lescript.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 9a2b2b94..4ea258b0 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -73,6 +73,12 @@ class lescript )); } $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 { @@ -84,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'); From f3e05742b56a7a903a874f1a3c4df72dc382ed96 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:29:59 +0200 Subject: [PATCH 7/9] LE: change semantics of setting.letsencryptreuseold Previously setting.letsencryptreuseold determined wheter both a domain's private key and a CSR should be re-generated. Preparing support of alias domains in LE certificates, this is changed to only determine the re-generation of the private key. CSRs now are always re-generated. --- lib/classes/ssl/class.lescript.php | 4 +--- lng/english.lng.php | 4 ++-- lng/german.lng.php | 4 ++-- scripts/jobs/cron_letsencrypt.php | 27 +++++++-------------------- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/lib/classes/ssl/class.lescript.php b/lib/classes/ssl/class.lescript.php index 4ea258b0..3bf8b6af 100644 --- a/lib/classes/ssl/class.lescript.php +++ b/lib/classes/ssl/class.lescript.php @@ -227,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( diff --git a/lng/english.lng.php b/lng/english.lng.php index 6783d8a8..61849a5b 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1949,8 +1949,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 671ce6c1..dd908b2f 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1603,8 +1603,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 0ee4722e..875f8ff5 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -92,26 +92,13 @@ 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']); - - // 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']; - } + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating SAN list for " . $certrow['domain']); + $domains = array( + $certrow['domain'] + ); + // Add www. for SAN + if ($certrow['wwwserveralias'] == 1) { + $domains[] = 'www.' . $certrow['domain']; } try { From 6e2b1773a39705e494cbfb5b97673d50c87a6e29 Mon Sep 17 00:00:00 2001 From: Daniel Reichelt Date: Mon, 16 May 2016 16:32:07 +0200 Subject: [PATCH 8/9] LE: support alias domains LE CSRs are triggered for the aliasdomain target domain on * domain deletion * domain creation * domain editing when * the aliasdomain target changes (CSR triggered both for old and new target) * wwwalias is disabled or enabled * letsencrypt is disabled or enabled (domain-local) fixes #1597 --- admin_domains.php | 22 +++++++----- customer_domains.php | 26 ++++++++------ ...etsEncryptCSRForAliasDestinationDomain.php | 34 ++++++++++++++++++ lng/english.lng.php | 1 - lng/german.lng.php | 1 - scripts/jobs/cron_letsencrypt.php | 35 +++++++++++++++++-- 6 files changed, 95 insertions(+), 24 deletions(-) create mode 100644 lib/functions/froxlor/function.triggerLetsEncryptCSRForAliasDestinationDomain.php diff --git a/admin_domains.php b/admin_domains.php index 479e1bc5..5fbabda4 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 de18a20c..d7bdbc9b 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/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 61849a5b..2db8a344 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"; diff --git a/lng/german.lng.php b/lng/german.lng.php index dd908b2f..ec8bc35f 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"; diff --git a/scripts/jobs/cron_letsencrypt.php b/scripts/jobs/cron_letsencrypt.php index 875f8ff5..8fe3d5af 100644 --- a/scripts/jobs/cron_letsencrypt.php +++ b/scripts/jobs/cron_letsencrypt.php @@ -38,7 +38,6 @@ $certificates_stmt = Database::query( domssl.`ssl_ca_file`, domssl.`ssl_csr_file`, dom.`domain`, - dom.`iswildcarddomain`, dom.`wwwserveralias`, dom.`documentroot`, dom.`id` AS 'domainid', @@ -56,12 +55,27 @@ $certificates_stmt = Database::query( 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 ) "); +$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 + "); + $updcert_stmt = Database::prepare( " REPLACE INTO @@ -92,15 +106,30 @@ foreach ($certrows as $certrow) { if ($certrow['ssl_redirect'] != 2) { $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Updating " . $certrow['domain']); - $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "letsencrypt generating SAN list for " . $certrow['domain']); + $cronlog->logAction(CRON_ACTION, LOG_DEBUG, "Adding SAN entry: " . $certrow['domain']); $domains = array( $certrow['domain'] ); - // Add www. for SAN + // 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']; } + // 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']; + } + } + try { // Initialize Lescript with documentroot $le = new lescript($cronlog); From b03eab897ab95cb5ce7ffe7df9ca3579f5a02612 Mon Sep 17 00:00:00 2001 From: "Michael Kaufmann (d00p)" Date: Mon, 16 May 2016 18:41:01 +0200 Subject: [PATCH 9/9] show a2enmod commands only when using apache, thx to d4n13L Signed-off-by: Michael Kaufmann (d00p) --- lib/configfiles/jessie.xml | 4 ++-- lib/configfiles/precise.xml | 4 ++-- lib/configfiles/trusty.xml | 6 ++++-- lib/configfiles/wheezy.xml | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/configfiles/jessie.xml b/lib/configfiles/jessie.xml index 1bbb713a..27050f9b 100644 --- a/lib/configfiles/jessie.xml +++ b/lib/configfiles/jessie.xml @@ -40,14 +40,14 @@ - - //service[@type='http']/general/commands + + {{settings.phpfpm.enabled}} 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/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}}