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:
		@@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user