From be7e92af64f2e5de5e170a321f5947bf79105ea4 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Tue, 30 Jun 2026 15:49:28 +0530 Subject: [PATCH 1/3] fix(ssl): copy and validate custom certs on site update --- src/helper/class-ee-site.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/helper/class-ee-site.php b/src/helper/class-ee-site.php index 08ad268e..ae3bfd2f 100644 --- a/src/helper/class-ee-site.php +++ b/src/helper/class-ee-site.php @@ -420,6 +420,12 @@ protected function delete_site( $level, $site_url, $site_fs_path, $db_data = [] * [--wildcard] * : Enable wildcard SSL on site. * + * [--ssl-key=] + * : Path to the SSL key file. Required with --ssl=custom. + * + * [--ssl-crt=] + * : Path to the SSL crt file. Required with --ssl=custom. + * * [--php=] * : PHP version for site. Currently only supports PHP 5.6, 7.0, 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, and 8.5. * --- @@ -471,6 +477,9 @@ protected function delete_site( $level, $site_url, $site_fs_path, $db_data = [] * # Add self-signed SSL to non-ssl site * $ ee site update example.com --ssl=self * + * # Add custom SSL to non-ssl site + * $ ee site update example.com --ssl=custom --ssl-key=/path/to/site.key --ssl-crt=/path/to/site.crt + * * # Update PHP version of site. * $ ee site update example.com --php=8.0 * @@ -937,6 +946,13 @@ protected function update_ssl( $assoc_args ) { $this->site_data['site_ssl'] = $ssl; if ( $ssl ) { + // www_ssl_wrapper() skips cert work for custom SSL, so mirror the create + // path here: validate the provided key/crt and copy them into the + // nginx-proxy certs dir before enabling HTTPS, else the site serves a wrong cert. + if ( 'custom' === $ssl ) { + $this->validate_site_custom_ssl( get_flag_value( $assoc_args, 'ssl-key' ), get_flag_value( $assoc_args, 'ssl-crt' ) ); + $this->custom_site_ssl(); + } $this->www_ssl_wrapper( [ 'nginx' ] ); } else { $this->disable_ssl(); From a1164bba036e7eef5a7d1641bb77b7ad65df319c Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Thu, 24 Sep 2026 10:41:21 +0000 Subject: [PATCH 2/3] fix(ssl): reject custom cert that is unreadable or does not match its key Only the existence of --ssl-key/--ssl-crt was checked, so a mismatched pair or a non-PEM file was copied into nginx-proxy/certs and the command reported success. nginx-proxy then fails `nginx -t` for the whole config, which blocks proxy reloads for every site and would stop the proxy from starting after a restart. Validate the pair with openssl_x509_check_private_key() before anything is copied. --- src/helper/class-ee-site.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helper/class-ee-site.php b/src/helper/class-ee-site.php index ae3bfd2f..ef75640c 100644 --- a/src/helper/class-ee-site.php +++ b/src/helper/class-ee-site.php @@ -2271,6 +2271,11 @@ protected function validate_site_custom_ssl( $ssl_key, $ssl_crt ) { } else { throw new \Exception( 'ssl-key OR ssl-crt path does not exist' ); } + + // nginx-proxy fails its config test on an unreadable or mismatched pair, which blocks reloads for every site. + if ( ! openssl_x509_check_private_key( file_get_contents( $this->site_data['ssl_crt'] ), file_get_contents( $this->site_data['ssl_key'] ) ) ) { + throw new \Exception( 'ssl-crt is not a valid PEM certificate or does not match ssl-key' ); + } } /** From 09813d9f6341a94692d9f6decbbdd106d10ba9e1 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Thu, 24 Sep 2026 10:41:21 +0000 Subject: [PATCH 3/3] fix(ssl): don't truncate custom cert when copied onto itself Symfony Filesystem::copy() opens the target for writing before reading the source, so passing the files already in nginx-proxy/certs (the natural way to re-enable custom SSL after --ssl=off) left an empty key and cert while the command reported success. Skip the copy when the source already is the destination. --- src/helper/class-ee-site.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/helper/class-ee-site.php b/src/helper/class-ee-site.php index ef75640c..f8250656 100644 --- a/src/helper/class-ee-site.php +++ b/src/helper/class-ee-site.php @@ -2286,8 +2286,13 @@ protected function custom_site_ssl() { $ssl_key_dest = sprintf( '%1$s/nginx-proxy/certs/%2$s.key', remove_trailing_slash( EE_SERVICE_DIR ), $this->site_data['site_url'] ); $ssl_crt_dest = sprintf( '%1$s/nginx-proxy/certs/%2$s.crt', remove_trailing_slash( EE_SERVICE_DIR ), $this->site_data['site_url'] ); - $this->fs->copy( $this->site_data['ssl_key'], $ssl_key_dest, true ); - $this->fs->copy( $this->site_data['ssl_crt'], $ssl_crt_dest, true ); + // Copying a file onto itself truncates it, e.g. when re-enabling SSL with the files already in the certs dir. + if ( realpath( $ssl_key_dest ) !== $this->site_data['ssl_key'] ) { + $this->fs->copy( $this->site_data['ssl_key'], $ssl_key_dest, true ); + } + if ( realpath( $ssl_crt_dest ) !== $this->site_data['ssl_crt'] ) { + $this->fs->copy( $this->site_data['ssl_crt'], $ssl_crt_dest, true ); + } } /**