diff --git a/CHANGELOG.md b/CHANGELOG.md index ffc98fb4..46f25039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # Changelog - 4NK Node +## [1.1.2] - 2025-08-27 + +### Added +- Scripts de déploiement initial: + - `scripts/deploy_first_install_with_certs.sh` (Let’s Encrypt webroot + cron de renouvellement) + - `scripts/deploy_first_install_no_certs.sh` (auto‑signé) + +### Changed +- Nginx: activation de HSTS (Strict‑Transport‑Security) sur l’hôte public +- Documentation: INSTALLATION.md et CONFIGURATION.md enrichies (webroot ACME, renouvellement, déploiement automatisé) + Tous les changements notables de ce projet seront documentés dans ce fichier. @@ -63,10 +74,16 @@ et ce projet adhère au [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - `docker-compose.yml` : ajout d’un mini serveur HTTP (busybox httpd) sur `sdk_signer` pour l’endpoint de santé interne 9092, exposé via Nginx en `/signer/health`. - `sdk_relay/healthcheck.sh` : assouplissement du healthcheck (attente d’un indicateur de démarrage) pour éviter les faux négatifs durant la phase de rescan/initialisation. +- CI : publication automatique des releases sur push de tag `v*` via secret `RELEASE_TOKEN`. ### Fixed - `/signer/health` retournait 502 via le reverse proxy ; désormais HTTP 200 avec corps `ok`. +### Added +- Scripts de déploiement initial: + - `scripts/deploy_first_install_with_certs.sh` (Let’s Encrypt webroot, cron de renouvellement) + - `scripts/deploy_first_install_no_certs.sh` (auto‑signé) + ## [1.0.0] - 2024-12-19 ### Added diff --git a/VERSION b/VERSION index 56130fb3..0f1acbd5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.1.1 +v1.1.2 diff --git a/docker-compose.yml b/docker-compose.yml index 2e23302d..37cab818 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -235,6 +235,7 @@ services: - ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./certs:/etc/nginx/certs:ro - ./ihm_client/dist:/usr/share/nginx/html:ro + - ./acme:/var/www/certbot:ro ports: - "80:80" - "443:443" diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 17089109..d88018c2 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -597,6 +597,24 @@ sudo certbot --nginx -d your-domain.com sudo certbot renew --dry-run ``` +### 3. Production (webroot, HSTS et renouvellement) + +- Le reverse proxy sert les challenges ACME en HTTP: `/.well-known/acme-challenge/` via le montage `./acme -> /var/www/certbot`. +- Le certificat de production est installé dans `./certs/server.crt` et `./certs/server.key`. +- HSTS est activé par défaut (entête `Strict-Transport-Security: max-age=31536000; includeSubDomains`). +- Renouvellement: script idempotent `scripts/renew_certs.sh` (webroot certbot + reload Nginx). + - Déploiement initial automatisé: `scripts/deploy_first_install_with_certs.sh` (avec certificats) ou `scripts/deploy_first_install_no_certs.sh` (auto‑signé). + +Exécution manuelle: +```bash +./scripts/renew_certs.sh +``` + +Cron (quotidien à 03:00): +```bash +0 3 * * * /home/debian/4NK_node/scripts/renew_certs.sh >> /home/debian/4NK_node/logs/cert_renew.log 2>&1 +``` + ## 🔧 Configuration de Monitoring ### 1. Prometheus diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index a0b38f99..ce0698cf 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -172,6 +172,27 @@ chmod +x scripts/generate_certs.sh ./scripts/generate_certs.sh ``` +#### (Option production) Certificats Let’s Encrypt + HSTS + +Pré-requis : le DNS du domaine (ex. `dev4.4nkweb.com`) pointe vers le serveur et le port 80 est accessible. + +1) Le reverse proxy expose le webroot ACME en HTTP sur `/.well-known/acme-challenge/` (répertoire local `./acme`). + +2) Émettre/renouveler et installer le certificat : +```bash +chmod +x scripts/renew_certs.sh +./scripts/renew_certs.sh +``` + +3) Activer un cron quotidien (03:00) : +```bash +0 3 * * * /home/debian/4NK_node/scripts/renew_certs.sh >> /home/debian/4NK_node/logs/cert_renew.log 2>&1 +``` + +Notes : +- Les fichiers sont installés dans `./certs/server.crt` et `./certs/server.key` (droits: 0644/0600). +- HSTS est activé par défaut sur le reverse proxy. + ### 4. Configuration Bitcoin Core ```bash @@ -268,6 +289,18 @@ relay_id=relay-1 # Changer pour chaque relay ## 🚀 Démarrage +### Déploiement automatisé (première installation) + +Deux scripts facilitent l’installation complète A→Z : + +- Avec certificats Let’s Encrypt (domaine requis): + - `scripts/deploy_first_install_with_certs.sh --domain dev4.4nkweb.com --email admin@exemple.com [--skip-ui]` + - Actions: préparation des dossiers, lancement du proxy pour ACME, émission LE en webroot, installation dans `./certs/`, démarrage complet, cron de renouvellement, vérifications. + +- Sans certificats (auto‑signé): + - `scripts/deploy_first_install_no_certs.sh [--skip-ui]` + - Actions: génération auto‑signée via `scripts/generate_certs.sh`, démarrage complet, vérifications locales. + ### 1. Démarrage Complet ```bash diff --git a/proxy/nginx.conf b/proxy/nginx.conf index 77f873f5..5efa3fe8 100644 --- a/proxy/nginx.conf +++ b/proxy/nginx.conf @@ -1,6 +1,18 @@ server { listen 80; - return 301 https://$host$request_uri; + server_name dev4.4nkweb.com; + + # ACME HTTP-01 challenge (Let’s Encrypt) + location ^~ /.well-known/acme-challenge/ { + alias /var/www/certbot/.well-known/acme-challenge/; + default_type text/plain; + try_files $uri =404; + } + + # Redirection par défaut vers HTTPS + location / { + return 301 https://$host$request_uri; + } } server { @@ -19,6 +31,7 @@ server { add_header X-Content-Type-Options nosniff always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # CSP minimale (adapter selon besoins) add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: http: https:; img-src 'self' data:;" always; diff --git a/scripts/deploy_first_install_no_certs.sh b/scripts/deploy_first_install_no_certs.sh new file mode 100644 index 00000000..c0eabb36 --- /dev/null +++ b/scripts/deploy_first_install_no_certs.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Déploiement initial A→Z sans certificats (auto-signé local) +# Usage: ./scripts/deploy_first_install_no_certs.sh [--skip-ui] + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +BUILD_UI=1 + +while [[ $# -gt 0 ]]; do + case "$1" in + --skip-ui) BUILD_UI=0; shift;; + *) echo "Option inconnue: $1" >&2; exit 2;; + esac +done + +echo "[1/5] Préparation des dossiers (certs)" +mkdir -p "$ROOT_DIR/certs" + +echo "[2/5] Génération de certificats auto-signés (script projet)" +if [[ -x "$ROOT_DIR/scripts/generate_certs.sh" ]]; then + ( cd "$ROOT_DIR" && ./scripts/generate_certs.sh ) +else + echo "scripts/generate_certs.sh introuvable ou non exécutable. Abandon." >&2 + exit 1 +fi + +echo "[3/5] Optionnel: build UI locale (ihm_client/dist)" +if [[ $BUILD_UI -eq 1 && -x "$ROOT_DIR/scripts/build_ui_local.sh" ]]; then + ( cd "$ROOT_DIR" && ./scripts/build_ui_local.sh ) || true +fi + +echo "[4/5] Démarrage complet de l’infrastructure (build si nécessaire)" +docker compose -f "$ROOT_DIR/docker-compose.yml" up -d --build + +echo "[5/5] Vérifications rapides" +curl -skI https://127.0.0.1/signer/health | head -n 1 || true +curl -skI https://127.0.0.1/ | head -n 1 || true + +echo "Déploiement initial (auto-signé) terminé. Accès local: https://127.0.0.1/" diff --git a/scripts/deploy_first_install_with_certs.sh b/scripts/deploy_first_install_with_certs.sh new file mode 100644 index 00000000..aed9e88a --- /dev/null +++ b/scripts/deploy_first_install_with_certs.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Déploiement initial A→Z avec certificats Let’s Encrypt (webroot) +# Usage: ./scripts/deploy_first_install_with_certs.sh --domain dev4.4nkweb.com --email admin@example.com [--skip-ui] + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +DOMAIN="" +EMAIL="" +BUILD_UI=1 + +while [[ $# -gt 0 ]]; do + case "$1" in + --domain) DOMAIN="$2"; shift 2;; + --email) EMAIL="$2"; shift 2;; + --skip-ui) BUILD_UI=0; shift;; + *) echo "Option inconnue: $1" >&2; exit 2;; + esac +done + +if [[ -z "$DOMAIN" || -z "$EMAIL" ]]; then + echo "Erreur: --domain et --email sont requis" >&2 + exit 2 +fi + +echo "[1/8] Préparation des dossiers (acme/letsencrypt/certs)" +mkdir -p "$ROOT_DIR/acme/.well-known/acme-challenge" \ + "$ROOT_DIR/letsencrypt" \ + "$ROOT_DIR/letsencrypt_lib" \ + "$ROOT_DIR/certs" +chmod -R 755 "$ROOT_DIR/acme" + +echo "[2/8] Optionnel: build UI locale (ihm_client/dist)" +if [[ $BUILD_UI -eq 1 && -x "$ROOT_DIR/scripts/build_ui_local.sh" ]]; then + ( cd "$ROOT_DIR" && ./scripts/build_ui_local.sh ) || true +fi + +echo "[3/8] Démarrage du reverse proxy pour le challenge ACME" +docker compose -f "$ROOT_DIR/docker-compose.yml" up -d --no-deps reverse_proxy + +echo "[4/8] Émission du certificat (Let’s Encrypt, webroot) pour $DOMAIN" +docker run --rm \ + -v "$ROOT_DIR/acme:/var/www/certbot" \ + -v "$ROOT_DIR/letsencrypt:/etc/letsencrypt" \ + -v "$ROOT_DIR/letsencrypt_lib:/var/lib/letsencrypt" \ + certbot/certbot certonly --webroot -w /var/www/certbot -d "$DOMAIN" --email "$EMAIL" --agree-tos --non-interactive + +echo "[5/8] Installation des fichiers de cert dans ./certs" +install -m 0644 "$ROOT_DIR/letsencrypt/live/$DOMAIN/fullchain.pem" "$ROOT_DIR/certs/server.crt" +install -m 0600 "$ROOT_DIR/letsencrypt/live/$DOMAIN/privkey.pem" "$ROOT_DIR/certs/server.key" + +echo "[6/8] Démarrage complet de l’infrastructure (build si nécessaire)" +docker compose -f "$ROOT_DIR/docker-compose.yml" up -d --build + +echo "[7/8] Mise en place du renouvellement automatique (cron 03:00)" +chmod +x "$ROOT_DIR/scripts/renew_certs.sh" || true +(sudo crontab -l 2>/dev/null; echo "0 3 * * * $ROOT_DIR/scripts/renew_certs.sh >> $ROOT_DIR/logs/cert_renew.log 2>&1") | sudo crontab - + +echo "[8/8] Vérifications rapides" +curl -skI "https://$DOMAIN" | head -n 1 || true +curl -skI "https://$DOMAIN/signer/health" | head -n 1 || true + +echo "Déploiement initial terminé. Domaine: https://$DOMAIN" diff --git a/scripts/renew_certs.sh b/scripts/renew_certs.sh new file mode 100755 index 00000000..c3a94cf0 --- /dev/null +++ b/scripts/renew_certs.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +DOMAIN="dev4.4nkweb.com" +EMAIL="admin@4nkweb.com" +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" + +mkdir -p "$ROOT_DIR/acme/.well-known/acme-challenge" "$ROOT_DIR/letsencrypt" "$ROOT_DIR/letsencrypt_lib" "$ROOT_DIR/certs" + +# Renew certificates using the same webroot method (volumes must be consistent) +docker run --rm \ + -v "$ROOT_DIR/acme:/var/www/certbot" \ + -v "$ROOT_DIR/letsencrypt:/etc/letsencrypt" \ + -v "$ROOT_DIR/letsencrypt_lib:/var/lib/letsencrypt" \ + certbot/certbot renew --non-interactive || true + +# Fallback: issue if missing (first time) +if [ ! -f "$ROOT_DIR/letsencrypt/live/$DOMAIN/fullchain.pem" ]; then + docker run --rm \ + -v "$ROOT_DIR/acme:/var/www/certbot" \ + -v "$ROOT_DIR/letsencrypt:/etc/letsencrypt" \ + -v "$ROOT_DIR/letsencrypt_lib:/var/lib/letsencrypt" \ + certbot/certbot certonly --webroot -w /var/www/certbot -d "$DOMAIN" --email "$EMAIL" --agree-tos --non-interactive +fi + +install -m 0644 "$ROOT_DIR/letsencrypt/live/$DOMAIN/fullchain.pem" "$ROOT_DIR/certs/server.crt" +install -m 0600 "$ROOT_DIR/letsencrypt/live/$DOMAIN/privkey.pem" "$ROOT_DIR/certs/server.key" + +# Reload reverse proxy with updated files +docker compose -f "$ROOT_DIR/docker-compose.yml" up -d --no-deps --force-recreate reverse_proxy + +echo "Certificates installed for $DOMAIN and reverse proxy reloaded."