Files
Thanakorn ae98dcdcdd Harden web root, secrets and realtime auth
- .htaccess: refuse .git, .env, docker, nodejs, sdlc*, scripts, archives and
  app internals; security headers, HSTS over TLS, optional HTTPS redirect
- uploads served through app/file.php to signed-in users only
- Apache/PHP hardening config for the container (ServerTokens, expose_php)
- least-privilege DB account and APP_SECRET_KEY via docker/php/provision.php;
  SMTP passwords re-encrypted with a random IV (secret_box.php)
- Socket.IO rooms from a PHP-signed token; /emit and cron refuse empty secrets
- escape notification text; CLI guards on build scripts; no fixed demo password
2026-09-24 14:53:40 +07:00

75 lines
3.1 KiB
Bash

#!/bin/sh
set -e
APP_DIR=/var/www/html/wms-app
CONFIG=$APP_DIR/app/config.php
# Email OTP on sign-in, OFF BY DEFAULT. Only the exact string "true" turns it
# on; a missing variable or anything else means false.
: "${OTP_REQUIRED:=false}"
[ "$OTP_REQUIRED" = "true" ] || OTP_REQUIRED=false
export OTP_REQUIRED
# An empty EMIT_SECRET would let anyone call Node's /emit and the PHP cron
# endpoints, so refuse to start without one.
if [ -z "$EMIT_SECRET" ]; then
echo "[entrypoint] ERROR: EMIT_SECRET is empty. Set it in .env (docker/init-env.sh generates one)." >&2
exit 1
fi
if [ -z "$APP_SECRET_KEY" ]; then
echo "[entrypoint] WARNING: APP_SECRET_KEY is not set; SMTP passwords stay in the legacy fixed-key format."
fi
# The app connects as a least-privilege account (see provision.php). Without
# DB_APP_PASSWORD it keeps connecting as root, as before.
: "${DB_APP_USER:=wms_app}"
if [ -z "$DB_APP_PASSWORD" ]; then
echo "[entrypoint] WARNING: DB_APP_PASSWORD is not set; the app connects as root. Re-run docker/init-env.sh to add it."
DB_APP_USER=root
DB_APP_PASSWORD=$DB_ROOT_PASSWORD
fi
export DB_APP_USER DB_APP_PASSWORD APP_SECRET_KEY CONFIG
# Generate app/config.php from template on first run only.
# Restrict envsubst to known placeholders so it never touches the app's own
# $variable syntax (envsubst blanks out any $NAME it doesn't recognize).
if [ ! -f "$CONFIG" ]; then
echo "[entrypoint] generating app/config.php"
envsubst '${DB_APP_USER} ${DB_APP_PASSWORD} ${PUBLIC_HOST} ${EMIT_SECRET} ${SMTP_USERNAME} ${SMTP_PASSWORD} ${OTP_REQUIRED} ${APP_SECRET_KEY}' \
< /usr/local/etc/wms/config.php.template > "$CONFIG"
fi
# config.php is never regenerated once it exists, so OTP_REQUIRED is the one
# line reconciled on every start: the .env value always wins, and a config.php
# written before this switch existed gets the line added.
if grep -q "define('OTP_REQUIRED'" "$CONFIG"; then
if ! grep -q "define('OTP_REQUIRED', ${OTP_REQUIRED});" "$CONFIG"; then
sed -i "s/define('OTP_REQUIRED', [A-Za-z]*);/define('OTP_REQUIRED', ${OTP_REQUIRED});/" "$CONFIG"
echo "[entrypoint] OTP_REQUIRED is now ${OTP_REQUIRED}"
fi
else
# Drop a closing ?> on the last line so the appended block stays inside PHP.
sed -i -e '${/^[[:space:]]*?>[[:space:]]*$/d}' "$CONFIG"
printf "\nif (!defined('OTP_REQUIRED')) {\n\tdefine('OTP_REQUIRED', %s);\n}\n" "$OTP_REQUIRED" >> "$CONFIG"
echo "[entrypoint] added OTP_REQUIRED = ${OTP_REQUIRED} to an existing config.php"
fi
if [ "$OTP_REQUIRED" = "false" ]; then
echo "[entrypoint] email OTP is off (OTP_REQUIRED=false); sign-in is password only."
fi
mkdir -p "$APP_DIR/app/uploads"
chown -R www-data:www-data "$APP_DIR/app/uploads"
echo "[entrypoint] waiting for database at db:3306"
until mysqladmin ping -h db -u root -p"$DB_ROOT_PASSWORD" --silent 2>/dev/null; do
sleep 2
done
php /usr/local/etc/wms/provision.php
# setup.php creates databases and tables, so it runs as root, not the app account.
echo "[entrypoint] running setup.php (idempotent schema sync)"
DB_SETUP_USER=root DB_SETUP_PASSWORD="$DB_ROOT_PASSWORD" php "$APP_DIR/setup.php" || true
exec "$@"