Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .docker/wordpress/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ is_truthy() {
esac
}

is_local_environment() {
case "${WP_ENVIRONMENT_TYPE:-}" in
local|development)
return 0
;;
*)
return 1
;;
esac
}

plugins_config_exists() {
[ -n "$(trim_value "${WORDPRESS_SETUP_CONFIG_YAML:-}")" ]
}
Expand Down Expand Up @@ -127,6 +138,11 @@ sync_site_urls() {
return
fi

if ! is_local_environment; then
echo "ERROR: PROD_SITE_URL is set but WP_ENVIRONMENT_TYPE is not local or development; skipping URL synchronization." >&2
return
fi

prod_url="$(normalize_url "${PROD_SITE_URL}")"
local_url="$(normalize_url "${LOCAL_SITE_URL:-http://localhost}")"
prod_host="$(strip_protocol "${prod_url}")"
Expand All @@ -151,6 +167,11 @@ reset_local_user_passwords() {
return
fi

if ! is_local_environment; then
echo "ERROR: WORDPRESS_LOCAL_USERS_PASSWORD is set but WP_ENVIRONMENT_TYPE is not local or development; skipping password reset." >&2
return
fi

if is_truthy "$reset_all_users"; then
echo "Resetting password for all local WordPress users..."
while IFS= read -r user_id; do
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ services:
wordpress:
environment:
XDEBUG_MODE: ${XDEBUG_MODE:-debug}
# PROD_SITE_URL and the password reset below only run when this is local or development.
WP_ENVIRONMENT_TYPE: local
# Production base URL used for automatic search-replace in the database.
# Set this only when importing a production dump and needing URL remap.
PROD_SITE_URL: ${PROD_SITE_URL:-}
Expand Down Expand Up @@ -104,7 +106,7 @@ After that, start the stack with the standard Compose command:
docker compose up -d --build
```

On each startup (when WordPress is already installed), the entrypoint can automatically reset local user passwords:
On each startup (when WordPress is already installed and `WP_ENVIRONMENT_TYPE` is `local` or `development`), the entrypoint can automatically reset local user passwords:
- `WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS=1`: reset all users to `WORDPRESS_LOCAL_USERS_PASSWORD`.
- `WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER=<login|email|id>`: reset only one user.

Expand Down
1 change: 1 addition & 0 deletions common-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
- WORDPRESS_DB_PASSWORD=${WORDPRESS_DB_PASSWORD:-root}
- WORDPRESS_TABLE_PREFIX=${WORDPRESS_TABLE_PREFIX:-wp_}
- WORDPRESS_SETUP_CONFIG_YAML=${WORDPRESS_SETUP_CONFIG_YAML:-}
- WP_ENVIRONMENT_TYPE
- WORDPRESS_LOCAL_USERS_PASSWORD=${WORDPRESS_LOCAL_USERS_PASSWORD:-}
- WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS=${WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS:-0}
- WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER=${WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER:-}
Expand Down
67 changes: 67 additions & 0 deletions tests/security/helpers/entrypoint.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash

entrypoint_root="${BATS_TEST_DIRNAME}/../.."
entrypoint_tmp="${BATS_TEST_TMPDIR}/entrypoint"
entrypoint_network=""
entrypoint_database_container=""

entrypoint_setup() {
entrypoint_network="entrypoint-bats-${BATS_TEST_NUMBER:-0}-$$"
mkdir -p "${entrypoint_tmp}/document-root" "${entrypoint_tmp}/bin" "${entrypoint_tmp}/log"
touch "${entrypoint_tmp}/document-root/wp-load.php" "${entrypoint_tmp}/document-root/wp-config.php"
chmod 777 "${entrypoint_tmp}/log"

cat > "${entrypoint_tmp}/bin/wp" <<'WP_STUB'
#!/bin/sh
echo "wp $*" >> /var/log/entrypoint/wp-calls
case "$1 $2" in
"user list") echo 1 ;;
"user get") echo 1 ;;
esac
exit 0
WP_STUB
printf '%s\n' '#!/bin/sh' 'exit 0' > "${entrypoint_tmp}/bin/php-fpm"
chmod 755 "${entrypoint_tmp}/bin/wp" "${entrypoint_tmp}/bin/php-fpm"

printf '%s\n' 'server { listen 3306; return 444; }' > "${entrypoint_tmp}/database.conf"
docker network create "${entrypoint_network}" >/dev/null
entrypoint_database_container="$(docker run -d --rm \
--network "${entrypoint_network}" --network-alias mariadb \
-v "${entrypoint_tmp}/database.conf:/etc/nginx/conf.d/default.conf:ro" \
nginx:latest)"
}

entrypoint_teardown() {
if [ -n "${entrypoint_database_container}" ]; then
docker rm -f "${entrypoint_database_container}" >/dev/null 2>&1 || true
entrypoint_database_container=""
fi
if [ -n "${entrypoint_network}" ]; then
docker network rm "${entrypoint_network}" >/dev/null 2>&1 || true
entrypoint_network=""
fi
}

entrypoint_run() {
local -a environment_args=()
local variable

for variable in "$@"; do
environment_args+=(-e "${variable}")
done

docker run --rm \
--network "${entrypoint_network}" \
-e "HOST_UID=$(id -u)" -e "HOST_GID=$(id -g)" \
"${environment_args[@]}" \
-v "${entrypoint_root}/.docker/wordpress/entrypoint.sh:/var/www/scripts/entrypoint.sh:ro" \
-v "${entrypoint_tmp}/bin/wp:/usr/local/bin/wp:ro" \
-v "${entrypoint_tmp}/bin/php-fpm:/usr/local/sbin/php-fpm:ro" \
-v "${entrypoint_tmp}/log:/var/log/entrypoint" \
-v "${entrypoint_tmp}/document-root:/var/www/html" \
php:8.3-fpm bash /var/www/scripts/entrypoint.sh
}

entrypoint_wp_calls() {
cat "${entrypoint_tmp}/log/wp-calls" 2>/dev/null || true
}
43 changes: 43 additions & 0 deletions tests/security/local-only-setup.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bats

load 'helpers/entrypoint.bash'

setup() {
entrypoint_setup
}

teardown() {
entrypoint_teardown
}

@test "password reset is skipped when the environment type is not set" {
run entrypoint_run WORDPRESS_LOCAL_USERS_PASSWORD=admin WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS=1
[ "${status}" -eq 0 ]
[[ "${output}" == *"ERROR: WORDPRESS_LOCAL_USERS_PASSWORD"* ]]
[[ "$(entrypoint_wp_calls)" != *"user update"* ]]
}

@test "password reset is skipped in production" {
run entrypoint_run WP_ENVIRONMENT_TYPE=production WORDPRESS_LOCAL_USERS_PASSWORD=admin WORDPRESS_LOCAL_RESET_PASSWORD_FOR_USER=admin
[ "${status}" -eq 0 ]
[[ "$(entrypoint_wp_calls)" != *"user update"* ]]
}

@test "password reset runs in a local environment" {
run entrypoint_run WP_ENVIRONMENT_TYPE=local WORDPRESS_LOCAL_USERS_PASSWORD=admin WORDPRESS_LOCAL_RESET_ALL_USERS_PASSWORDS=1
[ "${status}" -eq 0 ]
[[ "$(entrypoint_wp_calls)" == *"user update 1 --user_pass=admin"* ]]
}

@test "URL synchronization is skipped outside local environments" {
run entrypoint_run WP_ENVIRONMENT_TYPE=staging PROD_SITE_URL=https://example.com
[ "${status}" -eq 0 ]
[[ "${output}" == *"ERROR: PROD_SITE_URL"* ]]
[[ "$(entrypoint_wp_calls)" != *"search-replace"* ]]
}

@test "URL synchronization runs in a development environment" {
run entrypoint_run WP_ENVIRONMENT_TYPE=development PROD_SITE_URL=https://example.com
[ "${status}" -eq 0 ]
[[ "$(entrypoint_wp_calls)" == *"search-replace https://example.com http://localhost"* ]]
}
Loading