Remove staging basic-auth wall — box is LAN-only

Staging runs on a private-network VM, not reachable from outside, so the
HTTP basic-auth layer was unnecessary defense-in-depth. Removing it also
lets the Caddyfile drop the render-around-Compose workaround entirely
(that workaround existed specifically because Compose's interpolation
mangles a bcrypt hash) — the noindex header and blog_public=0 stay, since
those guard against search-engine indexing, a separate concern from
network-level access.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 11:23:35 -04:00
co-authored by Claude Sonnet 5
parent b43852e733
commit cd48f85a47
6 changed files with 11 additions and 47 deletions
+1 -9
View File
@@ -7,9 +7,7 @@
# compose passes these into containers via ${VAR} interpolation, which will # compose passes these into containers via ${VAR} interpolation, which will
# silently mangle a value containing `$` followed by a letter (it tries to # silently mangle a value containing `$` followed by a letter (it tries to
# resolve it as another variable and blanks it out if unset). Avoid `$` in # resolve it as another variable and blanks it out if unset). Avoid `$` in
# these specific values, or double it ($$) if you must use one. This does # these specific values, or double it ($$) if you must use one.
# NOT apply to STAGING_BASIC_AUTH_HASH below — that one's wired through
# env_file instead specifically so a bcrypt hash's `$` signs are safe.
# --- Site --- # --- Site ---
# (WP_ENVIRONMENT_TYPE is NOT set here — it's hardcoded per environment in # (WP_ENVIRONMENT_TYPE is NOT set here — it's hardcoded per environment in
@@ -30,12 +28,6 @@ DB_PASSWORD=changeme
DB_ROOT_PASSWORD=changeme DB_ROOT_PASSWORD=changeme
WP_TABLE_PREFIX=wp_ WP_TABLE_PREFIX=wp_
# --- Staging only: basic-auth wall + noindex ---
# Generate the hash with:
# docker run --rm caddy:2-alpine caddy hash-password --plaintext 'your-password'
STAGING_BASIC_AUTH_USER=staging
STAGING_BASIC_AUTH_HASH=
# --- Backups (deploy/backup.sh) --- # --- Backups (deploy/backup.sh) ---
BACKUP_DIR=./backups BACKUP_DIR=./backups
BACKUP_REMOTE= BACKUP_REMOTE=
-1
View File
@@ -3,7 +3,6 @@
!/.env.example !/.env.example
/backups/ /backups/
/docker/caddy/.generated/
vendor/ vendor/
node_modules/ node_modules/
*.log *.log
+7 -7
View File
@@ -44,17 +44,17 @@ drops into the app container.
``` ```
cp .env.example .env.staging cp .env.example .env.staging
# fill in SITE_DOMAIN, SITE_URL, DB_*, and: # fill in SITE_DOMAIN, SITE_URL, DB_*
docker run --rm caddy:2-alpine caddy hash-password --plaintext 'pick-a-password'
# -> paste result into STAGING_BASIC_AUTH_HASH
make deploy ENV=staging make deploy ENV=staging
``` ```
Staging is `noindex`'d and sits behind HTTP basic auth (Caddyfile.staging) Staging is `noindex`'d (`blog_public=0` plus the `X-Robots-Tag` header in
in addition to `blog_public=0` — two independent reasons search engines and Caddyfile.staging) so search engines won't index it — there's no basic-auth
random visitors won't see it, per the launch gate. Mail never leaves the wall on top of that, since this box is LAN-only and not reachable from
box: it's caught by MailHog, viewable at `:8025`. outside. If that ever changes (a public domain, port-forwarding, etc.),
basic-auth is worth adding back before that happens, not after. Mail never
leaves the box: it's caught by MailHog, viewable at `:8025`.
### Theme: Blocksy + the Book Store starter site ### Theme: Blocksy + the Book Store starter site
-13
View File
@@ -32,19 +32,6 @@ WP_ADMIN_EMAIL="$(env_get "$ENV_FILE" WP_ADMIN_EMAIL)"
# reuse dev's db_data volume/credentials the first time this ran). # reuse dev's db_data volume/credentials the first time this ran).
COMPOSE="docker compose -p bookstore-${ENVIRONMENT} -f docker-compose.yml -f docker-compose.${ENVIRONMENT}.yml --env-file ${ENV_FILE}" COMPOSE="docker compose -p bookstore-${ENVIRONMENT} -f docker-compose.yml -f docker-compose.${ENVIRONMENT}.yml --env-file ${ENV_FILE}"
if [[ "$ENVIRONMENT" == "staging" ]]; then
echo "==> rendering Caddyfile.staging (basic-auth hash bypasses compose entirely)"
mkdir -p docker/caddy/.generated
AUTH_USER="$(env_get "$ENV_FILE" STAGING_BASIC_AUTH_USER)"
AUTH_HASH="$(env_get "$ENV_FILE" STAGING_BASIC_AUTH_HASH)"
: "${AUTH_USER:?set STAGING_BASIC_AUTH_USER in ${ENV_FILE}}"
: "${AUTH_HASH:?set STAGING_BASIC_AUTH_HASH in ${ENV_FILE} — see .env.example for how to generate it}"
TEMPLATE="$(cat docker/caddy/Caddyfile.staging)"
TEMPLATE="${TEMPLATE//__STAGING_BASIC_AUTH_USER__/$AUTH_USER}"
TEMPLATE="${TEMPLATE//__STAGING_BASIC_AUTH_HASH__/$AUTH_HASH}"
printf '%s\n' "$TEMPLATE" > docker/caddy/.generated/Caddyfile.staging
fi
echo "==> building and starting ${ENVIRONMENT}" echo "==> building and starting ${ENVIRONMENT}"
$COMPOSE up -d --build $COMPOSE up -d --build
+1 -5
View File
@@ -16,14 +16,10 @@ services:
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"
# SITE_DOMAIN is safe to pass through normally (no `$` in a domain name).
# STAGING_BASIC_AUTH_USER/HASH are NOT passed via compose at all — see
# the comment in docker/caddy/Caddyfile.staging for why; deploy.sh
# renders them directly into the mounted file below instead.
env_file: env_file:
- .env.staging - .env.staging
volumes: volumes:
- ./docker/caddy/.generated/Caddyfile.staging:/etc/caddy/Caddyfile:ro - ./docker/caddy/Caddyfile.staging:/etc/caddy/Caddyfile:ro
- wp_core:/var/www/html:ro - wp_core:/var/www/html:ro
- wp_uploads:/var/www/html/wp-content/uploads:ro - wp_uploads:/var/www/html/wp-content/uploads:ro
- wp_themes:/var/www/html/wp-content/themes:ro - wp_themes:/var/www/html/wp-content/themes:ro
+2 -12
View File
@@ -2,18 +2,8 @@
encode gzip encode gzip
# Launch gate: "No staging URLs are publicly indexed." Belt-and-suspenders # Launch gate: "No staging URLs are publicly indexed." Belt-and-suspenders
# with wp_option blog_public=0, which deploy.sh sets on staging. # with wp_option blog_public=0, which deploy.sh sets on staging. No
# # basic-auth wall — this box is LAN-only, not reachable from outside.
# The two tokens below are substituted by deploy.sh directly (bash string
# replacement), not by Caddy's {$VAR} or docker compose's ${VAR} — a
# bcrypt hash contains `$identifier`-looking substrings that Compose's
# own interpolation will silently corrupt if this value ever passes
# through it (confirmed: both `environment:` and `env_file:` are
# affected). This file is a tracked template; deploy.sh renders it into
# docker/caddy/.generated/Caddyfile.staging, which is what's mounted.
basic_auth {
__STAGING_BASIC_AUTH_USER__ __STAGING_BASIC_AUTH_HASH__
}
header X-Robots-Tag "noindex, nofollow, noarchive" header X-Robots-Tag "noindex, nofollow, noarchive"
root * /var/www/html root * /var/www/html