add certificate metadata to db table

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann
2023-03-23 12:40:01 +01:00
parent d60e48849b
commit de7729cec8
8 changed files with 81 additions and 44 deletions

View File

@@ -983,7 +983,9 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` (
`ssl_cert_chainfile` mediumtext, `ssl_cert_chainfile` mediumtext,
`ssl_csr_file` mediumtext, `ssl_csr_file` mediumtext,
`ssl_fullchain_file` mediumtext, `ssl_fullchain_file` mediumtext,
`expirationdate` datetime DEFAULT NULL, `validfromdate` datetime DEFAULT NULL,
`validtodate` datetime DEFAULT NULL,
`issuer` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY (`domainid`) UNIQUE KEY (`domainid`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;

View File

@@ -429,5 +429,27 @@ if (Froxlor::isDatabaseVersion('202302030')) {
} }
Update::lastStepStatus(0); Update::lastStepStatus(0);
Update::showUpdateStep("Enhancing ssl data table");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` RENAME COLUMN `expirationdate` TO `validtodate`;");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `validfromdate` datetime DEFAULT NULL AFTER `ssl_fullchain_file`;");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` ADD `issuer` varchar(255) NOT NULL default '' AFTER `validtodate`;");
Update::lastStepStatus(0);
Update::showUpdateStep("Filling new ssl data fields with existing certificate data");
$crt_upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET `validfromdate` = :validfromdate, `issuer` = :issuer WHERE `id` = :id");
$crt_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`");
Database::pexecute($crt_stmt);
while ($cert = $crt_stmt->fetch(\PDO::FETCH_ASSOC)) {
$cert_content = openssl_x509_parse($cert['ssl_cert_file']);
if (is_array($cert_content)) {
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
$issuer = $cert_content['issuer']['O'] ?? "";
Database::pexecute($crt_upd_stmt, ['validfromdate' => $validfromdate, 'issuer' => $issuer, 'id' => $cert['id']]);
}
}
// clear possible user customized columns
Database::query("DELETE FROM `" . TABLE_PANEL_USERCOLUMNS . "` WHERE `section` = 'sslcertificates_list'");
Update::lastStepStatus(0);
Froxlor::updateToDbVersion('202303150'); Froxlor::updateToDbVersion('202303150');
} }

View File

@@ -127,7 +127,9 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
$do_verify = true; $do_verify = true;
$expirationdate = null; $validtodate = null;
$validtodate = null;
$issuer = "";
// no cert-file given -> forget everything // no cert-file given -> forget everything
if ($ssl_cert_file == '') { if ($ssl_cert_file == '') {
$ssl_key_file = ''; $ssl_key_file = '';
@@ -168,7 +170,10 @@ class Certificates extends ApiCommand implements ResourceEntity
} else { } else {
Response::standardError('sslcertificateinvalidcert', '', true); Response::standardError('sslcertificateinvalidcert', '', true);
} }
$expirationdate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']); // get data from certificate to store in the table
$validfromdate = empty($cert_content['validFrom_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validFrom_time_t']);
$validtodate = empty($cert_content['validTo_time_t']) ? null : date("Y-m-d H:i:s", $cert_content['validTo_time_t']);
$issuer = $cert_content['issuer']['O'] ?? "";
} }
// Add/Update database entry // Add/Update database entry
@@ -183,7 +188,9 @@ class Certificates extends ApiCommand implements ResourceEntity
`ssl_key_file` = :ssl_key_file, `ssl_key_file` = :ssl_key_file,
`ssl_ca_file` = :ssl_ca_file, `ssl_ca_file` = :ssl_ca_file,
`ssl_cert_chainfile` = :ssl_cert_chainfile, `ssl_cert_chainfile` = :ssl_cert_chainfile,
`expirationdate` = :expirationdate `validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
" . $qrywhere . " `domainid`= :domainid " . $qrywhere . " `domainid`= :domainid
"); ");
$params = [ $params = [
@@ -191,7 +198,9 @@ class Certificates extends ApiCommand implements ResourceEntity
"ssl_key_file" => $ssl_key_file, "ssl_key_file" => $ssl_key_file,
"ssl_ca_file" => $ssl_ca_file, "ssl_ca_file" => $ssl_ca_file,
"ssl_cert_chainfile" => $ssl_cert_chainfile, "ssl_cert_chainfile" => $ssl_cert_chainfile,
"expirationdate" => $expirationdate, "validfromdate" => $validfromdate,
"validtodate" => $validtodate,
"issuer" => $issuer,
"domainid" => $domainid "domainid" => $domainid
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
@@ -299,27 +308,23 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
// Set data from certificate // Set data from certificate
$cert['isvalid'] = false;
$cert['san'] = null;
$cert_data = openssl_x509_parse($cert['ssl_cert_file']); $cert_data = openssl_x509_parse($cert['ssl_cert_file']);
if ($cert_data) { if ($cert_data) {
$cert['validfromdate'] = date('Y-m-d H:i:s', $cert_data['validFrom_time_t']);
$cert['validtodate'] = date('Y-m-d H:i:s', $cert_data['validTo_time_t']);
$cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time(); $cert['isvalid'] = (bool)$cert_data['validTo_time_t'] > time();
$cert['issuer'] = $cert_data['issuer']['O'] ?? null; // Set subject alt names from certificate
} if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
// Set subject alt names from certificate $SANs = array_map('trim', $SANs);
$cert['san'] = null; foreach ($SANs as $san) {
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) { $san = str_replace("DNS:", "", $san);
$SANs = explode(",", $cert_data['extensions']['subjectAltName']); if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
$SANs = array_map('trim', $SANs); $cert['san'][] = $san;
foreach ($SANs as $san) { }
$san = str_replace("DNS:", "", $san);
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
$cert['san'][] = $san;
} }
} }
} }
$result[] = $cert; $result[] = $cert;
} }
return $this->response([ return $this->response([

View File

@@ -76,7 +76,7 @@ final class ValidateAcmeWebroot extends CliCommand
'domain' => Settings::Get('system.hostname') 'domain' => Settings::Get('system.hostname')
]; ];
} }
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET expirationdate=NULL WHERE `domainid` = :did"); $upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET `validtodate`=NULL WHERE `domainid` = :did");
$acmesh_dir = dirname(Settings::Get('system.acmeshpath')); $acmesh_dir = dirname(Settings::Get('system.acmeshpath'));
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/"); $acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/"); $recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");

View File

@@ -179,7 +179,7 @@ class HttpConfigBase
$froxlor_ssl_settings_stmt = Database::prepare(" $froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
WHERE `domainid` = '0' AND WHERE `domainid` = '0' AND
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL) (`validtodate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `validtodate` IS NULL)
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) { if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {

View File

@@ -114,7 +114,9 @@ class AcmeSh extends FroxlorCron
`ssl_cert_chainfile` = :chain, `ssl_cert_chainfile` = :chain,
`ssl_csr_file` = :csr, `ssl_csr_file` = :csr,
`ssl_fullchain_file` = :fullchain, `ssl_fullchain_file` = :fullchain,
`expirationdate` = :expirationdate `validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
"); ");
// prepare domain update sql // prepare domain update sql
@@ -136,7 +138,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'), 'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'), 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'), 'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null, 'validfromdate' => null,
'validtodate' => null,
'issuer' => "",
'ssl_cert_file' => null, 'ssl_cert_file' => null,
'ssl_key_file' => null, 'ssl_key_file' => null,
'ssl_ca_file' => null, 'ssl_ca_file' => null,
@@ -171,7 +175,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'), 'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'), 'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'), 'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => is_array($renew_froxlor) ? $renew_froxlor['expirationdate'] : date('Y-m-d H:i:s', 0), 'validfromdate' => is_array($renew_froxlor) ? $renew_froxlor['validfromdate'] : date('Y-m-d H:i:s', 0),
'validtodate' => is_array($renew_froxlor) ? $renew_froxlor['validtodate'] : date('Y-m-d H:i:s', 0),
'issuer' => is_array($renew_froxlor) ? $renew_froxlor['issuer'] : "",
'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null, 'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null,
'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null, 'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null,
'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null, 'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,
@@ -187,7 +193,7 @@ class AcmeSh extends FroxlorCron
'loginname' => $domain['loginname'], 'loginname' => $domain['loginname'],
'adminsession' => 0 'adminsession' => 0
]); ]);
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['expirationdate'])) { if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['validtodate'])) {
self::certToDb($domain, $cronlog, []); self::certToDb($domain, $cronlog, []);
$changedetected = 1; $changedetected = 1;
} }
@@ -279,7 +285,9 @@ EOC;
SELECT SELECT
domssl.`id`, domssl.`id`,
domssl.`domainid`, domssl.`domainid`,
domssl.`expirationdate`, domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`, domssl.`ssl_cert_file`,
domssl.`ssl_key_file`, domssl.`ssl_key_file`,
domssl.`ssl_ca_file`, domssl.`ssl_ca_file`,
@@ -306,7 +314,7 @@ EOC;
AND dom.`letsencrypt` = 1 AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0 AND dom.`iswildcarddomain` = 0
AND domssl.`expirationdate` IS NULL AND domssl.`validtodate` IS NULL
"); ");
$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC); $customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
if ($customer_ssl) { if ($customer_ssl) {
@@ -330,7 +338,7 @@ EOC;
"); ");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt); $froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate // also check for possible existing certificate
if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['expirationdate'])) { if ($froxlor_ssl && self::checkFsFilesAreNewer(Settings::Get('system.hostname'), $froxlor_ssl['validtodate'])) {
return $froxlor_ssl; return $froxlor_ssl;
} }
} }
@@ -346,7 +354,9 @@ EOC;
SELECT SELECT
domssl.`id`, domssl.`id`,
domssl.`domainid`, domssl.`domainid`,
domssl.`expirationdate`, domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`, domssl.`ssl_cert_file`,
domssl.`ssl_key_file`, domssl.`ssl_key_file`,
dom.`domain`, dom.`domain`,
@@ -370,7 +380,7 @@ EOC;
if ($renew_certs) { if ($renew_certs) {
if ($check) { if ($check) {
foreach ($renew_certs as $cert) { foreach ($renew_certs as $cert) {
if (self::checkFsFilesAreNewer($cert['domain'], $cert['expirationdate'])) { if (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {
return true; return true;
} }
} }
@@ -453,7 +463,7 @@ EOC;
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled // Only issue let's encrypt certificate if no broken ssl_redirect is enabled
if ($certrow['ssl_redirect'] != 2) { if ($certrow['ssl_redirect'] != 2) {
$do_force = false; $do_force = false;
if (!empty($certrow['ssl_cert_file']) && empty($certrow['expirationdate'])) { if (!empty($certrow['ssl_cert_file']) && empty($certrow['validtodate'])) {
// domain changed (SAN or similar) // domain changed (SAN or similar)
$do_force = true; $do_force = true;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']); $cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
@@ -594,7 +604,9 @@ EOC;
'chain' => $return['chain'], 'chain' => $return['chain'],
'csr' => $return['csr'], 'csr' => $return['csr'],
'fullchain' => $return['fullchain'], 'fullchain' => $return['fullchain'],
'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']) 'validfromdate' => date('Y-m-d H:i:s', $newcert['validFrom_time_t']),
'validtodate' => date('Y-m-d H:i:s', $newcert['validTo_time_t']),
'issuer' => $newcert['issuer']['O'] ?? ""
]); ]);
if ($certrow['ssl_redirect'] == 3) { if ($certrow['ssl_redirect'] == 3) {

View File

@@ -350,7 +350,7 @@ class Domain
$upd_stmt = Database::prepare("UPDATE $upd_stmt = Database::prepare("UPDATE
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
SET SET
`expirationdate` = null `validtodate` = null
WHERE WHERE
domainid = :domainid domainid = :domainid
"); ");

View File

@@ -45,31 +45,27 @@ return [
'callback' => [SSLCertificate::class, 'domainWithSan'], 'callback' => [SSLCertificate::class, 'domainWithSan'],
'searchable' => false, 'searchable' => false,
], ],
'c.issuer' => [ 's.issuer' => [
'label' => lng('ssl_certificates.issuer'), 'label' => lng('ssl_certificates.issuer'),
'field' => 'issuer', 'field' => 'issuer',
'searchable' => false,
'sortable' => false,
], ],
'c.validfromdate' => [ 's.validfromdate' => [
'label' => lng('ssl_certificates.valid_from'), 'label' => lng('ssl_certificates.valid_from'),
'field' => 'validfromdate', 'field' => 'validfromdate',
'searchable' => false, 'searchable' => false,
'sortable' => false,
], ],
'c.validtodate' => [ 's.validtodate' => [
'label' => lng('ssl_certificates.valid_until'), 'label' => lng('ssl_certificates.valid_until'),
'field' => 'validtodate', 'field' => 'validtodate',
'searchable' => false, 'searchable' => false,
'sortable' => false,
], ],
], ],
'visible_columns' => Listing::getVisibleColumnsForListing('sslcertificates_list', [ 'visible_columns' => Listing::getVisibleColumnsForListing('sslcertificates_list', [
'd.domain', 'd.domain',
'c.domain', 'c.domain',
'c.issuer', 's.issuer',
'c.validfromdate', 's.validfromdate',
'c.validtodate', 's.validtodate',
]), ]),
'actions' => [ 'actions' => [
'edit' => [ 'edit' => [