Skip to content

fix(ssl): copy and validate custom certs on site update - #489

Open
mrrobot47 wants to merge 3 commits into
EasyEngine:developfrom
mrrobot47:fix/update-ssl-custom-copy-certs
Open

mrrobot47 wants to merge 3 commits into
EasyEngine:developfrom
mrrobot47:fix/update-ssl-custom-copy-certs

Conversation

@mrrobot47

@mrrobot47 mrrobot47 commented Jun 30, 2026 •

Copy link
Copy Markdown
Member

Problem

ee site update --ssl=custom set the DB flag and enabled HTTPS but never copied the certificate files. update_ssl() calls www_ssl_wrapper(), which explicitly skips all cert work for custom, and the cert-copy helpers (validate_site_custom_ssl() / custom_site_ssl()) were only invoked from the create path. Result: HTTPS turned on with no certs in nginx-proxy/certs/, so the site served a wrong/default cert. This is also the path Cloner.php recommends after cloning a custom-SSL site, so that guidance led to a broken state too.

Fix

In update_ssl(), when the target type is custom, mirror the create path: validate --ssl-key/--ssl-crt via validate_site_custom_ssl() and copy them via custom_site_ssl() before www_ssl_wrapper(). Also documents the --ssl-key/--ssl-crt flags and adds an example. To replace an expiring custom cert, disable SSL and re-enable it with the new pair (ee site update <site> --ssl=off, then ee site update <site> --ssl=custom --ssl-key=... --ssl-crt=...); running --ssl=custom on a site that already has SSL still stops with "SSL is already enabled".

Missing or nonexistent --ssl-key/--ssl-crt, a certificate that isn't valid PEM, or a key that doesn't match the certificate fails fast (inside the existing try/catch, before $site->save()), so nothing is copied and there is no partial DB-vs-cert state. Re-enabling with the files already in nginx-proxy/certs/ as the source no longer truncates them.

Known limitation

If the post-copy bring-up (www_ssl_wrapper) throws, the copied cert files are left in place (no rollback); re-running the command overwrites them. The create path's catch_clean unwind is not mirrored here — a follow-up could unify the two cert paths.

Testing

Manual: create a non-SSL site, generate a self-signed key/crt, run ee site update <site> --ssl=custom --ssl-key=... --ssl-crt=..., and assert nginx-proxy/certs/<site>.key|.crt exist and the served cert matches.

Tested on Ubuntu 26.04 with EasyEngine 4.12.0: valid pair with relative paths (served cert matches, HTTP redirects to HTTPS), missing flags, nonexistent path and mismatched pair (rejected, nothing copied), same-path re-enable, the off-then-custom replacement flow, and create --ssl=custom regressions.

Merge note

#492 adds assert_valid_cert_key_pair() to the same function, which also checks the key/cert match. Once both are merged, this PR's openssl_x509_check_private_key() check is redundant; whichever merges second should drop the duplicate.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

… 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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants