Conversation
…y template The auth files migration runs before the image migration, and reloaded the proxy right after writing the new _wildcard.X files. The old nginx-proxy template applies _wildcard.X to every subdomain of X without its own file, so a sibling site such as shop.<multisite> asked for the multisite's password until the proxy was recreated, and indefinitely when the image pull failed. The migration now backs up htpasswd/ and vhost.d/*_acl to /opt/easyengine/.backup/auth-migration-<ts>/ and reloads the proxy only when the running one has the new template (its nginx.tmpl defines the acl block); otherwise the image migration's recreated proxy renders the files. down() restores the backup exactly, including removing files it added, and reloads the proxy if it runs. The backup is kept after a successful upgrade for a manual downgrade.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Docker events can still make the old template consume newly written wildcard files before proxy replacement.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Prevents wildcard authentication files from being applied prematurely by legacy nginx-proxy templates during upgrades.
Changes:
- Backs up and restores authentication/ACL files.
- Detects the running proxy template before reloading.
- Adds rollback support for failed upgrades.
| File | Description |
|---|---|
migrations/container/20260924120000_auth-command_regenerate_site_auth_files.php |
Adds template detection and reversible auth-file migration. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+55
to
+59
| if ( $new_template ) { | ||
| \EE\Site\Utils\reload_global_nginx_proxy(); | ||
| } else { | ||
| // The old template applies `_wildcard.X` to every subdomain of X; the image migration recreates the proxy with the new one. | ||
| EE::debug( 'Not reloading nginx-proxy: it runs the old template, the image migration recreates it.' ); |
Skipping the reload didn't close the leak: the old nginx-proxy's docker-gen re-renders its template on any container event on the host, e.g. the cron container restarting, and that template applies _wildcard.X to every subdomain of X without its own file. Once the migration had written _wildcard.* files into the proxy's mounts, a sibling site such as shop.<multisite> asked for the multisite's password until the proxy was recreated, until down() restored the files after a failed pull, or until the retry when ee was killed mid-pull. When the running proxy has the old template, or isn't running, the migration now writes only the exact-host htpasswd and ACL files, which the old template handles correctly, and stages the _wildcard.* ones in /opt/easyengine/.staging/auth-wildcard/ (outside the mounts, mode 0700). They are moved into place (temp file + rename) and the proxy is reloaded by the after_docker_image_migration hook once the new proxy runs, or by the first ee run after the migration completed (find_command_to_run_pre), so a killed or failed upgrade heals on the retry. A site whose auth changed since staging, e.g. through the older ee after an interrupted upgrade, gets its files regenerated instead, and files of deleted sites are dropped. down() discards the staging dir. With the new template, nothing changes: everything is written and the proxy reloaded. Until promotion, subdomains stay unprotected as in 4.12.0, and the old template never sees a _wildcard.* file.
The staged _wildcard.* files now live in EE_BACKUP_DIR/auth-wildcard-staging/{htpasswd,vhost.d} (/opt/easyengine/.backup/...) instead of a new /opt/easyengine/.staging dir. It's still outside the proxy's mounts and 0700. Site names always contain a dot, so no per-site .backup/<site>/ dir can clash with it. The migration, the promotion, the self-heal and down() all use get_wildcard_staging_dir().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
The site auth files migration (
20260924120000_auth-command_regenerate_site_auth_files.php, new in v1.3.0) runs before core's image migration and writes the new_wildcard.Xand alias files straight into the proxy's mounts. On an upgrade from 4.12.0 the running proxy still has the old template, which applieshtpasswd/_wildcard.Xto every subdomain of X that has no file of its own. Its docker-gen re-renders that template on any container event on the host, e.g. the cron container restarting or any site restart, with or without a reload. So a sibling site such asshop.<multisite>asks for the multisite's password until the proxy is recreated with the new template, and indefinitely if the image pull fails, becausedown()was empty and the migration stays recorded.Fix
up()first backs uphtpasswd/andvhost.d/*_aclto/opt/easyengine/.backup/auth-migration-<timestamp>/(mode 0700, since the files hold password hashes; copies keep mode and mtime). If the backup fails, the migration fails before anything is written. The backup is kept after a successful upgrade, since a manual downgrade to 4.12.0 needs it.up()checks whether the RUNNING proxy has the new template (grep -c 'define "acl"' /app/nginx.tmplin the proxy; that block only exists in the template that applies_wildcard.Xto*.Xhosts only). A proxy that isn't running counts as old._wildcard.*htpasswd and_wildcard.*_aclfiles are staged inEE_BACKUP_DIR/auth-wildcard-staging/{htpasswd,vhost.d}(/opt/easyengine/.backup/auth-wildcard-staging/, mode 0700), outside the proxy's mounts. A live file of a staged name is removed._wildcard.*file.after_docker_image_migrationhook (fix(migration): revert container migrations when a later upgrade step fails easyengine#1936), right after the image migration.eerun (find_command_to_run_pre, once per process, only when the staging dir exists and no migration is pending), e.g. aftereewas killed mid-upgrade.down()discards the staging dir and makeshtpasswd/and the*_aclfiles ofvhost.d/identical to the backup (added files removed, restored files renamed into place). It reloads the proxy if it's running and removes the backup. Core calls it only when the image migration (or an earlier step) fails, not after the image migration has completed.Known window: until promotion, subdomains of subdomain multisites and
*.Xalias domains are unprotected, which is the 4.12.0 behaviour. Promotion happens at the end of the image migration, about 8 s per WP/PHP site after the proxy is recreated (55 s with 6 sites). This fails closed and never leaks. Exact-host names stay protected throughout.Tests
Isolated harness (fake docker emulating the old template, the new one or a stopped proxy; PHP 7.4 and 8.5, 65/65). It covers:
*.Xalias files, with nothing wildcard live, the exact-host files live and the staging 0700 under the backup dir;down()discards staging;down()after promotion restores exactly;docker-gen render check with
easyengine/nginx-proxy:v4.11.1and:v4.13.0:*.Xhost gets the wildcard auth.Live upgrade from 4.12.0, with a phar of develop + fix(migration): revert container migrations when a later upgrade step fails easyengine#1936 + #1937 + this PR built the way CI builds the nightly.
shop.<multisite>without auth, a site withaliasand*.Xaliases plus auth and a whitelist, and WP--cache, PHP with a DB, php 8.1, php 7.4 and html sites._wildcard.*files every 0.2 s.shop.never 401 (0/88), no_wildcard.*file in the mounts at any time, files byte-identical to before, staging and backup removed, row removedshop.never 401 (0/113);_wildcard.*files appear only after the new proxy runs, then subsites, deeper subsites, alias and*.Xaliases answer 401 and accept the credentials; backup kepteekilled mid-pull, password changed with the 4.12.0 phar, retryshop.never 401 (0/72); the retry regenerates the outdated files from the DB: the new password works on subsites, the old one is refusedee cli versionoree site listpromotes it onceThe TESTING-NOTES §1 matrix passed after the upgrade (site auth on subsites and aliases, whitelist, global auth, alias add/remove and validation, self-signed HTTPS, site delete cleanup), and so did create/delete of every site type.
Behat (
@destructive @migration,features/auth-migration.featureon the test branch, on the earlier head): with the registry blocked, the upgrade fails, the alias/_wildcardfiles are absent again, the row is removed and the version stays old. After unblocking, they're written and the aliases answer 401. CI: https://github.com/mrrobot47/auth-command/actions/runs/36124341500.php -lon PHP 7.4 and 8.5; no new PHPCS violations.Release
Needs a patch tag, auth-command v1.3.1, and a core pin bump to it before core v4.13.0 is tagged. Ship it with EasyEngine/easyengine#1936 in v4.13.0.