Skip to content

Add opt-in WordPress runtime hardening controls#1649

Merged
retlehs merged 4 commits intomasterfrom
feat/opt-in-hardened-php-runtime
Mar 11, 2026
Merged

Add opt-in WordPress runtime hardening controls#1649
retlehs merged 4 commits intomasterfrom
feat/opt-in-hardened-php-runtime

Conversation

@retlehs
Copy link
Copy Markdown
Member

@retlehs retlehs commented Mar 5, 2026

Summary

This PR adds an opt-in hardening mode for WordPress runtime file permissions in Trellis (related to #368), while preserving current behavior by default.

When enabled, PHP-FPM can run as a separate runtime identity and writable paths are explicitly allowlisted.

What Changed

1. New hardening variables (roles/wordpress-setup/defaults/main.yml)

Added:

wordpress_runtime_hardened: false
wordpress_runtime_user: www-data
wordpress_runtime_group: www-data
wordpress_runtime_writable_paths:
  - shared/uploads
wordpress_runtime_cron_as_runtime_user: false

2. PHP-FPM runtime identity is conditional (roles/wordpress-setup/templates/php-fpm-pool-wordpress.conf.j2)

Pool user/group now switch based on hardening mode:

  • hardened = false -> web_user / web_group (existing behavior)
  • hardened = true -> wordpress_runtime_user / wordpress_runtime_group

Also added a clarifying comment that socket owner/group remain www-data so Nginx can connect to PHP-FPM.

3. Hardened-mode validation + writable path ownership (roles/wordpress-setup/tasks/main.yml + runtime-writable-paths.yml)

When wordpress_runtime_hardened: true:

  • Validate runtime group exists (getent group)
  • Validate runtime user exists (getent passwd)
  • Ensure writable paths exist and are owned by runtime user/group (0775)

Per-site writable path override is supported:

  • wordpress_sites.<site>.runtime_writable_paths
  • falls back to global wordpress_runtime_writable_paths

4. Optional cron user alignment (roles/wordpress-setup/tasks/main.yml)

WP cron can run as runtime user when both are true:

  • wordpress_runtime_hardened: true
  • wordpress_runtime_cron_as_runtime_user: true

Otherwise cron continues to run as web_user (backward compatible).

Example Configuration

In group_vars/production/main.yml (or group_vars/all/main.yml):

wordpress_runtime_hardened: true
wordpress_runtime_user: php-app
wordpress_runtime_group: php-app
wordpress_runtime_writable_paths:
  - shared/uploads
  - current/web/app/cache

# Optional
wordpress_runtime_cron_as_runtime_user: true

Per-site override:

wordpress_sites:
  example.com:
    runtime_writable_paths:
      - shared/uploads
      - current/web/app/cache

Backward Compatibility

  • Defaults keep current Trellis behavior unchanged.
  • Hardening is opt-in.

Custom Runtime User/Group Note

wordpress_runtime_user / wordpress_runtime_group must exist on target hosts when hardening is enabled.

  • If custom values are used (for example php-app), provision that OS user/group first.
  • In Trellis, define that user in users config (for example group_vars/all/users.yml) so the account exists before enabling hardening:
users:
  - name: php-app
    groups:
      - php-app
    keys: []
  • Then set:
wordpress_runtime_hardened: true
wordpress_runtime_user: php-app
wordpress_runtime_group: php-app
  • The playbook fails fast with getent checks if the configured user/group are missing.
  • If you prefer not to provision a custom runtime account, use defaults (www-data), but runtime/deploy identity separation may be reduced depending on your existing web_user setup.

Close #368

Docs updates: roots/docs#566

@retlehs retlehs requested a review from swalkinshaw March 5, 2026 21:14
@retlehs retlehs self-assigned this Mar 5, 2026
retlehs added a commit to roots/docs that referenced this pull request Mar 6, 2026
Add documentation for the opt-in runtime hardening feature from
roots/trellis#1649, covering PHP-FPM identity separation, writable
path allowlisting, and per-site configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread roles/wordpress-setup/templates/php-fpm-pool-wordpress.conf.j2 Outdated
Comment thread roles/wordpress-setup/tasks/main.yml Outdated
Comment thread roles/wordpress-setup/tasks/main.yml Outdated
Comment thread roles/wordpress-setup/tasks/main.yml Outdated
@retlehs retlehs merged commit e32ec79 into master Mar 11, 2026
2 checks passed
@retlehs retlehs deleted the feat/opt-in-hardened-php-runtime branch March 11, 2026 14:32
retlehs added a commit to roots/docs that referenced this pull request Mar 11, 2026
Add documentation for the opt-in runtime hardening feature from
roots/trellis#1649, covering PHP-FPM identity separation, writable
path allowlisting, and per-site configuration.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

PHP has write access to entire web root

2 participants