feat(ssl): improve SSL generation and security a lot

- rename some variables for better clarity
- format subjectAltNames correctly
- setup extensions more securely and improve them a lot
- use finally block to remove tempConfig
This commit is contained in:
peaklabs-dev
2025-02-05 22:09:37 +01:00
parent 951a454cbc
commit 806d9af569

View File

@@ -10,9 +10,9 @@ class SslHelper
{ {
private const DEFAULT_ORGANIZATION_NAME = 'Coolify'; private const DEFAULT_ORGANIZATION_NAME = 'Coolify';
private const DEFAULT_COUNTRY_CODE = 'ZZ'; private const DEFAULT_COUNTRY_NAME = 'ZZ';
private const DEFAULT_STATE = 'Default'; private const DEFAULT_STATE_NAME = 'Default';
public static function generateSslCertificate( public static function generateSslCertificate(
string $commonName, string $commonName,
@@ -27,6 +27,9 @@ class SslHelper
?string $configurationDir = null, ?string $configurationDir = null,
?string $mountPath = null ?string $mountPath = null
): SslCertificate { ): SslCertificate {
$organizationName = self::DEFAULT_ORGANIZATION_NAME;
$countryName = self::DEFAULT_COUNTRY_NAME;
$stateName = self::DEFAULT_STATE_NAME;
try { try {
$privateKey = openssl_pkey_new([ $privateKey = openssl_pkey_new([
@@ -60,41 +63,42 @@ class SslHelper
array_merge(["DNS:$commonName"], $subjectAlternativeNames) array_merge(["DNS:$commonName"], $subjectAlternativeNames)
); );
$countryCode = self::DEFAULT_COUNTRY_CODE; $formattedSubjectAltNames = [];
$state = self::DEFAULT_STATE;
$organization = self::DEFAULT_ORGANIZATION_NAME;
$altNames = [];
foreach ($subjectAlternativeNames as $index => $san) { foreach ($subjectAlternativeNames as $index => $san) {
[$type, $value] = explode(':', $san, 2); [$type, $value] = explode(':', $san, 2);
$altNames[] = "{$type}.".($index + 1)." = $value"; $formattedSubjectAltNames[] = "{$type}.".($index + 1)." = $value";
} }
$altNamesSection = implode("\n", $altNames); $formattedSubjectAltNamesSection = implode("\n", $formattedSubjectAltNames);
$basicConstraints = $isCaCertificate ? 'CA:TRUE' : 'CA:FALSE'; $basicConstraints = $isCaCertificate ? 'critical, CA:TRUE, pathlen:0' : 'critical, CA:FALSE';
$keyUsage = $isCaCertificate ? 'keyCertSign, cRLSign' : 'digitalSignature, keyEncipherment'; $keyUsage = $isCaCertificate ? 'critical, keyCertSign, cRLSign' : 'critical, digitalSignature';
$extendedKeyUsage = $isCaCertificate ? '' : 'extendedKeyUsage = serverAuth'; $authorityKeyIdentifierLine = $isCaCertificate ? '' : "authorityKeyIdentifier = critical,keyid,issuer\n";
$config = <<<CONF $config = <<<CONF
[req] [req]
prompt = no prompt = no
distinguished_name = req_distinguished_name distinguished_name = distinguished_name
req_extensions = v3_req req_extensions = req_ext
[req_distinguished_name] [distinguished_name]
C = $countryCode C = $countryName
ST = $state ST = $stateName
O = $organization O = $organizationName
CN = $commonName CN = $commonName
[req_ext]
basicConstraints = $basicConstraints
keyUsage = $keyUsage
[v3_req] [v3_req]
basicConstraints = $basicConstraints basicConstraints = $basicConstraints
keyUsage = $keyUsage keyUsage = $keyUsage
$extendedKeyUsage subjectKeyIdentifier = critical,hash
subjectAltName = @alt_names {$authorityKeyIdentifierLine}
subjectAltName = critical,@subject_alt_names
[alt_names] [subject_alt_names]
$altNamesSection $formattedSubjectAltNamesSection
CONF; CONF;
$tempConfig = tmpfile(); $tempConfig = tmpfile();
@@ -103,13 +107,13 @@ class SslHelper
$csr = openssl_csr_new([ $csr = openssl_csr_new([
'commonName' => $commonName, 'commonName' => $commonName,
'organizationName' => self::DEFAULT_ORGANIZATION_NAME, 'organizationName' => $organizationName,
'countryName' => self::DEFAULT_COUNTRY_CODE, 'countryName' => $countryName,
'stateOrProvinceName' => self::DEFAULT_STATE, 'stateOrProvinceName' => $stateName,
], $privateKey, [ ], $privateKey, [
'digest_alg' => 'sha512', 'digest_alg' => 'sha512',
'config' => $tempConfigPath, 'config' => $tempConfigPath,
'encrypt_key' => false, 'req_extensions' => 'req_ext',
]); ]);
if ($csr === false) { if ($csr === false) {
@@ -194,11 +198,11 @@ class SslHelper
]); ]);
} }
fclose($tempConfig);
return $sslCertificate; return $sslCertificate;
} catch (\Throwable $e) { } catch (\Throwable $e) {
throw new \RuntimeException('SSL Certificate generation failed: '.$e->getMessage(), 0, $e); throw new \RuntimeException('SSL Certificate generation failed: '.$e->getMessage(), 0, $e);
} finally {
fclose($tempConfig);
} }
} }
} }