From 525919926c8fa78b1492eac50024a8ae0087afbe Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Thu, 25 Jun 2026 14:01:00 +0000 Subject: [PATCH] fix(vm201): roa-qr cert auto-renew + add to SSL monitor Cauza ERR_CERT_DATE_INVALID pe roa-qr.romfast.ro: renewal-ul win-acme avea Installation plugin "None" in loc de IIS -> certul se reinnoia in store dar binding-ul SNI ramanea pe certul vechi (expirat 31 mai). - monitor-ssl-certificates.sh: adaugat roa-qr.romfast.ro (Site ID 5); normalizat CRLF->LF (CRLF dadea exit 127 la exec pe Linux) - docs: box incident 2026-06-25 cu cauza-radacina + diagnostic per renewal Fix aplicat pe VM 201: plugin install None->IIS in renewal.json + force renew (cert nou valid pana 23 sep 2026, binding auto-actualizat). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../docs/vm201-certificat-letsencrypt-iis.md | 24 ++ .../scripts/monitor-ssl-certificates.sh | 345 +++++++++--------- 2 files changed, 197 insertions(+), 172 deletions(-) diff --git a/proxmox/vm201-windows/docs/vm201-certificat-letsencrypt-iis.md b/proxmox/vm201-windows/docs/vm201-certificat-letsencrypt-iis.md index 4048b3e..48ed670 100644 --- a/proxmox/vm201-windows/docs/vm201-certificat-letsencrypt-iis.md +++ b/proxmox/vm201-windows/docs/vm201-certificat-letsencrypt-iis.md @@ -206,6 +206,30 @@ iisreset Win-acme poate reînnoi certificatele, dar IIS uneori nu aplică noile certificate pe binding-uri. Acest lucru cauzează servirea certificatelor expirate chiar dacă cele noi sunt în Certificate Store. +> **Incident 2026-06-25 — `roa-qr.romfast.ro` servea cert expirat (31 mai).** +> Cauza-rădăcină: renewal-ul `roa-qr` fusese creat cu **Installation plugin = `None`** +> (`aecc502c-5f75-43d2-b578-f95d50c79ea1`) în loc de **IIS** +> (`ea6a5be3-f8de-4d27-a6bd-750b619b2ee2`). Win-acme reînnoia certul și-l punea în +> store (27 apr, 21 iun), dar **nu reactualiza niciodată binding-ul SNI** → binding-ul +> a rămas blocat pe certul din 2 mar, care a expirat pe 31 mai. (`wacs --list` arăta +> `roa-qr ... 1 error`.) Site-urile 1–4 erau OK pentru că au plugin-ul IIS. +> +> **Fix aplicat:** în `…\Renewals\.renewal.json` (roa-qr = +> `kfRYWLrEAkSk_-XRqoobEQ`) am schimbat GUID-ul din `InstallationPluginOptions` pe +> cel de IIS, apoi `wacs.exe --renew --id --force` → certul nou s-a legat +> **automat** pe binding (dovada că auto-renew-ul viitor funcționează). Backup: +> `.renewal.json.bak-20260625`. +> +> **Diagnostic (ce plugin de install are fiecare renewal):** +> ```powershell +> $dir = "C:\ProgramData\win-acme\acme-v02.api.letsencrypt.org" +> Get-ChildItem $dir -Filter "*.renewal.json" | ForEach-Object { +> $j = Get-Content $_.FullName -Raw | ConvertFrom-Json +> "{0,-40} install={1}" -f $j.LastFriendlyName, $j.InstallationPluginOptions[0].Plugin +> } +> # ea6a5be3-... = IIS (corect) ; aecc502c-... = None (NU re-leagă binding-ul) +> ``` + ### Soluția: Scripturi de Monitorizare #### 1. Script PowerShell pe VM 201 diff --git a/proxmox/vm201-windows/scripts/monitor-ssl-certificates.sh b/proxmox/vm201-windows/scripts/monitor-ssl-certificates.sh index 97b3eea..d9df651 100644 --- a/proxmox/vm201-windows/scripts/monitor-ssl-certificates.sh +++ b/proxmox/vm201-windows/scripts/monitor-ssl-certificates.sh @@ -1,172 +1,173 @@ -#!/bin/bash -# Script: monitor-ssl-certificates.sh -# Locatie: /opt/scripts/monitor-ssl-certificates.sh (pe pvemini) -# Scop: Verifica certificatele SSL extern si alerteaza/forteaza reinstalare -# Rulare: Cron zilnic sau la cerere - -set -e - -# Configurare -DAYS_WARNING=14 -DAYS_CRITICAL=7 -LOG_FILE="/var/log/ssl-monitor.log" -EMAIL_TO="root" # Proxmox trimite la adresa configurata - -# Domenii de verificat -# NOTA: efactura.roa.romfast.ro este un SENTINEL pentru certificatul wildcard -# *.roa.romfast.ro. Wildcardul nu poate fi testat direct, asa ca verificam -# un subdomeniu real acoperit de el. Site ID "WILDCARD" => doar ALERTA, -# fara auto-renew (wildcardul e DNS-01, reinnoit de cpanel-acme-dns.ps1 pe -# VM 201; auto-renew prin guest-exec nu mai functioneaza - exec dezactivat). -# Context: incident expirare wildcard 2026-05-31 (vezi README VM 201). -DOMAINS=( - "roa.romfast.ro" - "dokploy.romfast.ro" - "gitea.romfast.ro" - "roa2web.romfast.ro" - "efactura.roa.romfast.ro" -) - -# Site IDs pentru fiecare domeniu (in aceeasi ordine) -SITE_IDS=(1 2 3 4 "WILDCARD") - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" -} - -check_certificate() { - local domain=$1 - local expiry_date expiry_epoch now_epoch days_left - - # Obtine data expirare - expiry_date=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | \ - openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) - - if [[ -z "$expiry_date" ]]; then - echo "-1" - return - fi - - # Calculeaza zilele ramase - expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null) - now_epoch=$(date +%s) - days_left=$(( (expiry_epoch - now_epoch) / 86400 )) - - echo "$days_left" -} - -force_renew_certificate() { - local site_id=$1 - local domain=$2 - - # Wildcard (*.roa) = DNS-01, reinnoit automat de cpanel-acme-dns.ps1 pe VM 201. - # Nu incercam auto-renew aici (nu e HTTP-01/siteid si guest-exec e dezactivat) - - # doar semnalam ca a expirat ca sa intervina cineva pe VM 201. - if ! [[ "$site_id" =~ ^[0-9]+$ ]]; then - log "ALERTA: $domain (wildcard *.roa) necesita interventie manuala pe VM 201 - verifica renewal-ul win-acme cu validare cPanel (cpanel-acme-dns.ps1)" - return 1 - fi - - log "Fortez reinstalare certificat pentru $domain (Site ID: $site_id)..." - - # Executa pe VM 201 prin Proxmox guest agent - result=$(qm guest exec 201 -- powershell -Command \ - "cd C:\\Tools\\win-acme; .\\wacs.exe --target iis --siteid $site_id --installation iis --force" 2>&1) - - if echo "$result" | grep -q '"exitcode" : 0'; then - log "SUCCES: Certificat reinstalat pentru $domain" - return 0 - else - log "EROARE: Reinstalare esuata pentru $domain" - log "$result" - return 1 - fi -} - -restart_iis() { - log "Restart IIS..." - qm guest exec 201 -- cmd /c "iisreset" >/dev/null 2>&1 - log "IIS restartat" -} - -send_alert() { - local subject=$1 - local body=$2 - - # Foloseste sistemul de notificari Proxmox - if command -v pvesh &>/dev/null; then - echo "$body" | mail -s "$subject" "$EMAIL_TO" 2>/dev/null || true - fi - - log "ALERT: $subject" -} - -# Main -log "========== Verificare certificate SSL ==========" - -warnings=() -criticals=() -renewed=0 - -for i in "${!DOMAINS[@]}"; do - domain="${DOMAINS[$i]}" - site_id="${SITE_IDS[$i]}" - - days_left=$(check_certificate "$domain") - - if [[ "$days_left" == "-1" ]]; then - log "EROARE: Nu pot verifica $domain" - criticals+=("$domain: Nu pot obtine certificatul") - continue - fi - - log "$domain: $days_left zile ramase" - - if [[ $days_left -lt 0 ]]; then - criticals+=("$domain: EXPIRAT!") - # Forteaza reinstalare - if force_renew_certificate "$site_id" "$domain"; then - ((renewed++)) - fi - elif [[ $days_left -lt $DAYS_CRITICAL ]]; then - criticals+=("$domain: expira in $days_left zile") - # Forteaza reinstalare - if force_renew_certificate "$site_id" "$domain"; then - ((renewed++)) - fi - elif [[ $days_left -lt $DAYS_WARNING ]]; then - warnings+=("$domain: expira in $days_left zile") - # Forteaza reinstalare preventiv - if force_renew_certificate "$site_id" "$domain"; then - ((renewed++)) - fi - fi -done - -# Restart IIS daca am reinoit -if [[ $renewed -gt 0 ]]; then - restart_iis -fi - -# Trimite alerte -if [[ ${#criticals[@]} -gt 0 ]]; then - body="Certificate SSL CRITICE:\n\n" - for msg in "${criticals[@]}"; do - body+="- $msg\n" - done - body+="\nActiuni intreprinse: $renewed certificate reinstalate" - send_alert "[CRITICAL] Certificate SSL expirate/aproape de expirare" "$body" -fi - -if [[ ${#warnings[@]} -gt 0 && ${#criticals[@]} -eq 0 ]]; then - body="Certificate SSL WARNING:\n\n" - for msg in "${warnings[@]}"; do - body+="- $msg\n" - done - body+="\nActiuni intreprinse: $renewed certificate reinstalate" - send_alert "[WARNING] Certificate SSL aproape de expirare" "$body" -fi - -log "========== Sumar: $renewed reinstalate, ${#warnings[@]} warnings, ${#criticals[@]} critice ==========" - -exit 0 +#!/bin/bash +# Script: monitor-ssl-certificates.sh +# Locatie: /opt/scripts/monitor-ssl-certificates.sh (pe pvemini) +# Scop: Verifica certificatele SSL extern si alerteaza/forteaza reinstalare +# Rulare: Cron zilnic sau la cerere + +set -e + +# Configurare +DAYS_WARNING=14 +DAYS_CRITICAL=7 +LOG_FILE="/var/log/ssl-monitor.log" +EMAIL_TO="root" # Proxmox trimite la adresa configurata + +# Domenii de verificat +# NOTA: efactura.roa.romfast.ro este un SENTINEL pentru certificatul wildcard +# *.roa.romfast.ro. Wildcardul nu poate fi testat direct, asa ca verificam +# un subdomeniu real acoperit de el. Site ID "WILDCARD" => doar ALERTA, +# fara auto-renew (wildcardul e DNS-01, reinnoit de cpanel-acme-dns.ps1 pe +# VM 201; auto-renew prin guest-exec nu mai functioneaza - exec dezactivat). +# Context: incident expirare wildcard 2026-05-31 (vezi README VM 201). +DOMAINS=( + "roa.romfast.ro" + "dokploy.romfast.ro" + "gitea.romfast.ro" + "roa2web.romfast.ro" + "roa-qr.romfast.ro" + "efactura.roa.romfast.ro" +) + +# Site IDs pentru fiecare domeniu (in aceeasi ordine) +SITE_IDS=(1 2 3 4 5 "WILDCARD") + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +check_certificate() { + local domain=$1 + local expiry_date expiry_epoch now_epoch days_left + + # Obtine data expirare + expiry_date=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | \ + openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) + + if [[ -z "$expiry_date" ]]; then + echo "-1" + return + fi + + # Calculeaza zilele ramase + expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null) + now_epoch=$(date +%s) + days_left=$(( (expiry_epoch - now_epoch) / 86400 )) + + echo "$days_left" +} + +force_renew_certificate() { + local site_id=$1 + local domain=$2 + + # Wildcard (*.roa) = DNS-01, reinnoit automat de cpanel-acme-dns.ps1 pe VM 201. + # Nu incercam auto-renew aici (nu e HTTP-01/siteid si guest-exec e dezactivat) - + # doar semnalam ca a expirat ca sa intervina cineva pe VM 201. + if ! [[ "$site_id" =~ ^[0-9]+$ ]]; then + log "ALERTA: $domain (wildcard *.roa) necesita interventie manuala pe VM 201 - verifica renewal-ul win-acme cu validare cPanel (cpanel-acme-dns.ps1)" + return 1 + fi + + log "Fortez reinstalare certificat pentru $domain (Site ID: $site_id)..." + + # Executa pe VM 201 prin Proxmox guest agent + result=$(qm guest exec 201 -- powershell -Command \ + "cd C:\\Tools\\win-acme; .\\wacs.exe --target iis --siteid $site_id --installation iis --force" 2>&1) + + if echo "$result" | grep -q '"exitcode" : 0'; then + log "SUCCES: Certificat reinstalat pentru $domain" + return 0 + else + log "EROARE: Reinstalare esuata pentru $domain" + log "$result" + return 1 + fi +} + +restart_iis() { + log "Restart IIS..." + qm guest exec 201 -- cmd /c "iisreset" >/dev/null 2>&1 + log "IIS restartat" +} + +send_alert() { + local subject=$1 + local body=$2 + + # Foloseste sistemul de notificari Proxmox + if command -v pvesh &>/dev/null; then + echo "$body" | mail -s "$subject" "$EMAIL_TO" 2>/dev/null || true + fi + + log "ALERT: $subject" +} + +# Main +log "========== Verificare certificate SSL ==========" + +warnings=() +criticals=() +renewed=0 + +for i in "${!DOMAINS[@]}"; do + domain="${DOMAINS[$i]}" + site_id="${SITE_IDS[$i]}" + + days_left=$(check_certificate "$domain") + + if [[ "$days_left" == "-1" ]]; then + log "EROARE: Nu pot verifica $domain" + criticals+=("$domain: Nu pot obtine certificatul") + continue + fi + + log "$domain: $days_left zile ramase" + + if [[ $days_left -lt 0 ]]; then + criticals+=("$domain: EXPIRAT!") + # Forteaza reinstalare + if force_renew_certificate "$site_id" "$domain"; then + ((renewed++)) + fi + elif [[ $days_left -lt $DAYS_CRITICAL ]]; then + criticals+=("$domain: expira in $days_left zile") + # Forteaza reinstalare + if force_renew_certificate "$site_id" "$domain"; then + ((renewed++)) + fi + elif [[ $days_left -lt $DAYS_WARNING ]]; then + warnings+=("$domain: expira in $days_left zile") + # Forteaza reinstalare preventiv + if force_renew_certificate "$site_id" "$domain"; then + ((renewed++)) + fi + fi +done + +# Restart IIS daca am reinoit +if [[ $renewed -gt 0 ]]; then + restart_iis +fi + +# Trimite alerte +if [[ ${#criticals[@]} -gt 0 ]]; then + body="Certificate SSL CRITICE:\n\n" + for msg in "${criticals[@]}"; do + body+="- $msg\n" + done + body+="\nActiuni intreprinse: $renewed certificate reinstalate" + send_alert "[CRITICAL] Certificate SSL expirate/aproape de expirare" "$body" +fi + +if [[ ${#warnings[@]} -gt 0 && ${#criticals[@]} -eq 0 ]]; then + body="Certificate SSL WARNING:\n\n" + for msg in "${warnings[@]}"; do + body+="- $msg\n" + done + body+="\nActiuni intreprinse: $renewed certificate reinstalate" + send_alert "[WARNING] Certificate SSL aproape de expirare" "$body" +fi + +log "========== Sumar: $renewed reinstalate, ${#warnings[@]} warnings, ${#criticals[@]} critice ==========" + +exit 0