Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
171 lines
7.0 KiB
Bash
Executable File
171 lines
7.0 KiB
Bash
Executable File
#!/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_<id>.
|
|
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 <<NEXT
|
|
|
|
=============================================================================
|
|
Noeud de donnees pret.
|
|
=============================================================================
|
|
Etapes suivantes (depuis votre poste) :
|
|
|
|
1. Copier la configuration :
|
|
rsync -a infra/prod/data-node/ deploy@${DB_PRIVATE_IP}:/tmp/data-node/
|
|
ssh deploy@<ip-publique-db> '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@<ip-publique-db>:/tmp/
|
|
ssh deploy@<ip> '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 <ip-publique-db> # doit repondre filtered
|
|
=============================================================================
|
|
NEXT
|