From eb884faea18662f5030c572b5b60d228777fce93 Mon Sep 17 00:00:00 2001 From: Serreau Jovann Date: Fri, 10 Apr 2026 14:04:14 +0200 Subject: [PATCH] Align prod PHP container UID with bot user (1001) and harden restore.sh Sur le serveur prod, l'utilisateur 'bot' (cree par Ansible) a UID 1001 alors que l'utilisateur 'appuser' du conteneur PHP etait hardcode a UID 1000, ce qui causait des erreurs 'Unable to write in var/cache/prod' apres restore ou deploiement: les fichiers du host appartenaient a bot:bot (1001) et appuser (1000) ne pouvait pas y ecrire. docker/php/prod/Dockerfile: - appuser UID/GID passe de 1000 a 1001 pour matcher bot - dev/Dockerfile reste a 1000 (les users dev sont generalement 1000) ansible/deploy.yml: - chown des dossiers public/uploads/* et var/payouts passe de 1000 a 1001 restore.sh: - Nouvelle option --owner USER:GRP (defaut: bot:bot, override via RESTORE_OWNER) - Chown automatique en fin de restore sur var/, public/uploads/, config/cert/, .env.local - Purge automatique de var/cache (sera reconstruit par PHP) - Verification root au demarrage (sudo requis pour chown) - Verification de l'existence du user et group cibles - Option --skip-chown pour bypass (deconseille) - Etapes post-migration mises a jour (ajout de make build_prod en premier) Procedure de migration sur le serveur apres ce commit: cd /var/www/e-ticket git pull origin master make build_prod # rebuild image PHP avec UID 1001 make stop_prod sudo chown -R bot:bot var public/uploads config/cert .env.local sudo rm -rf var/cache make start_prod make clear_prod Co-Authored-By: Claude Opus 4.6 (1M context) --- ansible/deploy.yml | 8 ++-- docker/php/prod/Dockerfile | 2 +- restore.sh | 93 ++++++++++++++++++++++++++++++++++---- 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 7675767..51d9e46 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -157,8 +157,8 @@ file: path: "/var/www/e-ticket/public/uploads/{{ item }}" state: directory - owner: "1000" - group: "1000" + owner: "1001" + group: "1001" mode: "0755" recurse: true loop: @@ -168,8 +168,8 @@ file: path: /var/www/e-ticket/var/payouts state: directory - owner: "1000" - group: "1000" + owner: "1001" + group: "1001" mode: "0755" - name: Ensure Caddy sites directory exists diff --git a/docker/php/prod/Dockerfile b/docker/php/prod/Dockerfile index 13825de..9f57ab0 100644 --- a/docker/php/prod/Dockerfile +++ b/docker/php/prod/Dockerfile @@ -24,7 +24,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ opcache \ && pecl install redis imagick \ && docker-php-ext-enable redis imagick \ - && groupadd -g 1000 appuser && useradd -u 1000 -g appuser -m appuser + && groupadd -g 1001 appuser && useradd -u 1001 -g appuser -m appuser COPY php.ini /usr/local/etc/php/conf.d/app.ini COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini diff --git a/restore.sh b/restore.sh index f00274d..ea40d3c 100755 --- a/restore.sh +++ b/restore.sh @@ -5,14 +5,19 @@ # Le bundle est attendu au format e_ticket_migration_.tar.gz et contient: # db.sql.gz, public_uploads.tar.gz, var_state.tar.gz, env.local, cert/, manifest.txt # +# A la fin, le script chown automatiquement les fichiers restaures vers RESTORE_OWNER +# (defaut: bot) afin que l'utilisateur PHP du conteneur puisse y ecrire, et purge +# var/cache pour eviter les fichiers obsoletes appartenant a un autre utilisateur. +# # Usage: -# ./restore.sh [--yes] [--skip-db] [--skip-uploads] [--skip-var] [--skip-secrets] +# ./restore.sh [options] # # Variables d'environnement supportees: -# COMPOSE_FILE : fichier docker-compose a utiliser (defaut: docker-compose-prod.yml) -# DB_SERVICE : service Docker de la base (defaut: db-master) -# DB_USER : utilisateur PostgreSQL (defaut: e-ticket) -# DB_NAME : base de donnees a restaurer (defaut: e-ticket) +# COMPOSE_FILE : fichier docker-compose a utiliser (defaut: docker-compose-prod.yml) +# DB_SERVICE : service Docker de la base (defaut: db-master) +# DB_USER : utilisateur PostgreSQL (defaut: e-ticket) +# DB_NAME : base de donnees a restaurer (defaut: e-ticket) +# RESTORE_OWNER : user:group pour le chown final (defaut: bot:bot) set -euo pipefail @@ -21,6 +26,7 @@ COMPOSE_FILE="${COMPOSE_FILE:-${SCRIPT_DIR}/docker-compose-prod.yml}" DB_SERVICE="${DB_SERVICE:-db-master}" DB_USER="${DB_USER:-e-ticket}" DB_NAME="${DB_NAME:-e-ticket}" +RESTORE_OWNER="${RESTORE_OWNER:-bot:bot}" BUNDLE="" ASSUME_YES="false" @@ -28,6 +34,7 @@ SKIP_DB="false" SKIP_UPLOADS="false" SKIP_VAR="false" SKIP_SECRETS="false" +SKIP_CHOWN="false" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" @@ -40,7 +47,7 @@ err() { usage() { cat <<'EOF' Usage: - ./restore.sh [options] + sudo ./restore.sh [options] Options: --yes, -y Mode non interactif (pas de confirmation) @@ -48,9 +55,15 @@ Options: --skip-uploads Ne pas restaurer public/uploads --skip-var Ne pas restaurer var/ (payouts, billets, invoices, unsubscribed.json) --skip-secrets Ne pas restaurer .env.local et config/cert/ + --skip-chown Ne pas faire le chown final (deconseille) + --owner USER:GRP Utilisateur:groupe pour le chown final (defaut: bot:bot) -h, --help Affiche cette aide +Variables d'environnement (alternatives aux flags): + RESTORE_OWNER Equivalent de --owner + ATTENTION: La restauration ECRASE les donnees existantes ! +ATTENTION: Le chown final necessite root (sudo). EOF } @@ -62,6 +75,15 @@ while [ $# -gt 0 ]; do --skip-uploads) SKIP_UPLOADS="true"; shift ;; --skip-var) SKIP_VAR="true"; shift ;; --skip-secrets) SKIP_SECRETS="true"; shift ;; + --skip-chown) SKIP_CHOWN="true"; shift ;; + --owner) + if [ $# -lt 2 ]; then + err "--owner requiert une valeur (ex: --owner bot:bot)" + exit 1 + fi + RESTORE_OWNER="$2" + shift 2 + ;; -h|--help) usage; exit 0 ;; --*) err "Option inconnue: $1"; usage; exit 1 ;; *) @@ -77,6 +99,27 @@ while [ $# -gt 0 ]; do esac done +# --- Verification root pour le chown final --- +if [ "${SKIP_CHOWN}" = "false" ] && [ "$(id -u)" != "0" ]; then + err "Ce script doit etre execute en root (sudo) pour le chown final vers ${RESTORE_OWNER}" + err "Soit relancer avec sudo, soit ajouter --skip-chown (deconseille)" + exit 1 +fi + +# --- Verification que le user/group cible existe --- +if [ "${SKIP_CHOWN}" = "false" ]; then + OWNER_USER="${RESTORE_OWNER%%:*}" + OWNER_GROUP="${RESTORE_OWNER##*:}" + if ! id -u "${OWNER_USER}" >/dev/null 2>&1; then + err "Utilisateur '${OWNER_USER}' introuvable sur le systeme" + exit 1 + fi + if ! getent group "${OWNER_GROUP}" >/dev/null 2>&1; then + err "Groupe '${OWNER_GROUP}' introuvable sur le systeme" + exit 1 + fi +fi + if [ -z "${BUNDLE}" ]; then err "Aucun bundle specifie" usage @@ -245,10 +288,40 @@ if [ "${HAS_VAR}" = "true" ] && [ "${SKIP_VAR}" = "false" ]; then log " OK" fi +# --- 5. Purge du cache Symfony --- +# Indispensable: var/cache contient des fichiers compiles avec les anciens +# uid/gid et peut empecher PHP d'ecrire au demarrage suivant. +if [ -d "${SCRIPT_DIR}/var/cache" ]; then + log "[cache] Purge de var/cache (sera reconstruit par PHP au demarrage)..." + rm -rf "${SCRIPT_DIR}/var/cache" + log " OK" +fi + +# --- 6. Chown final pour matcher l'utilisateur PHP du conteneur --- +if [ "${SKIP_CHOWN}" = "false" ]; then + log "[chown] Application de '${RESTORE_OWNER}' sur les fichiers restaures..." + CHOWN_TARGETS=() + [ -d "${SCRIPT_DIR}/var" ] && CHOWN_TARGETS+=("${SCRIPT_DIR}/var") + [ -d "${SCRIPT_DIR}/public/uploads" ] && CHOWN_TARGETS+=("${SCRIPT_DIR}/public/uploads") + [ -d "${SCRIPT_DIR}/config/cert" ] && CHOWN_TARGETS+=("${SCRIPT_DIR}/config/cert") + [ -f "${SCRIPT_DIR}/.env.local" ] && CHOWN_TARGETS+=("${SCRIPT_DIR}/.env.local") + + if [ ${#CHOWN_TARGETS[@]} -gt 0 ]; then + chown -R "${RESTORE_OWNER}" "${CHOWN_TARGETS[@]}" + log " OK - cibles: ${CHOWN_TARGETS[*]}" + else + log " Aucune cible a chown" + fi +else + log "[chown] Saut du chown final (--skip-chown)" + log " ATTENTION: PHP pourrait ne pas avoir les droits d'ecriture sur var/, public/uploads/..." +fi + log "=== Migration terminee avec succes ===" log log "Etapes recommandees post-migration:" -log " 1. make start_prod (si pas deja fait)" -log " 2. make migrate_prod (appliquer les migrations Doctrine eventuelles)" -log " 3. make clear_prod (vider le cache)" -log " 4. Verifier le bon fonctionnement de l'application" +log " 1. make build_prod (si l'image PHP n'a pas ete reconstruite avec le bon UID)" +log " 2. make start_prod (si pas deja fait)" +log " 3. make migrate_prod (appliquer les migrations Doctrine eventuelles)" +log " 4. make clear_prod (vider le cache)" +log " 5. Verifier le bon fonctionnement de l'application"