#!/usr/bin/env bash # ============================================================================= # 01 - Installation du noeud de donnees (db-01) # ============================================================================= # Prerequis : 00-bootstrap-common.sh data deja execute sur ce serveur. # # sudo bash 01-setup-data-node.sh # # Ce script : # 1. monte le volume Hetzner sur /var/lib/xpeditis/pgdata # 2. installe Docker Engine depuis le depot officiel # 3. genere le certificat TLS interne de PostgreSQL # 4. installe age (chiffrement des dumps) # 5. installe les unites systemd de sauvegarde # # Il ne demarre PAS la base : il faut d'abord deposer .env.data (SOPS). set -euo pipefail [[ "$EUID" -eq 0 ]] || { echo "Executer en root (sudo)." >&2; exit 1; } APP_PRIVATE_IP="${APP_PRIVATE_IP:-10.10.1.10}" DB_PRIVATE_IP="${DB_PRIVATE_IP:-10.10.1.20}" BASE_DIR=/opt/xpeditis/data-node DATA_ROOT=/var/lib/xpeditis log() { printf '\n>>> %s\n' "$*"; } # --- 1. Volume de donnees ---------------------------------------------------- log "Montage du volume PostgreSQL" mkdir -p "${DATA_ROOT}"/{pgdata,redis,certs,dumps} # Hetzner expose les volumes sous /dev/disk/by-id/scsi-0HC_Volume_. VOLUME_DEV="$(ls /dev/disk/by-id/scsi-0HC_Volume_* 2>/dev/null | head -1 || true)" if [[ -n "$VOLUME_DEV" ]]; then if ! blkid "$VOLUME_DEV" >/dev/null 2>&1; then log "Formatage de $VOLUME_DEV en ext4 (volume vierge)" mkfs.ext4 -F -L xpeditis-pgdata "$VOLUME_DEV" else log "Volume deja formate, conservation des donnees existantes" fi if ! grep -q "${DATA_ROOT}/pgdata" /etc/fstab; then # `nofail` : si le volume est absent au boot, le serveur demarre quand meme # et on diagnostique en SSH plutot que de se retrouver hors ligne. echo "${VOLUME_DEV} ${DATA_ROOT}/pgdata ext4 discard,nofail,defaults 0 2" >> /etc/fstab fi mount -a df -h "${DATA_ROOT}/pgdata" else echo "AVERTISSEMENT: aucun volume Hetzner detecte. Les donnees resteront sur le disque systeme." fi # L'UID/GID 999 est celui de l'utilisateur postgres dans l'image officielle. chown -R 999:999 "${DATA_ROOT}/pgdata" "${DATA_ROOT}/redis" "${DATA_ROOT}/dumps" chmod 700 "${DATA_ROOT}/pgdata" # --- 2. Docker Engine -------------------------------------------------------- if ! command -v docker >/dev/null; then log "Installation de Docker Engine (depot officiel)" install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ > /etc/apt/sources.list.d/docker.list apt-get update -qq apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin fi log "Durcissement du demon Docker" cat > /etc/docker/daemon.json <<'JSON' { "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" }, "live-restore": true, "userland-proxy": false, "no-new-privileges": true, "icc": false, "default-address-pools": [ { "base": "172.28.0.0/16", "size": 24 } ] } JSON systemctl enable --now docker systemctl restart docker # --- 3. Certificat TLS de PostgreSQL ---------------------------------------- # Certificat auto-signe : PostgreSQL n'est joignable que depuis app-01 sur un # reseau prive, il n'y a pas de tiers a authentifier. Ce certificat sert a # CHIFFRER le transport, pas a prouver une identite publique. # Cote client, DATABASE_SSL=true avec rejectUnauthorized:false accepte ce # certificat : c'est coherent, et documente dans 04-noeud-donnees.md. if [[ ! -f "${DATA_ROOT}/certs/server.key" ]]; then log "Generation du certificat TLS PostgreSQL (10 ans)" openssl req -new -x509 -days 3650 -nodes \ -newkey rsa:4096 \ -out "${DATA_ROOT}/certs/server.crt" \ -keyout "${DATA_ROOT}/certs/server.key" \ -subj "/CN=xpeditis-prod-db-01/O=Xpeditis/C=FR" \ -addext "subjectAltName=IP:${DB_PRIVATE_IP},DNS:postgres" # PostgreSQL refuse de demarrer si la cle privee est lisible par d'autres. chown 999:999 "${DATA_ROOT}/certs/server.key" "${DATA_ROOT}/certs/server.crt" chmod 600 "${DATA_ROOT}/certs/server.key" chmod 644 "${DATA_ROOT}/certs/server.crt" else log "Certificat TLS deja present, conserve" fi # --- 4. age (chiffrement des dumps) ----------------------------------------- if ! command -v age >/dev/null; then log "Installation de age" apt-get install -y -qq age fi mkdir -p /root/.config/xpeditis chmod 700 /root/.config/xpeditis # --- 5. Arborescence applicative -------------------------------------------- log "Preparation de ${BASE_DIR}" mkdir -p "${BASE_DIR}"/{conf,backup} chmod 750 "${BASE_DIR}" # --- 6. Unites systemd de sauvegarde ---------------------------------------- if [[ -f "${BASE_DIR}/backup/xpeditis-backup.service" ]]; then log "Installation des timers de sauvegarde" install -m 0644 "${BASE_DIR}/backup/xpeditis-backup.service" /etc/systemd/system/ install -m 0644 "${BASE_DIR}/backup/xpeditis-backup.timer" /etc/systemd/system/ install -m 0644 "${BASE_DIR}/backup/xpeditis-backup-verify.service" /etc/systemd/system/ install -m 0644 "${BASE_DIR}/backup/xpeditis-backup-verify.timer" /etc/systemd/system/ chmod +x "${BASE_DIR}/backup/"*.sh systemctl daemon-reload systemctl enable --now xpeditis-backup.timer xpeditis-backup-verify.timer systemctl list-timers 'xpeditis-*' --no-pager else echo "AVERTISSEMENT: ${BASE_DIR}/backup/ vide. Copiez infra/prod/data-node/ puis relancez ce script." fi # --- 7. Rappel du filtrage reseau ------------------------------------------- log "Regles UFW effectives" ufw status numbered cat < 'sudo rsync -a /tmp/data-node/ ${BASE_DIR}/' 2. Dechiffrer et deposer les secrets : sops -d --input-type dotenv --output-type dotenv \\ infra/prod/data-node/data-node.sops.env > /tmp/.env.data scp /tmp/.env.data deploy@:/tmp/ ssh deploy@ 'sudo install -m600 -o root -g root /tmp/.env.data ${BASE_DIR}/.env.data && shred -u /tmp/.env.data' 3. Demarrer : cd ${BASE_DIR} && sudo docker compose -f docker-compose.data.yml --env-file .env.data up -d --build 4. Premiere sauvegarde complete (obligatoire avant d'ouvrir au public) : sudo systemctl start xpeditis-backup.service sudo journalctl -u xpeditis-backup -f Verifier que rien n'est expose publiquement, depuis un autre reseau : nmap -Pn -p 5432,6379 # doit repondre filtered ============================================================================= NEXT