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_csr_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`),
UNIQUE KEY (`domainid`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;

View File

@@ -429,5 +429,27 @@ if (Froxlor::isDatabaseVersion('202302030')) {
}
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');
}

View File

@@ -127,7 +127,9 @@ class Certificates extends ApiCommand implements ResourceEntity
}
$do_verify = true;
$expirationdate = null;
$validtodate = null;
$validtodate = null;
$issuer = "";
// no cert-file given -> forget everything
if ($ssl_cert_file == '') {
$ssl_key_file = '';
@@ -168,7 +170,10 @@ class Certificates extends ApiCommand implements ResourceEntity
} else {
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
@@ -183,7 +188,9 @@ class Certificates extends ApiCommand implements ResourceEntity
`ssl_key_file` = :ssl_key_file,
`ssl_ca_file` = :ssl_ca_file,
`ssl_cert_chainfile` = :ssl_cert_chainfile,
`expirationdate` = :expirationdate
`validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
" . $qrywhere . " `domainid`= :domainid
");
$params = [
@@ -191,7 +198,9 @@ class Certificates extends ApiCommand implements ResourceEntity
"ssl_key_file" => $ssl_key_file,
"ssl_ca_file" => $ssl_ca_file,
"ssl_cert_chainfile" => $ssl_cert_chainfile,
"expirationdate" => $expirationdate,
"validfromdate" => $validfromdate,
"validtodate" => $validtodate,
"issuer" => $issuer,
"domainid" => $domainid
];
Database::pexecute($stmt, $params, true, true);
@@ -299,27 +308,23 @@ class Certificates extends ApiCommand implements ResourceEntity
}
// Set data from certificate
$cert['isvalid'] = false;
$cert['san'] = null;
$cert_data = openssl_x509_parse($cert['ssl_cert_file']);
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['issuer'] = $cert_data['issuer']['O'] ?? null;
}
// Set subject alt names from certificate
$cert['san'] = null;
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
$SANs = array_map('trim', $SANs);
foreach ($SANs as $san) {
$san = str_replace("DNS:", "", $san);
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
$cert['san'][] = $san;
// Set subject alt names from certificate
if (isset($cert_data['extensions']['subjectAltName']) && !empty($cert_data['extensions']['subjectAltName'])) {
$SANs = explode(",", $cert_data['extensions']['subjectAltName']);
$SANs = array_map('trim', $SANs);
foreach ($SANs as $san) {
$san = str_replace("DNS:", "", $san);
if ($san != $cert_data['subject']['CN'] && strpos($san, "othername:") === false) {
$cert['san'][] = $san;
}
}
}
}
$result[] = $cert;
}
return $this->response([

View File

@@ -76,7 +76,7 @@ final class ValidateAcmeWebroot extends CliCommand
'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_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
$recommended = rtrim(FileDir::makeCorrectDir(Froxlor::getInstallDir()), "/");

View File

@@ -179,7 +179,7 @@ class HttpConfigBase
$froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
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);
if ($froxlor_ssl && !empty($froxlor_ssl['ssl_cert_file'])) {

View File

@@ -114,7 +114,9 @@ class AcmeSh extends FroxlorCron
`ssl_cert_chainfile` = :chain,
`ssl_csr_file` = :csr,
`ssl_fullchain_file` = :fullchain,
`expirationdate` = :expirationdate
`validfromdate` = :validfromdate,
`validtodate` = :validtodate,
`issuer` = :issuer
");
// prepare domain update sql
@@ -136,7 +138,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null,
'validfromdate' => null,
'validtodate' => null,
'issuer' => "",
'ssl_cert_file' => null,
'ssl_key_file' => null,
'ssl_ca_file' => null,
@@ -171,7 +175,9 @@ class AcmeSh extends FroxlorCron
'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'),
'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_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,
@@ -187,7 +193,7 @@ class AcmeSh extends FroxlorCron
'loginname' => $domain['loginname'],
'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, []);
$changedetected = 1;
}
@@ -279,7 +285,9 @@ EOC;
SELECT
domssl.`id`,
domssl.`domainid`,
domssl.`expirationdate`,
domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`,
domssl.`ssl_key_file`,
domssl.`ssl_ca_file`,
@@ -306,7 +314,7 @@ EOC;
AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0
AND domssl.`expirationdate` IS NULL
AND domssl.`validtodate` IS NULL
");
$customer_ssl = $certificates_stmt->fetchAll(PDO::FETCH_ASSOC);
if ($customer_ssl) {
@@ -330,7 +338,7 @@ EOC;
");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// 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;
}
}
@@ -346,7 +354,9 @@ EOC;
SELECT
domssl.`id`,
domssl.`domainid`,
domssl.`expirationdate`,
domssl.`validfromdate`,
domssl.`validtodate`,
domssl.`issuer`,
domssl.`ssl_cert_file`,
domssl.`ssl_key_file`,
dom.`domain`,
@@ -370,7 +380,7 @@ EOC;
if ($renew_certs) {
if ($check) {
foreach ($renew_certs as $cert) {
if (self::checkFsFilesAreNewer($cert['domain'], $cert['expirationdate'])) {
if (self::checkFsFilesAreNewer($cert['domain'], $cert['validtodate'])) {
return true;
}
}
@@ -453,7 +463,7 @@ EOC;
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled
if ($certrow['ssl_redirect'] != 2) {
$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)
$do_force = true;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
@@ -594,7 +604,9 @@ EOC;
'chain' => $return['chain'],
'csr' => $return['csr'],
'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) {

View File

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

View File

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