mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-11-05 12:48:14 +00:00
Enhance Shopware role: fix init script permissions, CSP for data: fonts, and unify shell usage
- Added 'font-src data:' to CSP whitelist to allow inline fonts in Admin UI - Refactored init.sh to run as root only for volume permission setup, then drop privileges to www-data - Unified all bash invocations to sh for POSIX compliance - Added missing 'bundles' named volume and mount to Docker Compose - Set init container to run as root (0:0) for permission setup - Added admin user rename step via Ansible task See discussion: https://chatgpt.com/share/69087361-859c-800f-862c-7413350cca3e
This commit is contained in:
@@ -10,7 +10,10 @@ server:
|
||||
flags:
|
||||
script-src-elem:
|
||||
unsafe-inline: true
|
||||
whitelist: {}
|
||||
unsafe-eval: true
|
||||
whitelist:
|
||||
font-src:
|
||||
- "data:"
|
||||
domains:
|
||||
aliases: []
|
||||
canonical:
|
||||
|
||||
@@ -1,81 +1,140 @@
|
||||
#!/bin/sh
|
||||
# Shopware initialization script (POSIX sh)
|
||||
# - Root phase: fix volumes & permissions, then switch to www-data
|
||||
# - First run: perform system:install
|
||||
# - Every run: run DB migrations + rebuild cache + compile assets & themes
|
||||
# - Verifies admin bundles exist, otherwise exits with error
|
||||
|
||||
set -eu
|
||||
|
||||
# Paths / constants
|
||||
APP_ROOT="/var/www/html"
|
||||
MARKER="$APP_ROOT/.infinito/installed"
|
||||
LOG_PREFIX="[INIT]"
|
||||
PHP_BIN="php"
|
||||
|
||||
cd "$APP_ROOT"
|
||||
mkdir -p "$APP_ROOT/.infinito"
|
||||
log() { printf "%s %s\n" "$LOG_PREFIX" "$1"; }
|
||||
fail() { printf "%s [ERROR] %s\n" "$LOG_PREFIX" "$1" >&2; exit 1; }
|
||||
|
||||
echo "[INIT] Checking database via PDO..."
|
||||
php -r '
|
||||
# ---------------------------
|
||||
# 0) Root phase (if running as root)
|
||||
# ---------------------------
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
# Prepare required folders and shared volumes
|
||||
mkdir -p "$APP_ROOT/.infinito" \
|
||||
"$APP_ROOT/public/bundles" \
|
||||
"$APP_ROOT/public/media" \
|
||||
"$APP_ROOT/public/theme" \
|
||||
"$APP_ROOT/public/thumbnail" \
|
||||
"$APP_ROOT/public/sitemap" \
|
||||
"$APP_ROOT/var"
|
||||
|
||||
log "Fixing permissions on shared volumes..."
|
||||
chown -R www-data:www-data "$APP_ROOT/public" "$APP_ROOT/var" || true
|
||||
chmod -R 775 "$APP_ROOT/public" "$APP_ROOT/var" || true
|
||||
|
||||
# Switch to www-data for all subsequent operations
|
||||
exec su -s /bin/sh www-data "$0" "$@"
|
||||
fi
|
||||
|
||||
# From here on: running as www-data
|
||||
cd "$APP_ROOT" || fail "Cannot cd to $APP_ROOT"
|
||||
|
||||
# Optional environment hints
|
||||
APP_ENV_STR=$($PHP_BIN -r 'echo getenv("APP_ENV") ?: "";' 2>/dev/null || true)
|
||||
APP_URL_STR=$($PHP_BIN -r 'echo getenv("APP_URL") ?: "";' 2>/dev/null || true)
|
||||
[ -n "$APP_ENV_STR" ] || log "APP_ENV not set (using defaults)"
|
||||
[ -n "$APP_URL_STR" ] || log "APP_URL not set (reverse proxy must set headers)"
|
||||
|
||||
# ---------------------------
|
||||
# 1) Database reachability check (PDO)
|
||||
# ---------------------------
|
||||
log "Checking database via PDO..."
|
||||
$PHP_BIN -r '
|
||||
$url = getenv("DATABASE_URL");
|
||||
if (!$url) { fwrite(STDERR, "DATABASE_URL not set\n"); exit(1); }
|
||||
$p = parse_url($url);
|
||||
if (!$p || !isset($p["scheme"])) { fwrite(STDERR, "Invalid DATABASE_URL\n"); exit(1); }
|
||||
$scheme = $p["scheme"];
|
||||
if ($scheme === "mysql" || $scheme === "mariadb") {
|
||||
$host = $p["host"] ?? "localhost";
|
||||
$port = $p["port"] ?? 3306;
|
||||
$db = ltrim($p["path"] ?? "", "/");
|
||||
$user = $p["user"] ?? "";
|
||||
$pass = $p["pass"] ?? "";
|
||||
$dsn = "mysql:host=".$host.";port=".$port.";dbname=".$db.";charset=utf8mb4";
|
||||
} else {
|
||||
fwrite(STDERR, "Unsupported DB scheme: ".$scheme."\n"); exit(1);
|
||||
}
|
||||
$host = $p["host"] ?? "localhost";
|
||||
$port = $p["port"] ?? 3306;
|
||||
$db = ltrim($p["path"] ?? "", "/");
|
||||
$user = $p["user"] ?? "";
|
||||
$pass = $p["pass"] ?? "";
|
||||
$dsn = "mysql:host=".$host.";port=".$port.";dbname=".$db.";charset=utf8mb4";
|
||||
$retries = 60;
|
||||
while ($retries-- > 0) {
|
||||
try { $pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_TIMEOUT => 3]); exit(0); }
|
||||
try { new PDO($dsn, $user, $pass, [PDO::ATTR_TIMEOUT => 3]); exit(0); }
|
||||
catch (Exception $e) { sleep(2); }
|
||||
}
|
||||
fwrite(STDERR, "DB not reachable\n"); exit(1);
|
||||
'
|
||||
' || fail "Database not reachable"
|
||||
|
||||
# ---------------------------
|
||||
# 2) First-time install detection
|
||||
# ---------------------------
|
||||
FIRST_INSTALL=0
|
||||
if [ ! -f "$MARKER" ]; then
|
||||
echo "[INIT] Checking if database is empty..."
|
||||
# PHP exits: 0 = empty, 100 = non-empty, 1 = error
|
||||
if php -r '
|
||||
log "Checking if database is empty..."
|
||||
if $PHP_BIN -r '
|
||||
$url = getenv("DATABASE_URL");
|
||||
$p = parse_url($url);
|
||||
$db = ltrim($p["path"] ?? "", "/");
|
||||
$dsn = "mysql:host=".($p["host"]??"localhost").";port=".($p["port"]??3306).";dbname=".$db.";charset=utf8mb4";
|
||||
try {
|
||||
$pdo = new PDO($dsn, $p["user"] ?? "", $p["pass"] ?? "");
|
||||
$q = $pdo->query("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=".$pdo->quote($db));
|
||||
$cnt = (int)$q->fetchColumn();
|
||||
if ($cnt === 0) { exit(0); } else { exit(100); }
|
||||
} catch (Exception $e) { fwrite(STDERR, $e->getMessage()."\n"); exit(1); }
|
||||
$pdo = new PDO($dsn, $p["user"] ?? "", $p["pass"] ?? "");
|
||||
$q = $pdo->query("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=".$pdo->quote($db));
|
||||
$cnt = (int)$q->fetchColumn();
|
||||
exit($cnt === 0 ? 0 : 100);
|
||||
'; then
|
||||
DBCHK=0
|
||||
FIRST_INSTALL=1
|
||||
else
|
||||
DBCHK=$?
|
||||
ST=$?
|
||||
if [ "$ST" -eq 100 ]; then
|
||||
log "Database not empty → skipping install"
|
||||
else
|
||||
fail "Database check failed (exit code $ST)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$DBCHK" -eq 0 ]; then
|
||||
echo "[INIT] Installing Shopware (empty DB detected)..."
|
||||
# IMPORTANT: no --force; let Shopware run its internal steps only on empty DB
|
||||
php -d memory_limit=1024M bin/console system:install --basic-setup --create-database
|
||||
elif [ "$DBCHK" -eq 100 ]; then
|
||||
echo "[INIT] Database is not empty -> skipping system:install"
|
||||
else
|
||||
echo "[INIT] Database check failed (code $DBCHK)"; exit 1
|
||||
fi
|
||||
|
||||
# Safe to run (no-ops when up-to-date)
|
||||
php -d memory_limit=1024M bin/console database:migrate --all || true
|
||||
php -d memory_limit=1024M bin/console database:migrate-destructive --all || true
|
||||
|
||||
# Housekeeping
|
||||
php bin/console cache:clear || true
|
||||
php bin/console dal:refresh:index || true
|
||||
|
||||
# Marker + perms
|
||||
touch "$MARKER"
|
||||
chown -R www-data:www-data "$APP_ROOT"
|
||||
|
||||
echo "[INIT] Done."
|
||||
else
|
||||
echo "[INIT] Marker found, skipping install."
|
||||
fi
|
||||
|
||||
if [ "$FIRST_INSTALL" -eq 1 ]; then
|
||||
log "Performing first-time Shopware installation..."
|
||||
$PHP_BIN -d memory_limit=1024M bin/console system:install --basic-setup --create-database
|
||||
mkdir -p "$(dirname "$MARKER")"
|
||||
: > "$MARKER"
|
||||
fi
|
||||
|
||||
# ---------------------------
|
||||
# 3) Always run migrations
|
||||
# ---------------------------
|
||||
log "Running database migrations..."
|
||||
$PHP_BIN -d memory_limit=1024M bin/console database:migrate --all
|
||||
$PHP_BIN -d memory_limit=1024M bin/console database:migrate-destructive --all
|
||||
|
||||
# ---------------------------
|
||||
# 4) Always rebuild caches, bundles, and themes
|
||||
# ---------------------------
|
||||
log "Rebuilding caches and assets..."
|
||||
$PHP_BIN bin/console cache:clear
|
||||
$PHP_BIN bin/console bundle:dump
|
||||
# Use --copy if symlinks cause issues
|
||||
$PHP_BIN bin/console assets:install --no-interaction --force
|
||||
$PHP_BIN bin/console theme:refresh
|
||||
$PHP_BIN bin/console theme:compile
|
||||
# Best-effort: not critical if it fails
|
||||
$PHP_BIN bin/console dal:refresh:index || log "dal:refresh:index failed (non-critical)"
|
||||
|
||||
# ---------------------------
|
||||
# 5) Verify admin bundles
|
||||
# ---------------------------
|
||||
if [ ! -d "public/bundles/administration" ]; then
|
||||
fail "Missing directory public/bundles/administration (asset build failed)"
|
||||
fi
|
||||
if ! ls public/bundles/administration/* >/dev/null 2>&1; then
|
||||
fail "No files found in public/bundles/administration (asset build failed)"
|
||||
fi
|
||||
|
||||
# ---------------------------
|
||||
# 6) Show version info
|
||||
# ---------------------------
|
||||
$PHP_BIN bin/console system:version 2>/dev/null || log "system:version not available"
|
||||
|
||||
log "Initialization complete."
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
# Ensures that the admin user exists and always has the desired password
|
||||
- name: "Rename default Shopware admin user to {{ users.administrator.username }}"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
old_user="admin"
|
||||
new_user="{{ users.administrator.username }}"
|
||||
if php bin/console user:list | grep -q "^$old_user "; then
|
||||
echo "[INFO] Renaming Shopware user: $old_user -> $new_user"
|
||||
php bin/console user:update "$old_user" --username="$new_user" || true
|
||||
else
|
||||
echo "[INFO] No user named $old_user found (already renamed or custom setup)"
|
||||
fi
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
changed_when: false
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
|
||||
- name: "Ensure Shopware admin exists and has the desired password"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
@@ -17,3 +35,4 @@
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||
@@ -1,6 +1,6 @@
|
||||
- name: "Deactivate/uninstall LDAP plugin if present"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:deactivate INFX_LDAP_PLUGIN || true
|
||||
php bin/console plugin:uninstall INFX_LDAP_PLUGIN --keep-user-data || true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- name: "Deactivate/uninstall OIDC plugin if present"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:deactivate INFX_OIDC_PLUGIN || true
|
||||
php bin/console plugin:uninstall INFX_OIDC_PLUGIN --keep-user-data || true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Replace INFX_LDAP_PLUGIN with the actual plugin name you use
|
||||
- name: "Install LDAP admin plugin & activate"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:refresh
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
- name: "Configure LDAP connection"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console system:config:set "InfxLdap.config.host" "{{ LDAP.SERVER.DOMAIN }}"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Replace INFX_OIDC_PLUGIN with the actual plugin name (Composer or local)
|
||||
- name: "Install OIDC plugin & activate"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:refresh
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
- name: "Configure OIDC via system:config"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console system:config:set "InfxOidc.config.clientId" "{{ OIDC.CLIENT.ID }}"
|
||||
|
||||
@@ -7,6 +7,7 @@ x-environment: &shopware
|
||||
- thumbnail:/var/www/html/public/thumbnail
|
||||
- sitemap:/var/www/html/public/sitemap
|
||||
- "{{ SHOPWARE_INIT_HOST }}:{{ SHOPWARE_INIT_DOCKER }}:ro"
|
||||
- bundles:/var/www/html/public/bundles
|
||||
working_dir: {{ SHOPWARE_ROOT }}
|
||||
|
||||
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
||||
@@ -22,6 +23,7 @@ x-environment: &shopware
|
||||
<<: *shopware
|
||||
container_name: "{{ SHOPWARE_INIT_CONTAINER }}"
|
||||
entrypoint: [ "sh", "{{ SHOPWARE_INIT_DOCKER }}" ]
|
||||
user: "0:0"
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
|
||||
@@ -113,5 +115,7 @@ x-environment: &shopware
|
||||
name: {{ entity_name }}_thumbnail
|
||||
sitemap:
|
||||
name: {{ entity_name }}_sitemap
|
||||
bundles:
|
||||
name: {{ entity_name }}_bundles
|
||||
|
||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||
|
||||
Reference in New Issue
Block a user