generated from bisco/codex-bootstrap
feat: add internal WordPress cron runner
This commit is contained in:
@@ -11,6 +11,7 @@ WP_TITLE=Azione!Lab
|
|||||||
WP_ADMIN_USER=azionelab-admin
|
WP_ADMIN_USER=azionelab-admin
|
||||||
WP_ADMIN_PASSWORD=replace-with-a-local-admin-password
|
WP_ADMIN_PASSWORD=replace-with-a-local-admin-password
|
||||||
WP_ADMIN_EMAIL=admin@example.org
|
WP_ADMIN_EMAIL=admin@example.org
|
||||||
|
WP_CRON_INTERVAL_SECONDS=300
|
||||||
NGINX_BIND_ADDRESS=127.0.0.1
|
NGINX_BIND_ADDRESS=127.0.0.1
|
||||||
NGINX_HTTP_PORT=8080
|
NGINX_HTTP_PORT=8080
|
||||||
NGINX_HTTPS_PORT=8443
|
NGINX_HTTPS_PORT=8443
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
A warm, editorial single-page website for the Azione!Lab contemporary theatre
|
A warm, editorial single-page website for the Azione!Lab contemporary theatre
|
||||||
workshop. WordPress manages the content, a custom theme owns the visual system, and
|
workshop. WordPress manages the content, a custom theme owns the visual system, and
|
||||||
Docker Compose provides MariaDB, NGINX, optional Let's Encrypt, WP-CLI, and isolated
|
Docker Compose provides MariaDB, NGINX, an internal WordPress cron runner, optional
|
||||||
browser tests.
|
Let's Encrypt, WP-CLI, and isolated browser tests.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ browser tests.
|
|||||||
- `db`: MariaDB 11.8 LTS, available only on the internal data network.
|
- `db`: MariaDB 11.8 LTS, available only on the internal data network.
|
||||||
- `proxy`: the only published service; routes `azionelab.org` to WordPress and applies
|
- `proxy`: the only published service; routes `azionelab.org` to WordPress and applies
|
||||||
edge security controls.
|
edge security controls.
|
||||||
|
- `wp-cron`: internal WP-CLI runner for due scheduled WordPress events.
|
||||||
- `certbot`: optional HTTP-01 certificate issue/renewal service.
|
- `certbot`: optional HTTP-01 certificate issue/renewal service.
|
||||||
- `wp-cli`: opt-in bootstrap and maintenance service.
|
- `wp-cli`: opt-in bootstrap and maintenance service.
|
||||||
- `tests/functional`: Playwright tests running only through the public virtual host.
|
- `tests/functional`: Playwright tests running only through the public virtual host.
|
||||||
@@ -61,6 +62,7 @@ Use this checklist for the first run of a new environment.
|
|||||||
|
|
||||||
- set strong `MARIADB_PASSWORD`, `MARIADB_ROOT_PASSWORD`, and `WP_ADMIN_PASSWORD`;
|
- set strong `MARIADB_PASSWORD`, `MARIADB_ROOT_PASSWORD`, and `WP_ADMIN_PASSWORD`;
|
||||||
- set `WP_URL` to the public URL, for example `https://lab.younerd.org`;
|
- set `WP_URL` to the public URL, for example `https://lab.younerd.org`;
|
||||||
|
- keep `WP_CRON_INTERVAL_SECONDS=300` unless scheduled jobs need a different cadence;
|
||||||
- set `LETSENCRYPT_DOMAIN` to the same hostname when direct Let's Encrypt is used;
|
- set `LETSENCRYPT_DOMAIN` to the same hostname when direct Let's Encrypt is used;
|
||||||
- keep `LETSENCRYPT_STAGING=1` for the first certificate test;
|
- keep `LETSENCRYPT_STAGING=1` for the first certificate test;
|
||||||
- configure `DB_DATA_PATH`, `WORDPRESS_DATA_PATH`, `LETSENCRYPT_DATA_PATH`, and
|
- configure `DB_DATA_PATH`, `WORDPRESS_DATA_PATH`, `LETSENCRYPT_DATA_PATH`, and
|
||||||
@@ -127,7 +129,7 @@ Use this checklist for the first run of a new environment.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose ps
|
docker compose ps
|
||||||
docker compose logs --tail=100 proxy wordpress db certbot
|
docker compose logs --tail=100 proxy wordpress wp-cron db certbot
|
||||||
```
|
```
|
||||||
|
|
||||||
Open the configured `WP_URL` and `/wp-admin/`.
|
Open the configured `WP_URL` and `/wp-admin/`.
|
||||||
@@ -212,6 +214,10 @@ docker compose --profile tools run --rm wp-cli /scripts/bootstrap.sh
|
|||||||
The `docker-compose.test.yml` override intentionally uses disposable test volumes; do
|
The `docker-compose.test.yml` override intentionally uses disposable test volumes; do
|
||||||
not use it for production or staging bootstrap commands.
|
not use it for production or staging bootstrap commands.
|
||||||
|
|
||||||
|
`wp-cron` runs internally through WP-CLI every `WP_CRON_INTERVAL_SECONDS` seconds. It
|
||||||
|
does not publish ports and does not join the public `web` network. Public
|
||||||
|
`/wp-cron.php` requests remain blocked intentionally.
|
||||||
|
|
||||||
## Maintenance commands with internet access
|
## Maintenance commands with internet access
|
||||||
|
|
||||||
The default `wp-cli` service joins only the internal database network, so it cannot
|
The default `wp-cli` service joins only the internal database network, so it cannot
|
||||||
@@ -232,8 +238,9 @@ Compose file.
|
|||||||
## Useful commands
|
## Useful commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose logs -f proxy wordpress db certbot
|
docker compose logs -f proxy wordpress wp-cron db certbot
|
||||||
docker compose --profile tools run --rm wp-cli -c 'wp plugin list'
|
docker compose --profile tools run --rm wp-cli -c 'wp plugin list'
|
||||||
|
docker compose --profile tools run --rm wp-cli -c 'wp cron event list'
|
||||||
docker compose --profile tools run --rm wp-cli -c 'wp core version'
|
docker compose --profile tools run --rm wp-cli -c 'wp core version'
|
||||||
docker compose -f docker-compose.yml -f docker-compose.maintenance.yml --profile tools run --rm wp-cli -c 'wp core update --version=7.0.2 --force'
|
docker compose -f docker-compose.yml -f docker-compose.maintenance.yml --profile tools run --rm wp-cli -c 'wp core update --version=7.0.2 --force'
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ services:
|
|||||||
- test_wordpress_data:/var/www/html
|
- test_wordpress_data:/var/www/html
|
||||||
- ./wp-cli/bootstrap.sh:/scripts/bootstrap.sh:ro
|
- ./wp-cli/bootstrap.sh:/scripts/bootstrap.sh:ro
|
||||||
|
|
||||||
|
wp-cron:
|
||||||
|
environment:
|
||||||
|
WP_CRON_INTERVAL_SECONDS: 300
|
||||||
|
volumes:
|
||||||
|
- test_wordpress_data:/var/www/html
|
||||||
|
- ./wp-cli/cron.sh:/scripts/cron.sh:ro
|
||||||
|
|
||||||
proxy:
|
proxy:
|
||||||
networks:
|
networks:
|
||||||
web:
|
web:
|
||||||
|
|||||||
@@ -81,6 +81,47 @@ services:
|
|||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
pids_limit: 300
|
pids_limit: 300
|
||||||
|
|
||||||
|
wp-cron:
|
||||||
|
image: wordpress:cli-2.12.0-php8.3
|
||||||
|
restart: unless-stopped
|
||||||
|
init: true
|
||||||
|
user: "33:33"
|
||||||
|
working_dir: /var/www/html
|
||||||
|
environment:
|
||||||
|
WORDPRESS_DB_HOST: db:3306
|
||||||
|
WORDPRESS_DB_NAME: ${MARIADB_DATABASE:-azionelab}
|
||||||
|
WORDPRESS_DB_USER: ${MARIADB_USER:-azionelab}
|
||||||
|
WORDPRESS_DB_PASSWORD: ${MARIADB_PASSWORD:-replace-with-a-local-password}
|
||||||
|
WORDPRESS_TABLE_PREFIX: ${WORDPRESS_TABLE_PREFIX:-azl_}
|
||||||
|
WP_CRON_INTERVAL_SECONDS: ${WP_CRON_INTERVAL_SECONDS:-300}
|
||||||
|
WP_ENVIRONMENT_TYPE: ${WP_ENVIRONMENT_TYPE:-local}
|
||||||
|
WP_URL: ${WP_URL:-http://azionelab.org:8080}
|
||||||
|
HOME: /tmp
|
||||||
|
HTTP_HOST: ${LETSENCRYPT_DOMAIN:-azionelab.org}
|
||||||
|
entrypoint: ["/bin/sh"]
|
||||||
|
command: ["/scripts/cron.sh"]
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ${WORDPRESS_DATA_PATH:-./runtime/wordpress}
|
||||||
|
target: /var/www/html
|
||||||
|
bind:
|
||||||
|
create_host_path: true
|
||||||
|
- ./wp-cli/cron.sh:/scripts/cron.sh:ro
|
||||||
|
networks:
|
||||||
|
- data
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
wordpress:
|
||||||
|
condition: service_healthy
|
||||||
|
tmpfs:
|
||||||
|
- /tmp
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
pids_limit: 100
|
||||||
|
|
||||||
proxy:
|
proxy:
|
||||||
build:
|
build:
|
||||||
context: ./nginx
|
context: ./nginx
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ classic theme for exact semantic markup and responsive design. Store homepage fi
|
|||||||
sanitized theme modifications, while a must-use plugin owns Shows and Gallery custom
|
sanitized theme modifications, while a must-use plugin owns Shows and Gallery custom
|
||||||
post types so structured content is not lost when changing themes.
|
post types so structured content is not lost when changing themes.
|
||||||
|
|
||||||
NGINX is the only public entry point. WP-CLI provides an idempotent opt-in bootstrap;
|
NGINX is the only public entry point. Public `wp-cron.php` requests stay blocked, while
|
||||||
Certbot provides opt-in direct TLS. Docker networks isolate the database. Runtime state
|
an internal WP-CLI based `wp-cron` service runs due scheduled events over the private
|
||||||
uses host-based bind mounts with a preparation script for ownership/mode, while
|
data network. WP-CLI provides an idempotent opt-in bootstrap; Certbot provides opt-in
|
||||||
functional tests use separate Docker volumes.
|
direct TLS. Docker networks isolate the database. Runtime state uses host-based bind
|
||||||
|
mounts with a preparation script for ownership/mode, while functional tests use
|
||||||
|
separate Docker volumes.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
@@ -30,6 +32,8 @@ functional tests use separate Docker volumes.
|
|||||||
- Core/theme/plugin patches require an image rebuild in production.
|
- Core/theme/plugin patches require an image rebuild in production.
|
||||||
- Fixed participation cards remain code until editorial requirements justify another
|
- Fixed participation cards remain code until editorial requirements justify another
|
||||||
structured content type.
|
structured content type.
|
||||||
|
- Scheduled WordPress jobs depend on the internal `wp-cron` service instead of visitor
|
||||||
|
traffic.
|
||||||
|
|
||||||
## Alternatives considered
|
## Alternatives considered
|
||||||
|
|
||||||
@@ -43,8 +47,9 @@ functional tests use separate Docker volumes.
|
|||||||
|
|
||||||
The architecture inherits WordPress's public CMS attack surface. The implementation
|
The architecture inherits WordPress's public CMS attack surface. The implementation
|
||||||
reduces it with network isolation, no direct application port, disabled file editing
|
reduces it with network isolation, no direct application port, disabled file editing
|
||||||
and XML-RPC, production immutability, sanitization/escaping, rate limiting, security
|
and XML-RPC, production immutability, sanitized/escaped rendering, no bundled demo
|
||||||
headers, and fail-closed TLS. Admin MFA and allowlisting remain external controls.
|
plugins, internal-only scheduled jobs, rate limiting, security headers, and fail-closed
|
||||||
|
TLS. Admin MFA and allowlisting remain external controls.
|
||||||
|
|
||||||
## Operational impact
|
## Operational impact
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ structured editorial content survives a theme change. These custom post types ar
|
|||||||
editorial data sources for the homepage, not standalone public routes or REST
|
editorial data sources for the homepage, not standalone public routes or REST
|
||||||
collections. Images use WordPress featured images with local SVG fallbacks.
|
collections. Images use WordPress featured images with local SVG fallbacks.
|
||||||
|
|
||||||
WP-CLI is an opt-in tools-profile service. Its idempotent bootstrap installs WordPress,
|
`wp-cron` is an internal WP-CLI runner on the `data` network. It executes due scheduled
|
||||||
activates the theme, configures the site, and creates realistic demo content. Certbot is
|
events with `wp cron event run --due-now` and keeps public `wp-cron.php` requests
|
||||||
another optional service, enabled only for direct deployments. It shares challenge and
|
blocked. WP-CLI is also available as an opt-in tools-profile service. Its idempotent
|
||||||
certificate volumes with NGINX but has no container-control access.
|
bootstrap installs WordPress, activates the theme, configures the site, and creates
|
||||||
|
realistic demo content. Certbot is another optional service, enabled only for direct
|
||||||
|
deployments. It shares challenge and certificate volumes with NGINX but has no
|
||||||
|
container-control access.
|
||||||
|
|
||||||
Apache includes a small defense-in-depth hardening file that denies uploaded PHP files,
|
Apache includes a small defense-in-depth hardening file that denies uploaded PHP files,
|
||||||
direct `wp-config.php` requests, and direct access to selected internal WordPress PHP
|
direct `wp-config.php` requests, and direct access to selected internal WordPress PHP
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ docker compose --profile tools run --rm wp-cli /scripts/bootstrap.sh
|
|||||||
|
|
||||||
NGINX binds to loopback ports 8080/8443. WordPress and MariaDB remain private. The
|
NGINX binds to loopback ports 8080/8443. WordPress and MariaDB remain private. The
|
||||||
bootstrap is safe to rerun and does not duplicate demo records.
|
bootstrap is safe to rerun and does not duplicate demo records.
|
||||||
|
The internal `wp-cron` service runs scheduled WordPress events after the bootstrap
|
||||||
|
completes.
|
||||||
|
|
||||||
The default persistent paths are host-based bind mounts under `./runtime`. Before the
|
The default persistent paths are host-based bind mounts under `./runtime`. Before the
|
||||||
first start, run:
|
first start, run:
|
||||||
@@ -37,6 +39,9 @@ Required controls:
|
|||||||
- either direct Let's Encrypt termination or a trusted external load balancer;
|
- either direct Let's Encrypt termination or a trusted external load balancer;
|
||||||
- off-host database/file backups and monitoring.
|
- off-host database/file backups and monitoring.
|
||||||
|
|
||||||
|
The default `wp-cron` service handles scheduled WordPress events internally and should
|
||||||
|
remain enabled unless another controlled cron runner replaces it.
|
||||||
|
|
||||||
When a load balancer terminates TLS, Certbot stays disabled. `TRUST_PROXY_HEADERS=1`
|
When a load balancer terminates TLS, Certbot stays disabled. `TRUST_PROXY_HEADERS=1`
|
||||||
is safe only when firewall/network policy makes the load balancer the sole NGINX
|
is safe only when firewall/network policy makes the load balancer the sole NGINX
|
||||||
caller and it overwrites `X-Forwarded-*` headers.
|
caller and it overwrites `X-Forwarded-*` headers.
|
||||||
|
|||||||
+9
-7
@@ -5,9 +5,10 @@
|
|||||||
```bash
|
```bash
|
||||||
docker compose up --build -d
|
docker compose up --build -d
|
||||||
docker compose ps
|
docker compose ps
|
||||||
docker compose logs -f proxy wordpress db certbot
|
docker compose logs -f proxy wordpress wp-cron db certbot
|
||||||
./scripts/prepare-host-volumes.sh
|
./scripts/prepare-host-volumes.sh
|
||||||
docker compose --profile tools run --rm wp-cli -c 'wp core version'
|
docker compose --profile tools run --rm wp-cli -c 'wp core version'
|
||||||
|
docker compose --profile tools run --rm wp-cli -c 'wp cron event list'
|
||||||
docker compose down
|
docker compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -37,10 +38,11 @@ docker compose restart wordpress proxy
|
|||||||
Do not use the maintenance override for routine bootstrap or inspection commands that
|
Do not use the maintenance override for routine bootstrap or inspection commands that
|
||||||
do not need outbound network access.
|
do not need outbound network access.
|
||||||
|
|
||||||
External `wp-cron.php` requests are blocked to reduce public attack surface. If future
|
External `wp-cron.php` requests are blocked to reduce public attack surface. The
|
||||||
content scheduling, maintenance jobs, or plugin features require WordPress cron, run it
|
default `wp-cron` service runs due scheduled events internally through WP-CLI every
|
||||||
from a controlled host/container cron against the private WordPress service instead of
|
`WP_CRON_INTERVAL_SECONDS` seconds, defaults to 300, and joins only the internal `data`
|
||||||
leaving the public trigger enabled.
|
network. If a scheduled job is late, inspect `docker compose logs wp-cron wordpress db`
|
||||||
|
and run `docker compose --profile tools run --rm wp-cli -c 'wp cron event list'`.
|
||||||
|
|
||||||
## Backup and restore
|
## Backup and restore
|
||||||
|
|
||||||
@@ -61,8 +63,8 @@ the database and file volume together, restart, and verify the homepage, media,
|
|||||||
owner or mode; run `./scripts/prepare-host-volumes.sh` after changing paths or image
|
owner or mode; run `./scripts/prepare-host-volumes.sh` after changing paths or image
|
||||||
user IDs.
|
user IDs.
|
||||||
- SMTP is not configured; WordPress password-reset email needs an external mail service.
|
- SMTP is not configured; WordPress password-reset email needs an external mail service.
|
||||||
- WordPress cron is not publicly triggerable; scheduled jobs need an operator-managed
|
- WordPress cron is not publicly triggerable; scheduled jobs depend on the internal
|
||||||
cron runner if that feature becomes necessary.
|
`wp-cron` runner being healthy.
|
||||||
- Admin MFA and network allowlisting are deployment concerns and are not bundled.
|
- Admin MFA and network allowlisting are deployment concerns and are not bundled.
|
||||||
- WordPress plugins expand the attack surface; install only reviewed, maintained,
|
- WordPress plugins expand the attack surface; install only reviewed, maintained,
|
||||||
necessary plugins.
|
necessary plugins.
|
||||||
|
|||||||
@@ -50,6 +50,39 @@ docker compose restart wordpress proxy
|
|||||||
If DNS still fails with the maintenance override, inspect the host/container DNS
|
If DNS still fails with the maintenance override, inspect the host/container DNS
|
||||||
configuration and firewall rules before retrying the update.
|
configuration and firewall rules before retrying the update.
|
||||||
|
|
||||||
|
## Scheduled WordPress events are late
|
||||||
|
|
||||||
|
The public `/wp-cron.php` trigger is intentionally blocked. Scheduled jobs are executed
|
||||||
|
by the internal `wp-cron` service.
|
||||||
|
|
||||||
|
1. Confirm the runner is present:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose ps wp-cron
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Inspect due events:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile tools run --rm wp-cli -c 'wp cron event list'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Inspect runner logs without printing `.env` values:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs --tail=100 wp-cron wordpress db
|
||||||
|
```
|
||||||
|
|
||||||
|
4. If needed, run due events manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose --profile tools run --rm wp-cli -c 'wp cron event run --due-now'
|
||||||
|
```
|
||||||
|
|
||||||
|
The Site Health loopback test may still report a blocked public loopback while the edge
|
||||||
|
continues to deny `/wp-cron.php`; that is an expected trade-off of the hardened public
|
||||||
|
configuration.
|
||||||
|
|
||||||
## Uploaded image does not appear on the public page
|
## Uploaded image does not appear on the public page
|
||||||
|
|
||||||
1. Confirm the image was selected in **Appearance > Customize**, not only uploaded in
|
1. Confirm the image was selected in **Appearance > Customize**, not only uploaded in
|
||||||
|
|||||||
+5
-1
@@ -11,6 +11,8 @@
|
|||||||
collections.
|
collections.
|
||||||
- File editing is always disabled. Production also disables web-based core, theme, and
|
- File editing is always disabled. Production also disables web-based core, theme, and
|
||||||
plugin changes; patched images are rebuilt and redeployed instead.
|
plugin changes; patched images are rebuilt and redeployed instead.
|
||||||
|
- The WordPress image removes the bundled Akismet and Hello Dolly plugins, and the
|
||||||
|
entrypoint also removes them from existing persistent volumes on startup.
|
||||||
- XML-RPC and comments are disabled. NGINX blocks PHP execution below uploads, dotfiles,
|
- XML-RPC and comments are disabled. NGINX blocks PHP execution below uploads, dotfiles,
|
||||||
direct `wp-config.php` requests, the WordPress readme/license files, direct
|
direct `wp-config.php` requests, the WordPress readme/license files, direct
|
||||||
installation entry points, public mu-plugin directory probing, and external
|
installation entry points, public mu-plugin directory probing, and external
|
||||||
@@ -28,7 +30,9 @@
|
|||||||
WordPress/Apache retains the capabilities needed by the official image internally,
|
WordPress/Apache retains the capabilities needed by the official image internally,
|
||||||
but no WordPress port is published. A containerized security test fails if the
|
but no WordPress port is published. A containerized security test fails if the
|
||||||
WordPress service is configured with host-published ports.
|
WordPress service is configured with host-published ports.
|
||||||
- WP-CLI normally joins only the internal data network. The maintenance override
|
- `wp-cron` runs WordPress scheduled events through WP-CLI on the internal data network
|
||||||
|
only; public `wp-cron.php` remains blocked. WP-CLI normally joins only the internal
|
||||||
|
data network. The maintenance override
|
||||||
attaches WP-CLI to the web network for operator-triggered commands that require
|
attaches WP-CLI to the web network for operator-triggered commands that require
|
||||||
outbound internet access, such as WordPress core downloads; do not use it for routine
|
outbound internet access, such as WordPress core downloads; do not use it for routine
|
||||||
bootstrap or inspection commands.
|
bootstrap or inspection commands.
|
||||||
|
|||||||
+2
-1
@@ -23,7 +23,8 @@ cover content and section order, contact actions, mobile overflow/navigation, se
|
|||||||
landmarks, image alternatives, admin routing, security headers, blocked sensitive
|
landmarks, image alternatives, admin routing, security headers, blocked sensitive
|
||||||
routes, blocked uploaded PHP requests, and unknown virtual hosts. Security checks also
|
routes, blocked uploaded PHP requests, and unknown virtual hosts. Security checks also
|
||||||
assert that WordPress does not publish host ports and that Apache hardening remains
|
assert that WordPress does not publish host ports and that Apache hardening remains
|
||||||
installed in the WordPress image.
|
installed in the WordPress image. They also assert that the default demo plugins are
|
||||||
|
removed and that the internal `wp-cron` service has no public network exposure.
|
||||||
|
|
||||||
Subjective visual review and a real-device accessibility audit remain manual release
|
Subjective visual review and a real-device accessibility audit remain manual release
|
||||||
checks.
|
checks.
|
||||||
|
|||||||
@@ -27,6 +27,26 @@ grep -q '^FROM wordpress:7\.0\.2-php8\.3-apache$' /workspace/wordpress/Dockerfil
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grep -q 'wp-content/plugins/akismet' /workspace/wordpress/Dockerfile || {
|
||||||
|
echo "The WordPress image must remove the bundled Akismet plugin." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -q 'wp-content/plugins/hello.php' /workspace/wordpress/Dockerfile || {
|
||||||
|
echo "The WordPress image must remove the bundled Hello Dolly plugin." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -q 'wp-content/plugins/akismet' /workspace/wordpress/entrypoint-wrapper.sh || {
|
||||||
|
echo "The WordPress entrypoint must remove Akismet from existing persistent volumes." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -q 'wp-content/plugins/hello.php' /workspace/wordpress/entrypoint-wrapper.sh || {
|
||||||
|
echo "The WordPress entrypoint must remove Hello Dolly from existing persistent volumes." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
grep -q 'wp-content/(uploads|files)' /workspace/wordpress/apache-hardening.conf || {
|
grep -q 'wp-content/(uploads|files)' /workspace/wordpress/apache-hardening.conf || {
|
||||||
echo "Apache hardening must block PHP execution below uploads/files." >&2
|
echo "Apache hardening must block PHP execution below uploads/files." >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -76,3 +96,57 @@ grep -q "^[[:space:]]*-[[:space:]]*web$" /workspace/docker-compose.maintenance.y
|
|||||||
echo "The maintenance wp-cli service must explicitly opt into the web network." >&2
|
echo "The maintenance wp-cli service must explicitly opt into the web network." >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grep -Eq "^[[:space:]]{2}wp-cron:" "$compose_file" || {
|
||||||
|
echo "Compose must define an internal wp-cron service." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -q "wp cron event run --due-now" /workspace/wp-cli/cron.sh || {
|
||||||
|
echo "The wp-cron runner must execute due WordPress cron events via WP-CLI." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
awk '
|
||||||
|
/^ wp-cron:/ {
|
||||||
|
in_wp_cron = 1
|
||||||
|
has_data_network = 0
|
||||||
|
has_db_host = 0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
in_wp_cron && /^ [A-Za-z0-9_-]+:/ {
|
||||||
|
if (!has_data_network) {
|
||||||
|
print "The wp-cron service must join the internal data network." > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if (!has_db_host) {
|
||||||
|
print "The wp-cron service must receive WordPress database environment variables." > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
in_wp_cron = 0
|
||||||
|
}
|
||||||
|
in_wp_cron && /^ ports:/ {
|
||||||
|
print "The wp-cron service must not publish host ports." > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
in_wp_cron && /^[[:space:]]+WORDPRESS_DB_HOST:/ {
|
||||||
|
has_db_host = 1
|
||||||
|
}
|
||||||
|
in_wp_cron && /^ - data$/ {
|
||||||
|
has_data_network = 1
|
||||||
|
}
|
||||||
|
in_wp_cron && /^ - web$/ {
|
||||||
|
print "The wp-cron service must not join the public web network." > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if (in_wp_cron && !has_data_network) {
|
||||||
|
print "The wp-cron service must join the internal data network." > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if (in_wp_cron && !has_db_host) {
|
||||||
|
print "The wp-cron service must receive WordPress database environment variables." > "/dev/stderr"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$compose_file"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ COPY theme/azionelab /opt/azionelab/theme
|
|||||||
COPY mu-plugins/azionelab-content.php /opt/azionelab/azionelab-content.php
|
COPY mu-plugins/azionelab-content.php /opt/azionelab/azionelab-content.php
|
||||||
|
|
||||||
RUN sed -ri 's!^[[:space:]]*CustomLog .*!CustomLog /dev/null combined!' /etc/apache2/sites-available/000-default.conf \
|
RUN sed -ri 's!^[[:space:]]*CustomLog .*!CustomLog /dev/null combined!' /etc/apache2/sites-available/000-default.conf \
|
||||||
|
&& rm -rf /usr/src/wordpress/wp-content/plugins/akismet /usr/src/wordpress/wp-content/plugins/hello.php \
|
||||||
&& a2enconf azionelab-apache-hardening \
|
&& a2enconf azionelab-apache-hardening \
|
||||||
&& chmod 755 /usr/local/bin/azionelab-entrypoint \
|
&& chmod 755 /usr/local/bin/azionelab-entrypoint \
|
||||||
&& chmod 644 /usr/local/bin/azionelab-healthcheck.php \
|
&& chmod 644 /usr/local/bin/azionelab-healthcheck.php \
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ case "$environment" in
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
if [ "${1:-}" = "apache2-foreground" ]; then
|
if [ "${1:-}" = "apache2-foreground" ]; then
|
||||||
mkdir -p /var/www/html/wp-content/themes /var/www/html/wp-content/mu-plugins
|
mkdir -p /var/www/html/wp-content/themes /var/www/html/wp-content/mu-plugins /var/www/html/wp-content/plugins
|
||||||
|
rm -rf /var/www/html/wp-content/plugins/akismet /var/www/html/wp-content/plugins/hello.php
|
||||||
rm -rf /var/www/html/wp-content/themes/azionelab
|
rm -rf /var/www/html/wp-content/themes/azionelab
|
||||||
cp -a /opt/azionelab/theme /var/www/html/wp-content/themes/azionelab
|
cp -a /opt/azionelab/theme /var/www/html/wp-content/themes/azionelab
|
||||||
cp /opt/azionelab/azionelab-content.php /var/www/html/wp-content/mu-plugins/azionelab-content.php
|
cp /opt/azionelab/azionelab-content.php /var/www/html/wp-content/mu-plugins/azionelab-content.php
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd /var/www/html
|
||||||
|
|
||||||
|
trap 'exit 0' INT TERM
|
||||||
|
|
||||||
|
interval="${WP_CRON_INTERVAL_SECONDS:-300}"
|
||||||
|
case "$interval" in
|
||||||
|
"" | *[!0-9]*)
|
||||||
|
echo "WP_CRON_INTERVAL_SECONDS must be a positive integer." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$interval" -lt 60 ]; then
|
||||||
|
echo "WP_CRON_INTERVAL_SECONDS must be at least 60 seconds." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [ ! -f /var/www/html/wp-load.php ]; do
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
until wp core is-installed >/dev/null 2>&1; do
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
if ! wp cron event run --due-now >/dev/null 2>&1; then
|
||||||
|
echo "Warning: failed to run due WordPress cron events." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "$interval" &
|
||||||
|
wait "$!"
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user