Fix expirare cert wildcard *.roa.romfast.ro (incident 2026-05-31): renewal-ul era [Manual] DNS-01, nu rula din Scheduled Task -> 61 erori -> expirat. Subdomeniile Dokploy (efactura.roa etc.) dadeau ERR_CERT_DATE_INVALID. - cpanel-acme-dns.ps1: hook win-ACME DNS-01 (cPanel UAPI mass_edit_zone, fallback ZoneEdit) care pune/sterge TXT _acme-challenge automat - cpanel-dns.config.example.json: template (token-ul real e gitignored) - monitor-ssl-certificates.sh: sentinel efactura.roa (wildcard) + alerta in loc de auto-renew prin guest-exec (dezactivat) - README + doc cert: flux DNS-01 cPanel + acces OpenSSH VM 201 Renewal nou roa-wildcard-cpanel, auto, due 2026-08-19; vechiul [Manual] anulat. Cert live valid pana 2026-09-23. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
320 lines
10 KiB
Markdown
320 lines
10 KiB
Markdown
# Certificat Let's Encrypt pentru IIS - Ghid Rapid
|
||
|
||
## Instalare Win-ACME
|
||
|
||
```powershell
|
||
# Download și instalare
|
||
Invoke-WebRequest -Uri "https://github.com/win-acme/win-acme/releases/download/v2.2.9.1701/win-acme.v2.2.9.1701.x64.pluggable.zip" -OutFile "$env:TEMP\win-acme.zip"
|
||
Expand-Archive -Path "$env:TEMP\win-acme.zip" -DestinationPath "C:\Tools\win-acme" -Force
|
||
```
|
||
|
||
## Prerequisite IIS
|
||
|
||
### Verificare Site ID-uri
|
||
```powershell
|
||
Import-Module WebAdministration
|
||
Get-Website | Select-Object ID, Name, State, @{N='Bindings';E={$_.Bindings.Collection.bindingInformation}}
|
||
```
|
||
|
||
### Adaugă Binding-uri pentru Domeniu
|
||
```powershell
|
||
# Exemplu pentru roa.romfast.ro pe Default Web Site
|
||
New-WebBinding -Name "Default Web Site" -Protocol http -Port 80 -HostHeader "roa.romfast.ro"
|
||
New-WebBinding -Name "Default Web Site" -Protocol https -Port 443 -HostHeader "roa.romfast.ro"
|
||
```
|
||
|
||
## Generare Certificate
|
||
|
||
### Metoda 1: Comenzi PowerShell (Automat)
|
||
```powershell
|
||
cd C:\Tools\win-acme
|
||
|
||
# Pentru fiecare site (înlocuiește Site ID și email)
|
||
.\wacs.exe --source iis --siteid 1 --accepttos --emailaddress your@email.com
|
||
.\wacs.exe --source iis --siteid 2 --accepttos --emailaddress your@email.com
|
||
.\wacs.exe --source iis --siteid 3 --accepttos --emailaddress your@email.com
|
||
```
|
||
|
||
### Metoda 2: Mod Interactiv
|
||
```powershell
|
||
cd C:\Tools\win-acme
|
||
.\wacs.exe
|
||
|
||
# În meniu:
|
||
# N - Create certificate (simple for IIS)
|
||
# Selectează site-ul
|
||
# Confirmă binding-urile
|
||
# yes - Accept ToS
|
||
# Enter email
|
||
```
|
||
|
||
## Configurare Binding-uri IIS cu SNI
|
||
|
||
### Important: SNI OBLIGATORIU pentru Multiple Certificate pe Același IP
|
||
|
||
**GUI - IIS Manager:**
|
||
1. Deschide IIS Manager (`inetmgr`)
|
||
2. Pentru fiecare site:
|
||
- Site → **Bindings** → Selectează **https** → **Edit**
|
||
- ☑️ **Bifează "Require Server Name Indication"**
|
||
- Selectează **certificatul corect** pentru site
|
||
- **OK**
|
||
3. Restart IIS: `iisreset`
|
||
|
||
**PowerShell:**
|
||
```powershell
|
||
Import-Module WebAdministration
|
||
|
||
# Exemplu pentru un site
|
||
$siteName = "Dokploy"
|
||
$hostHeader = "dokploy.romfast.ro"
|
||
|
||
# Găsește certificatul
|
||
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
|
||
$_.Subject -like "*$hostHeader*" -and $_.NotAfter -gt (Get-Date).AddDays(60)
|
||
} | Select-Object -First 1
|
||
|
||
# Șterge binding vechi și creează cu SNI (SslFlags = 1)
|
||
Remove-WebBinding -Name $siteName -Protocol https -HostHeader $hostHeader -ErrorAction SilentlyContinue
|
||
New-WebBinding -Name $siteName -Protocol https -Port 443 -HostHeader $hostHeader -SslFlags 1
|
||
|
||
# Asociază certificatul
|
||
$binding = Get-WebBinding -Name $siteName -Protocol https -HostHeader $hostHeader
|
||
$binding.AddSslCertificate($cert.Thumbprint, "My")
|
||
|
||
# Restart IIS
|
||
iisreset
|
||
```
|
||
|
||
## Verificare
|
||
|
||
### Listare Certificate Gestionate
|
||
```powershell
|
||
cd C:\Tools\win-acme
|
||
.\wacs.exe --list
|
||
```
|
||
|
||
### Verificare Certificate în Browser
|
||
```bash
|
||
# Din WSL sau Linux
|
||
echo | openssl s_client -connect domain.ro:443 -servername domain.ro 2>/dev/null | openssl x509 -noout -dates -subject
|
||
```
|
||
|
||
### Verificare Task Scheduler
|
||
```powershell
|
||
Get-ScheduledTask | Where-Object {$_.TaskName -like "*acme*"}
|
||
```
|
||
|
||
### Verificare Certificate IIS
|
||
```powershell
|
||
Get-ChildItem Cert:\LocalMachine\My | Where-Object {
|
||
$_.Issuer -like "*Let's Encrypt*" -and $_.NotAfter -gt (Get-Date)
|
||
} | Select-Object Subject, NotAfter, Thumbprint
|
||
```
|
||
|
||
## Reînnoire
|
||
|
||
### Automată
|
||
- Task Scheduler verifică zilnic
|
||
- Reînnoiește automat cu 30 zile înainte de expirare
|
||
|
||
### Manuală
|
||
```powershell
|
||
cd C:\Tools\win-acme
|
||
.\wacs.exe --renew --force
|
||
iisreset
|
||
```
|
||
|
||
## Troubleshooting
|
||
|
||
### Certificat Vechi Încă Servit
|
||
```powershell
|
||
# Verifică SNI
|
||
Get-WebBinding | Where-Object {$_.Protocol -eq "https"} | Select-Object @{N='Site';E={$_.ItemXPath -replace '.*name=''([^'']+)''.*','$1'}}, bindingInformation, @{N='SNI';E={($_.sslFlags -band 1) -eq 1}}
|
||
|
||
# Forțează reinstalare
|
||
cd C:\Tools\win-acme
|
||
.\wacs.exe --renew --force
|
||
iisreset
|
||
```
|
||
|
||
### Validare HTTP-01 Eșuează
|
||
- Verifică că portul 80 este accesibil din internet
|
||
- Verifică că DNS pointează corect
|
||
- Verifică că URL Rewrite nu blochează `/.well-known/acme-challenge/*`
|
||
|
||
### Certificate Nu Se Asociază Automat
|
||
Folosește scriptul: `configure-iis-sni.ps1`
|
||
|
||
## Structură Site-uri IIS
|
||
|
||
| Site ID | Nume | Hostname | Binding HTTPS | SNI |
|
||
|---------|--------------------|-----------------------|------------------------|----------|
|
||
| 1 | Default Web Site | roa.romfast.ro | *:443:roa.romfast.ro | ☑️ Activ |
|
||
| 2 | Dokploy | dokploy.romfast.ro | *:443:dokploy.romfast.ro | ☑️ Activ |
|
||
| 3 | Gitea | gitea.romfast.ro | *:443:gitea.romfast.ro | ☑️ Activ |
|
||
|
||
## Scripturi Utile
|
||
|
||
### Script Verificare/Reînnoire Certificate (PowerShell)
|
||
**Locație repo:** `../scripts/check-ssl-certificates.ps1`
|
||
**Locație VM 201:** `D:\kit\ssl\check-ssl-certificates.ps1`
|
||
|
||
```powershell
|
||
# Verificare manuală
|
||
D:\kit\ssl\check-ssl-certificates.ps1 -Verbose
|
||
|
||
# Forțare reinstalare toate certificatele
|
||
D:\kit\ssl\check-ssl-certificates.ps1 -Force -Verbose
|
||
```
|
||
|
||
### Script Monitorizare Certificate (Bash - Proxmox)
|
||
**Locație repo:** `../scripts/monitor-ssl-certificates.sh`
|
||
**Locație Proxmox:** `/opt/scripts/monitor-ssl-certificates.sh`
|
||
|
||
```bash
|
||
# Verificare manuală
|
||
/opt/scripts/monitor-ssl-certificates.sh
|
||
|
||
# Verificare log
|
||
tail -50 /var/log/ssl-monitor.log
|
||
```
|
||
|
||
## Comenzi Rapide
|
||
|
||
```powershell
|
||
# Instalare
|
||
Expand-Archive win-acme.zip -DestinationPath C:\Tools\win-acme
|
||
|
||
# Generare certificate
|
||
cd C:\Tools\win-acme
|
||
.\wacs.exe --source iis --siteid X --accepttos --emailaddress email@domain.com
|
||
|
||
# Verificare
|
||
.\wacs.exe --list
|
||
|
||
# Reînnoire
|
||
.\wacs.exe --renew --force
|
||
|
||
# Restart IIS
|
||
iisreset
|
||
```
|
||
|
||
## Monitorizare Automată Certificate
|
||
|
||
### Problema Cunoscută
|
||
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.
|
||
|
||
### Soluția: Scripturi de Monitorizare
|
||
|
||
#### 1. Script PowerShell pe VM 201
|
||
**Locație:** `D:\kit\ssl\check-ssl-certificates.ps1`
|
||
**Task Scheduler:** "SSL Certificate Check" - rulează zilnic la 07:00
|
||
|
||
```powershell
|
||
# Verificare manuală
|
||
D:\kit\ssl\check-ssl-certificates.ps1 -Verbose
|
||
|
||
# Forțare reinstalare toate certificatele
|
||
D:\kit\ssl\check-ssl-certificates.ps1 -Force -Verbose
|
||
```
|
||
|
||
#### 2. Script Bash pe Proxmox (pvemini)
|
||
**Locație:** `/opt/scripts/monitor-ssl-certificates.sh`
|
||
**Cron:** Zilnic la 08:00
|
||
|
||
```bash
|
||
# Verificare manuală
|
||
/opt/scripts/monitor-ssl-certificates.sh
|
||
|
||
# Verificare log
|
||
tail -50 /var/log/ssl-monitor.log
|
||
```
|
||
|
||
### Ce Fac Scripturile
|
||
- Verifică zilele rămase pentru fiecare certificat
|
||
- Dacă < 14 zile: forțează reinstalare cu `wacs.exe --force`
|
||
- Restart IIS după reinstalare
|
||
- Trimite alertă email dacă există probleme
|
||
|
||
### Verificare Task Schedulers
|
||
```powershell
|
||
# Pe VM 201
|
||
Get-ScheduledTask | Where-Object {$_.TaskName -like "*SSL*" -or $_.TaskName -like "*acme*"}
|
||
```
|
||
|
||
```bash
|
||
# Pe Proxmox
|
||
grep ssl /etc/crontab
|
||
```
|
||
|
||
## Wildcard `*.roa.romfast.ro` — Reînnoire Automată DNS-01 (cPanel)
|
||
|
||
> **Incident 2026-05-31:** wildcardul a expirat. Renewal-ul win-acme era de tip
|
||
> `[Manual]` (DNS-01 cu TXT pus de mână) → nu rula din Scheduled Task → ~60 de
|
||
> erori consecutive → expirare. Subdomeniile Dokploy (`efactura.roa…`, `space.roa…`)
|
||
> dădeau `ERR_CERT_DATE_INVALID`. Monitorizarea nu prindea pentru că `*.roa` nu era
|
||
> în lista de domenii verificate (era doar `roa.romfast.ro`, un cert separat).
|
||
|
||
Wildcardul **nu** poate folosi HTTP-01 (ca site-urile 1–6); necesită **DNS-01**.
|
||
DNS-ul `romfast.ro` e pe **hosting.com (cPanel)**, fără plugin nativ în win-acme,
|
||
deci folosim hook-ul de script `cpanel-acme-dns.ps1` care pune/șterge TXT-ul prin
|
||
cPanel API.
|
||
|
||
### Setup (o singură dată, pe VM 201 ca Administrator)
|
||
|
||
1. cPanel → **Manage API Tokens** → creează token cu drept de editare DNS.
|
||
2. Copiază pe VM 201:
|
||
- `scripts/cpanel-acme-dns.ps1` → `C:\Tools\win-acme\cpanel-acme-dns.ps1`
|
||
- `scripts/cpanel-dns.config.example.json` → `C:\Tools\win-acme\cpanel-dns.config.json`
|
||
și completează `Hostname`, `User`, `ApiToken`. **Nu commite** fișierul real
|
||
(e în `.gitignore`).
|
||
3. Test manual:
|
||
```powershell
|
||
cd C:\Tools\win-acme
|
||
.\cpanel-acme-dns.ps1 create _acme-challenge.roa.romfast.ro testvalue123
|
||
# verifică TXT-ul în cPanel Zone Editor, apoi:
|
||
.\cpanel-acme-dns.ps1 delete _acme-challenge.roa.romfast.ro testvalue123
|
||
```
|
||
4. Recreează renewal-ul wildcard:
|
||
```
|
||
.\wacs.exe (ca Administrator)
|
||
M → Create renewal (full options)
|
||
Source: Manual → *.roa.romfast.ro
|
||
Validation: dns-01 → "Create verification records with your own script"
|
||
Run create: Powershell.exe -File C:\Tools\win-acme\cpanel-acme-dns.ps1
|
||
Create args : create {RecordName} {Token}
|
||
Run delete: Powershell.exe -File C:\Tools\win-acme\cpanel-acme-dns.ps1
|
||
Delete args : delete {RecordName} {Token}
|
||
Store: Windows Certificate Store (My)
|
||
Installation: IIS → site "roa-apps" (re-leagă certul automat pe binding)
|
||
```
|
||
5. Confirmă Scheduled task (rulând `wacs.exe` ca admin) → reînnoire 100% automată.
|
||
|
||
### Verificare
|
||
```bash
|
||
echo | openssl s_client -connect efactura.roa.romfast.ro:443 \
|
||
-servername efactura.roa.romfast.ro 2>/dev/null | openssl x509 -noout -dates
|
||
```
|
||
|
||
### Monitorizare
|
||
`monitor-ssl-certificates.sh` include acum `efactura.roa.romfast.ro` ca **sentinel**
|
||
pentru wildcard (Site ID `WILDCARD`). Dacă expiră, scriptul **alertează** (nu mai
|
||
încearcă auto-renew prin guest-exec, care e dezactivat) → intervenție pe VM 201.
|
||
|
||
> **Securitate:** tokenul cPanel ajunge pe VM 201. Dă-i drepturi cât mai granulare
|
||
> (doar DNS). Dacă panoul e vechi și expune doar API2 ZoneEdit, setează
|
||
> `"LegacyZoneEdit": true` în config.
|
||
|
||
---
|
||
|
||
## Note Importante
|
||
|
||
- **SNI este OBLIGATORIU** pentru multiple certificate pe același IP:port
|
||
- Certificatele expiră la **90 zile**
|
||
- Task Scheduler reînnoiește automat cu **30 zile** înainte
|
||
- Fiecare domeniu trebuie să fie **accesibil pe port 80** din internet pentru validare HTTP-01
|
||
- DNS trebuie să pointeze corect către IP-ul public
|
||
- **Monitorizarea duală** (Windows + Proxmox) asigură redundanță
|