Align prod PHP container UID with bot user (1001) and harden restore.sh
Some checks failed
CI / sonarqube (push) Failing after 52s

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) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-10 14:04:14 +02:00
parent 238ded3f54
commit eb884faea1
3 changed files with 88 additions and 15 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -5,14 +5,19 @@
# Le bundle est attendu au format e_ticket_migration_<DATE>.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 <bundle.tar.gz> [--yes] [--skip-db] [--skip-uploads] [--skip-var] [--skip-secrets]
# ./restore.sh <bundle.tar.gz> [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 <bundle.tar.gz> [options]
sudo ./restore.sh <bundle.tar.gz> [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"