====== Rinnovo schedulato ====== **ComplessitΓ :** Bassa \\ **Durata:** 30-60 minuti di configurazione \\ **Caso d'uso:** Server senza ACME/Kubernetes Rinnovo automatico dei certificati tramite job schedulati per ambienti server classici. ---- ===== Architettura ===== flowchart LR subgraph TRIGGER["⏰ TRIGGER"] C[Cron Job] S[Systemd Timer] T[Task Scheduler] end subgraph CHECK["πŸ” VERIFICA"] E{Scadenza < 30 giorni?} end subgraph RENEW["πŸ”„ RINNOVO"] R1[Creazione CSR] R2[Richiesta firma] R3[Distribuzione] R4[Ricarica servizio] end subgraph NOTIFY["πŸ“§ NOTIFICA"] N1[E-Mail] N2[Slack/Teams] N3[Ticket] end C --> E S --> E T --> E E -->|SΓ¬| R1 --> R2 --> R3 --> R4 E -->|No| N1 R4 --> N1 R4 --> N2 R4 --> N3 style E fill:#fff3e0 style R3 fill:#e8f5e9 ---- ===== Linux: Cron + Bash ===== ==== Script di rinnovo ==== #!/bin/bash # /usr/local/bin/cert-renewal.sh # Rinnovo automatico certificati set -euo pipefail # Configurazione CERT_DIR="/etc/ssl/certs" KEY_DIR="/etc/ssl/private" CA_SERVER="https://ca.internal.example.com" RENEWAL_DAYS=30 LOG_FILE="/var/log/cert-renewal.log" MAIL_TO="pki-team@example.com" log() { echo "$(date -Iseconds) $1" >> "$LOG_FILE" } check_and_renew() { local cert_file="$1" local key_file="$2" local service="$3" # Verifica scadenza if openssl x509 -checkend $((RENEWAL_DAYS * 86400)) -noout -in "$cert_file" 2>/dev/null; then log "INFO: $cert_file ancora valido" return 0 fi log "WARN: $cert_file in scadenza, rinnovo..." # Estrazione subject dal vecchio certificato local subject=$(openssl x509 -in "$cert_file" -subject -noout | sed 's/subject=//') local san=$(openssl x509 -in "$cert_file" -text -noout | grep -A1 "Subject Alternative" | tail -1 | tr -d ' ') # Creazione nuovo CSR local csr_file=$(mktemp) openssl req -new -key "$key_file" -out "$csr_file" -subj "$subject" # Invio CSR alla CA (Esempio: REST API) local new_cert=$(curl -s -X POST "$CA_SERVER/api/sign" \ -H "Content-Type: application/x-pem-file" \ -H "Authorization: Bearer $(cat /etc/ssl/ca-token)" \ --data-binary @"$csr_file") if [ -z "$new_cert" ]; then log "ERROR: Rinnovo fallito per $cert_file" echo "Rinnovo certificato fallito: $cert_file" | mail -s "CERT RENEWAL FAILED" "$MAIL_TO" rm "$csr_file" return 1 fi # Backup e sostituzione cp "$cert_file" "${cert_file}.bak.$(date +%Y%m%d)" echo "$new_cert" > "$cert_file" # Ricarica servizio if [ -n "$service" ]; then systemctl reload "$service" || systemctl restart "$service" log "INFO: Servizio $service ricaricato" fi rm "$csr_file" log "INFO: $cert_file rinnovato con successo" # Notifica echo "Certificato rinnovato: $cert_file" | mail -s "CERT RENEWED" "$MAIL_TO" } # Logica principale log "=== Avvio verifica rinnovo ===" # Definizione certificati: cert_file:key_file:service CERTS=( "/etc/ssl/certs/webserver.pem:/etc/ssl/private/webserver.key:nginx" "/etc/ssl/certs/api.pem:/etc/ssl/private/api.key:api-service" "/etc/ssl/certs/mail.pem:/etc/ssl/private/mail.key:postfix" ) for cert_entry in "${CERTS[@]}"; do IFS=':' read -r cert_file key_file service <<< "$cert_entry" check_and_renew "$cert_file" "$key_file" "$service" || true done log "=== Verifica rinnovo terminata ===" chmod +x /usr/local/bin/cert-renewal.sh ==== Cron-Job ==== # /etc/cron.d/cert-renewal # Giornalmente alle 03:00 0 3 * * * root /usr/local/bin/cert-renewal.sh ---- ===== Linux: Systemd Timer ===== # /etc/systemd/system/cert-renewal.service [Unit] Description=Certificate Renewal Service After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/cert-renewal.sh StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target # /etc/systemd/system/cert-renewal.timer [Unit] Description=Daily Certificate Renewal Check [Timer] OnCalendar=*-*-* 03:00:00 RandomizedDelaySec=1h Persistent=true [Install] WantedBy=timers.target # Attivazione timer systemctl daemon-reload systemctl enable --now cert-renewal.timer # Verifica stato systemctl list-timers cert-renewal.timer journalctl -u cert-renewal.service ---- ===== Windows: Task Scheduler + PowerShell ===== # %SCRIPTS_PATH%\Cert-Renewal.ps1 param( [int]$RenewalDays = 30, [string]$LogPath = "C:\Logs\cert-renewal.log", [string]$SmtpServer = "smtp.example.com", [string]$MailTo = "pki-team@example.com" ) function Write-Log { param([string]$Message) $timestamp = Get-Date -Format "yyyy-MM-ddTHH:mm:ss" "$timestamp $Message" | Out-File -Append -FilePath $LogPath } function Test-CertificateExpiry { param( [string]$CertPath, [int]$DaysThreshold ) $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertPath) $expiryDate = $cert.NotAfter $daysUntilExpiry = ($expiryDate - (Get-Date)).Days return @{ DaysRemaining = $daysUntilExpiry ExpiryDate = $expiryDate NeedsRenewal = $daysUntilExpiry -lt $DaysThreshold Subject = $cert.Subject } } function Invoke-CertificateRenewal { param( [string]$CertPath, [string]$KeyPath, [string]$ServiceName ) Write-Log "INFO: Avvio rinnovo per $CertPath" try { # Creazione CSR dal certificato esistente $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertPath) # Qui si chiamerebbe l'API della CA # Esempio: REST-Call alla CA interna $body = @{ subject = $cert.Subject san = $cert.Extensions | Where-Object { $_.Oid.Value -eq "2.5.29.17" } } | ConvertTo-Json $newCert = Invoke-RestMethod -Uri "https://ca.internal.example.com/api/renew" ` -Method POST ` -Body $body ` -Headers @{ Authorization = "Bearer $(Get-Content %SCRIPTS_PATH%\ca-token.txt)" } # Creazione backup $backupPath = "$CertPath.bak.$(Get-Date -Format 'yyyyMMdd')" Copy-Item $CertPath $backupPath # Salvataggio nuovo certificato $newCert | Out-File -FilePath $CertPath -Encoding ascii # Riavvio servizio if ($ServiceName) { Restart-Service $ServiceName -Force Write-Log "INFO: Servizio $ServiceName riavviato" } Write-Log "INFO: Rinnovo riuscito per $CertPath" # Notifica e-mail Send-MailMessage -To $MailTo -From "pki@example.com" ` -Subject "Certificato rinnovato: $($cert.Subject)" ` -Body "Il certificato Γ¨ stato rinnovato con successo." ` -SmtpServer $SmtpServer return $true } catch { Write-Log "ERROR: Rinnovo fallito - $_" Send-MailMessage -To $MailTo -From "pki@example.com" ` -Subject "ERRORE: Rinnovo certificato" ` -Body "Errore durante il rinnovo di $CertPath : $_" ` -SmtpServer $SmtpServer return $false } } # Logica principale Write-Log "=== Avvio verifica rinnovo ===" $certificates = @( @{ Cert = "C:\certs\webserver.pem"; Key = "C:\certs\webserver.key"; Service = "W3SVC" }, @{ Cert = "C:\certs\api.pem"; Key = "C:\certs\api.key"; Service = "APIService" } ) foreach ($certConfig in $certificates) { $status = Test-CertificateExpiry -CertPath $certConfig.Cert -DaysThreshold $RenewalDays Write-Log "INFO: $($certConfig.Cert) - $($status.DaysRemaining) giorni rimanenti" if ($status.NeedsRenewal) { Invoke-CertificateRenewal ` -CertPath $certConfig.Cert ` -KeyPath $certConfig.Key ` -ServiceName $certConfig.Service } } Write-Log "=== Verifica rinnovo terminata ===" **Configurazione Task Scheduler:** # Creazione task $action = New-ScheduledTaskAction -Execute "PowerShell.exe" ` -Argument "-ExecutionPolicy Bypass -File %SCRIPTS_PATH%\Cert-Renewal.ps1" $trigger = New-ScheduledTaskTrigger -Daily -At "03:00" $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd Register-ScheduledTask -TaskName "Certificate Renewal" ` -Action $action ` -Trigger $trigger ` -Principal $principal ` -Settings $settings ---- ===== Automazione Ansible ===== # cert-renewal.yml --- - name: Certificate Renewal hosts: all become: yes vars: renewal_days: 30 ca_server: "https://ca.internal.example.com" tasks: - name: Check certificate expiry community.crypto.x509_certificate_info: path: "{{ item.cert }}" register: cert_info loop: "{{ certificates }}" - name: Create CSR for expiring certificates community.crypto.openssl_csr: path: "/tmp/{{ item.item.name }}.csr" privatekey_path: "{{ item.item.key }}" common_name: "{{ item.subject.commonName }}" subject_alt_name: "{{ item.subject_alt_name | default(omit) }}" when: (item.not_after | to_datetime('%Y%m%d%H%M%SZ') - ansible_date_time.iso8601 | to_datetime('%Y-%m-%dT%H:%M:%SZ')).days < renewal_days loop: "{{ cert_info.results }}" - name: Request new certificate from CA uri: url: "{{ ca_server }}/api/sign" method: POST body_format: raw body: "{{ lookup('file', '/tmp/' + item.item.name + '.csr') }}" headers: Authorization: "Bearer {{ ca_token }}" return_content: yes register: new_cert when: (item.not_after | to_datetime('%Y%m%d%H%M%SZ') - ansible_date_time.iso8601 | to_datetime('%Y-%m-%dT%H:%M:%SZ')).days < renewal_days loop: "{{ cert_info.results }}" - name: Deploy new certificate copy: content: "{{ item.content }}" dest: "{{ item.item.item.cert }}" backup: yes notify: Reload services when: item.content is defined loop: "{{ new_cert.results }}" handlers: - name: Reload services service: name: "{{ item.item.item.service }}" state: reloaded loop: "{{ new_cert.results }}" when: item.changed # Inventory (group_vars/all.yml) certificates: - name: webserver cert: /etc/ssl/certs/webserver.pem key: /etc/ssl/private/webserver.key service: nginx - name: api cert: /etc/ssl/certs/api.pem key: /etc/ssl/private/api.key service: api-service ---- ===== Integrazione monitoraggio ===== # Prometheus Node Exporter Textfile # /var/lib/node_exporter/textfile_collector/cert_expiry.prom for cert in /etc/ssl/certs/*.pem; do expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2) if [ -n "$expiry" ]; then expiry_epoch=$(date -d "$expiry" +%s) name=$(basename "$cert" .pem) echo "cert_expiry_seconds{cert=\"$name\"} $expiry_epoch" fi done ---- ===== Checklist ===== | # | Punto di verifica | βœ“ | |---|-------------------|---| | 1 | Script di rinnovo creato e testato | ☐ | | 2 | Cron/Timer/Task configurato | ☐ | | 3 | Autenticazione CA configurata | ☐ | | 4 | Backup prima del rinnovo | ☐ | | 5 | Ricarica servizio funzionante | ☐ | | 6 | Notifica in caso di errore | ☐ | | 7 | Logging attivato | ☐ | ---- ===== Documentazione correlata ===== * [[..:tagesgeschaeft:zertifikat-erneuern|Rinnovo manuale]] – Per casi speciali * [[.:acme-integration|Integrazione ACME]] – Per certificati pubblici * [[..:monitoring:ablauf-monitoring|Monitoraggio scadenze]] – Sorveglianza ---- << [[.:cert-manager-k8s|← Kubernetes Cert-Manager]] | [[..:start|β†’ Scenari per operatori]] >> ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional// {{tag>scheduled renewal cron systemd powershell ansible automazione operator}}