Skip to content

feat(site): add alias domain hooks and validate alias domain names - #502

Merged
mrrobot47 merged 7 commits into
EasyEngine:developfrom
mrrobot47:feat/alias-domains-updated-hook
Sep 25, 2026
Merged

mrrobot47 merged 7 commits into
EasyEngine:developfrom
mrrobot47:feat/alias-domains-updated-hook

Conversation

@mrrobot47

@mrrobot47 mrrobot47 commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds hooks around alias domain updates so packages that keep per-domain config (auth-command's htpasswd and ACL files) can protect new alias domains before the proxy serves them and clean up after removal. Alias domain names are now validated and blank entries dropped, since they end up in VIRTUAL_HOST and in per-domain proxy file names. Site delete leaves auth cleanup to auth-command's site_cleanup hook.

Changes

  • Fire site_alias_domains_updated ($site_url, $domains_to_add, $domains_to_delete) once an alias domain change has been applied.
  • Fire site_alias_domains_before_update ($site_url, $domains_to_add) after the input is validated and before docker-compose.yml is dumped, and site_alias_domains_update_failed ($site_url, $domains_to_add) when the update is aborted after that point (a callback or the compose dump throws, or the Let's Encrypt renewal fails and the old containers are restored).
  • Trim alias domain lists and drop blank entries on update and on html site create; an update with nothing left to add or delete is refused. The split is shared as EE\Site\Utils\split_alias_domains().
  • Add EE\Site\Utils\validate_alias_domains(): only a hostname or *.hostname with labels of letters, digits, - and _ is accepted (a label can't start with - or _, so _wildcard.X can't collide with another site's wildcard files, or end with -), and default / default_admin_tools (the global auth file names) are rejected in any case. The same rule is exposed per name as is_valid_alias_domain(), and the reserved names as is_reserved_proxy_file_name(), for auth-command. ee site update --add-alias-domains and ee site create --type=html --alias-domains exit with an error listing the invalid names before changing anything. Alias domains being deleted aren't checked, so existing invalid ones can still be removed.
  • delete_site() no longer removes htpasswd/<site> or the site's auth and whitelist rows; auth-command's site_cleanup hook removes them along with the _wildcard.<site>, alias and _acl files.

Release / merge order

These PRs ship together in one core release:

Order:

  1. Merge the dockerfiles PR, tag a new dockerfiles release and confirm the new easyengine/nginx-proxy image is on Docker Hub.
  2. Merge the site-command, auth-command, site-type-wp and site-type-php PRs close together (nightly and PR CI build dev-develop of every package) and tag each package.
  3. In one core release, bump the four package pins and easyengine/nginx-proxy in img-versions.json together.

Why they are coupled:

  • auth-command's container migration regenerates every site's auth files on upgrade and needs the new nginx-proxy image in the same release: the old template applies a _wildcard.X file to sibling sites too (e.g. shop.example.com picks up _wildcard.example.com).
  • auth-command syncs alias domain auth files from the site_alias_domains_before_update, site_alias_domains_updated and site_alias_domains_update_failed hooks, which only the new site-command fires.
  • site-command no longer removes a site's auth files and rows on site delete; auth-command's site_cleanup hook (autoloaded since feat(auth): support http auth and ip whitelist on wildcard subdomains and alias domains auth-command#57) does it.
  • site-type-wp and site-type-php call site-command's new split_alias_domains() and validate_alias_domains(), and auth-command its new is_valid_alias_domain() and is_reserved_proxy_file_name(); with an older site-command, they fail with a fatal "undefined function" error.
  • The dockerfiles change is breaking for hand-made feat(nginx-proxy): Add wildcard HTTP auth support for WordPress multisite dockerfiles#298-style _wildcard.X files that were also relied on to protect X itself: X now needs its own htpasswd/X (and vhost.d/X_acl) file.

Testing

  • Tested on a live EasyEngine 4.12 install with html, php, wp, wp subdir and wp subdom sites, with plain and *. aliases, multiple users, global auth, IP whitelists, alias add/remove, site delete, self-signed SSL and Let's Encrypt.
  • PHP 7.4–8.5 lint.

Fire site_alias_domains_updated with the site URL and the added and removed alias domains once the alias change has been applied, so packages that keep per-domain config (e.g. auth-command's htpasswd and whitelist files) can sync it.
`--add-alias-domains='b.com,'` stored an empty alias domain, and blank entries also reached the alias domain hooks, where they turned into file names. The existing, added and deleted lists are now trimmed and blank entries dropped before any processing, an update with nothing left to add or delete is refused, and the hook gets re-indexed arrays.
`site_alias_domains_updated` fires only after the new VIRTUAL_HOST has been rendered and the containers are up, so per-domain proxy config added from it (like auth-command's htpasswd and ACL files) arrives after the new alias domains are already served. `site_alias_domains_before_update` ($site_url, $domains_to_add) now fires once the input is validated and before docker-compose.yml is dumped.

`site_alias_domains_update_failed` ($site_url, $domains_to_add) fires when the update is aborted after that point: when a before-update callback or dumping the compose file throws, or when the Let's Encrypt renewal fails and the revert has restored the old containers. The site then still has its old alias domains in the database. The flag is set before the before-update hook runs, so a callback that throws still gets the failure hook.
site-command no longer removes `htpasswd/<site>` or the site's auth and whitelist rows in delete_site(). auth-command's `site_cleanup` hook, which runs just before, removes them along with the `_wildcard.<site>`, alias and `_acl` files, so the copy here was a duplicate.

Requires auth-command with the site_cleanup hook autoloaded (EasyEngine/auth-command#57). The two must ship in the same core release, or site delete leaves the site's auth files and rows behind.
`ee site create --type=html --alias-domains='a.com,,b.com,'` stored empty alias domains, which ended up in VIRTUAL_HOST and the nginx server_name. The trim-and-drop-blanks split used on update is now `split_alias_domains()` in the site utils and is also used on create. A flag passed without a value no longer becomes the alias domain `1`.
Alias domains were stored as given, so names like `../evil`, `a..b`, `a.com.` or `default` reached VIRTUAL_HOST and the per-domain proxy files, where `default` and `default_admin_tools` map onto the global auth and ACL files. `validate_alias_domains()` now allows only a hostname or `*.hostname` whose dot-separated labels use letters, digits, `-` and `_` (not starting or ending with `-`), and rejects `default` and `default_admin_tools` in any case. `ee site update --add-alias-domains` and `ee site create --type=html --alias-domains` exit with an error listing the invalid names before changing anything. Alias domains being deleted aren't checked, so existing invalid ones can still be removed.
An alias like `_wildcard.example.com` passed validation, but it shares its proxy file names with the `*.example.com` files of site example.com, so auth-command could delete or overwrite them. Labels can no longer start with `_` (hostnames like `my_blog.example.com` still work). The rule is now also exposed per name as `is_valid_alias_domain()`, and the global proxy file names as `is_reserved_proxy_file_name()`, so auth-command can use the same rule.
@mrrobot47
mrrobot47 marked this pull request as ready for review September 25, 2026 05:13
@mrrobot47
mrrobot47 merged commit 45f4012 into EasyEngine:develop Sep 25, 2026
1 of 5 checks passed
mrrobot47 added a commit to mrrobot47/site-command that referenced this pull request Sep 25, 2026
…gine#502)

AUTH-1 is fixed on develop, so the underscore alias scenario is a regular regression check. "Delete an alias domain" now waits for the remaining alias to answer 200 before checking the deleted one: the recreated nginx container drops all of the site's domains from the proxy until docker-gen catches up, and on develop the command returns inside that gap.
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.

1 participant