Potion Bottle Icon Manuel d'alchimie du code Potion Bottle Icon

Déployer avec rsync, SSH et changer les permissions avec un wrapper sudo restreint

- 760 mots - Temps de lecture estimé: 4 minutes

🌘 Introduction

Je te présente une procédure pour déployer un site avec rsync via SSH, puis appliquer une propriété finale contrôlée par un wrapper root restreint. Mon objectif ici est de t’offrir une méthode d’automatisation pour la mise à jour d’un site web statique sans donner de privilèges root étendus. J’ai mis en place cette nouvelle procédure pour remplacer l’utilisation de lftp, qui est moins efficace pour les synchronisations incrémentales étant donné que tous les fichiers sont transférés à chaque fois.

Contexte de test : j’ai réalisé les essais sur un serveur Yunohost (système Debian dérivé). Les commandes sont génériques mais peuvent nécessiter des ajustements selon ton environnement.

Remarque importante : les UID et GID cités dans les exemples (par ex. 990:33) sont des exemples. Adapte-les à ton contexte.

🌘 Raison technique

🌘 Créer l’utilisateur et le groupe (serveur / Yunohost)

Procédure minimale à exécuter en tant que root sur ton serveur :

# Crée le groupe dédié au déploiement (ignore l'erreur si le groupe existe déjà)
groupadd web-deploy || true

# Crée un utilisateur système pour les déploiements (connexion par clé SSH)
useradd -m -s /bin/bash deployuser || adduser --disabled-password --gecos "" deployuser

# Ajoute l'utilisateur au groupe de déploiement
usermod -aG web-deploy deployuser

Utilise des noms cohérents pour ton infrastructure. L’utilisateur deployuser servira uniquement à la connexion SSH pour rsync.

🌘 Préparer le dossier cible (serveur / Yunohost)

But : permettre l’écriture par les membres du groupe et préserver le groupe sur les nouveaux fichiers.

TARGET=/var/www/application/www
mkdir -p "$TARGET"
# Rends le dossier appartenant à root:web-deploy
chown -R root:web-deploy "$TARGET"

# Rends les dossiers : setgid + rwx pour le groupe
find "$TARGET" -type d -exec chmod 2775 {} \;
# Rends les fichiers : rw pour le propriétaire et le groupe
find "$TARGET" -type f -exec chmod 664 {} \;

Le bit setgid (2) sur les dossiers force l’héritage du groupe.

🌘 Wrapper root restreint pour appliquer la propriété (serveur / Yunohost)

Le wrapper est situé dans le fichier /usr/local/sbin/deploy_chown_site.sh. Il vérifie la cible canonique (readlink -f) et n’autorise que la racine autorisée ou ses sous-chemins. Le code ci-dessous contient des commentaires explicatifs ; garde-les pour la maintenance.

Contenu recommandé (avec commentaires) :

#!/bin/bash
set -euo pipefail
# Chemin autorisé : remplace-le par le chemin canonique de ton site
ALLOWED="/var/www/application/www"

# Vérifie le nombre d'arguments
if [ "$#" -ne 1 ]; then
  echo "Usage: $0 <path>"
  exit 2
fi

TARGET="$1"
# Obtiens le chemin canonique de la cible
TARGET_ABS="$(readlink -f "$TARGET")" || { echo "target_readlink_failed"; exit 3; }
# Obtiens le chemin canonique autorisé
ALLOWED_ABS="$(readlink -f "$ALLOWED")"

# N'autorise que la racine autorisée ou ses sous-chemins
case "$TARGET_ABS" in
  "$ALLOWED_ABS" | "$ALLOWED_ABS"/*)
    # Applique la propriété finale (exemple d'UID:GID)
    exec /bin/chown -R 990:33 "$TARGET_ABS"
    ;;
  *)
    echo "Target not allowed"
    exit 3
    ;;
esac

Sécurise le script :

chown root:root /usr/local/sbin/deploy_chown_site.sh
chmod 750 /usr/local/sbin/deploy_chown_site.sh

Vérifie que seul root possède le fichier (évite les modifications non autorisées). Nous recherchons la permission 750 : rwx pour root, rx pour le groupe (pour permettre l’exécution), et aucune permission pour les autres.

ls -l /usr/local/sbin/deploy_chown_site.sh

Remplace 990:33 par l’UID:GID que tu souhaites appliquer pour ton environnement, ou rends cette valeur paramétrable si tu gères ça dans un contexte contrôlé.

🌘 sudoers : autoriser uniquement ce wrapper (serveur)

Crée /etc/sudoers.d/deploy_chown contenant la ligne suivante :

deployuser ALL=(root) NOPASSWD: /usr/local/sbin/deploy_chown_site.sh

Vérifie la syntaxe avec visudo -c et assure-toi que le fichier a les permissions 0440.

🌘 Script local deploy.sh (client)

Ton code devrait avoir le comportement suivant : vérifie les variables d’environnement, lance rsync, puis invoque le wrapper via ssh.

Exemple de fichier .env.template :

# Nom de l'utilisateur SSH autorisé à déployer
DEPLOY_USER=your_deploy_user

# Hôte SSH de destination
DEPLOY_HOST=your.deployment.host

# Clé privée utilisée pour l'authentification SSH
DEPLOY_KEY=/path/to/your/ssh/key

# Chemin cible du déploiement sur le serveur
DEPLOY_PATH=/path/to/your/deploy/path

# Port SSH, habituellement 22
DEPLOY_PORT=22

DEPLOY_USER, DEPLOY_HOST, DEPLOY_KEY et DEPLOY_PATH sont requis. DEPLOY_PORT est optionnel si tu utilises le port SSH standard, auquel cas il peut rester à 22.

Extrait :

if [ -z "${DEPLOY_USER:-}" ] || [ -z "${DEPLOY_HOST:-}" ] || [ -z "${DEPLOY_PATH:-}" ] || [ -z "${DEPLOY_KEY:-}" ]; then
  echo "Veuillez définir DEPLOY_USER, DEPLOY_HOST, DEPLOY_PATH et DEPLOY_KEY"
  exit 1
fi

RSYNC_SSH_OPTS="-i \"$DEPLOY_KEY\" -p $DEPLOY_PORT -o StrictHostKeyChecking=no"

rsync -avz --delete --rsync-path="sudo mkdir -p '$DEPLOY_PATH' && sudo rsync" -e "ssh $RSYNC_SSH_OPTS" _site/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH"

ssh -i "$DEPLOY_KEY" -p "$DEPLOY_PORT" "$DEPLOY_USER@$DEPLOY_HOST" \
  "sudo /usr/local/sbin/deploy_chown_site.sh '$DEPLOY_PATH' || echo 'chown wrapper failed'"

Cette variante suppose que sudo est configuré sans mot de passe pour les commandes invoquées à distance. Sans NOPASSWD, rsync et mkdir ne pourront pas répondre à une invite interactive, et le déploiement échouera.

Je te recommande d’abord d’exécuter un rsync -n (dry-run) pour vérifier la liste des transferts.

🌘 Tests et vérifications

Après le déploiement, tu peux vérifier la propriété et les permissions :

ssh -i "$DEPLOY_KEY" -p "$DEPLOY_PORT" "$DEPLOY_USER@$DEPLOY_HOST" \
  "ls -ld '$DEPLOY_PATH' && stat -c '%U %G %a' '$DEPLOY_PATH'"

Si rsync renvoie Permission denied, vérifie que le parent existe, que les permissions du dossier permettent l’écriture par le groupe, et que l’utilisateur de déploiement est bien dans le groupe approprié.

🌘 Remarques sécurité et opérationnelles

🌘 Résumé

Récapitulatif rapide :

  1. crée un utilisateur de déploiement et un groupe dédié ;
  2. rends le dossier cible writable par le groupe (setgid) ;
  3. installe un wrapper root minimal qui n’accepte qu’un chemin canonique ;
  4. autorise l’utilisateur de déploiement à exécuter ce wrapper via sudo sans mot de passe ;
  5. utilise rsync localement puis appelle le wrapper pour fixer la propriété finale.

Cette approche sépare le transfert de fichiers (opéré par rsync sous l’utilisateur de déploiement) de l’opération privilégiée finale (contrôlée par un wrapper root).

Shooting Stars IconConfiguration ExpressShooting Stars Icon

Bénéficie d'une heure de consultation pour la configuration de tes outils de travail et de communications. Cette session intensive t'offre des solutions concrètes et un plan d'action clair.

Découvre la Configuration Express.
Abonne-toi au fil RSS pour ne rien manquer.

Étiquettes