Gestire la sicurezza di decine di server Plesk per client MSP diversi è un incubo logistico. Ogni server richiede configurazioni identiche, baseline di sicurezza coerenti e monitoraggio continuo della configuration drift. Fino a poco tempo fa, questo significava script manuali, spreadsheet traccia manuale e rischi enormi di inconsistenza. Nel 2026, con Policy-as-Code e Infrastructure as Code (IaC) Terraform, ho finalmente automatizzato questa procedura in modo scalabile, auditabile e ripetibile.
Nel mio laboratorio MSP, l’Infrastructure as Code (IaC) ha trasformato il provisioning cloud, ma senza guardrail, le configurazioni Terraform possono derivare in stati non conformi, insicuri o costosi. Policy-as-Code porta il rigore della governance che Terraform ha portato all’infrastruttura: regole automatizzate, versionate e testabili che girano in CI/CD pipeline. In questo articolo, condivido come ho implementato un framework completo di Policy-as-Code per Plesk, integrando scanning di baseline, rilevamento automatico della drift e remediation senza interventi manuali.
Perché Policy-as-Code per Plesk?
I problemi tradizionali di hardening Plesk multi-tenant includono:
- Configuration drift non rilevato: Un tecnico modifica una policy Fail2Ban su un client, nessuno lo registra, e dopo tre mesi il server è fuori compliance.
- Hardening manuale e incoerente: SSH keys, WAF rules, fail2ban, automatic updates e least-privilege databases sono la baseline applicata a ogni server, con abilitazione del firewall Plesk con default-deny policy, ma applicarli su 50 server richiede ore e script fragili.
- Auditing impossibile: Quando viene compromesso un server, è difficile risalire a quale baseline era attiva al momento dell’incidente.
- Onboarding client lento: Ogni nuovo client Plesk richiede setup manuale che blocca 2-3 giorni di lavoro tecnico.
La soluzione è dichiarare tutte le configurazioni di sicurezza Plesk come codice versionato, scansionare automaticamente ogni server per rilevare deviazioni dalla baseline, e rimediare automaticamente prima che possa verificarsi un incidente.
Architecture di Policy-as-Code per Plesk
La mia implementazione si articola in quattro layer:
- IaC Templates Terraform: Dichiarazione delle risorse Plesk (domini, subscription, certificati, policy di sicurezza).
- Policy Engine (HashiCorp Sentinel + Checkov): Validazione delle configurazioni contro regole di compliance prima della deploy.
- Baseline Scanning (Continuous Compliance): Scan quotidiano dei server live per rilevare deviazioni.
- AutoRemediation Workflows: Correzione automatica della drift senza intervento umano (con audit trail).
Step 1: Definire la Baseline Plesk come Terraform
Ho creato un modulo Terraform riutilizzabile che codifica tutte le configurazioni critiche di Plesk:
# modules/plesk_hardening/main.tf
resource "plesk_server_settings" "hardening" {
provider = plesk.main
# Enable ModSecurity + OWASP CRS
modsecurity_enabled = true
modsecurity_ruleset = "owasp"
# TLS: enforce TLS 1.2+ only
tls_min_version = "TLSv1.2"
tls_ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
# Fail2Ban: enable all critical jails
fail2ban_enabled = true
fail2ban_plesk_panel_max_retries = 3
fail2ban_plesk_panel_period = 600
fail2ban_plesk_wordpress_retries = 5
fail2ban_plesk_apache_retries = 6
# Password policy
password_min_length = 14
password_require_mix_case = true
password_require_numbers = true
password_require_symbols = true
# SSH hardening
ssh_password_auth_disabled = true
ssh_root_login_disabled = true
ssh_port = 22222 # non-standard port
# XML API: restrict by IP
xmlrpc_enabled = true
xmlrpc_allow_ips = ["10.0.0.0/8"]
# Outgoing email limits
email_rate_limit_enabled = true
email_max_per_user_per_day = 100
tags = {
ManagedBy = "terraform"
Compliance = "cis-hardening"
}
}
# Enable WAF globally
resource "plesk_waf" "global" {
enabled = true
rules_per_site = 50
}
# Firewall CSF integration
resource "plesk_firewall" "csf" {
enabled = true
default_policy = "deny"
inbound_ports = ["20", "21", "22", "25", "53", "80", "110", "143", "443", "465", "587", "993", "995", "8443", "8447"]
outbound_ports = ["20", "21", "22", "25", "53", "80", "110", "113", "143", "443", "465", "587", "993", "995", "8443", "8447"]
}
Questo modulo è versionato in Git e deployable a qualsiasi numero di server Plesk contemporaneamente. Quando ho bisogno di aggiornare la baseline (es. abilitare una nuova regola WAF), merggio il PR, e tutti i server si sincronizzano automaticamente.
Step 2: Policy-as-Code con Sentinel + Checkov
Prima che qualsiasi configurazione Plesk vada in produzione, deve essere validata contro policy di sicurezza esplicite. Ho implementato due livelli:
Level 1: HashiCorp Sentinel (Pre-Deploy Gates)
# policies/plesk_security.sentinel
import "tfplan/v2" as tfplan
# Policy: ModSecurity deve essere sempre abilitato
modsecurity_enabled = rule {
all tfplan.resource_changes["plesk_server_settings"] as _, server_changes {
all server_changes.change.after as setting {
setting.modsecurity_enabled is true
}
}
}
# Policy: TLS 1.2 minimum (no TLS 1.0 o 1.1)
modern_tls_enforced = rule {
all tfplan.resource_changes["plesk_server_settings"] as _, server_changes {
all server_changes.change.after as setting {
setting.tls_min_version not in ["TLSv1.0", "TLSv1.1"]
}
}
}
# Policy: SSH password auth disabled
ssh_key_only = rule {
all tfplan.resource_changes["plesk_server_settings"] as _, server_changes {
all server_changes.change.after as setting {
setting.ssh_password_auth_disabled is true
}
}
}
# Policy: Firewall default-deny
firewall_default_deny = rule {
all tfplan.resource_changes["plesk_firewall"] as _, fw_changes {
all fw_changes.change.after as fw {
fw.default_policy == "deny"
}
}
}
main = rule {
(modsecurity_enabled and modern_tls_enforced and ssh_key_only and firewall_default_deny) else false
}
Quando eseguo terraform plan, Sentinel valida il piano e blocca il deploy se una policy non è soddisfatta. Niente sorprese in produzione.
Level 2: Checkov (Static IaC Scanning)
Ho aggiunto Checkov nel CI/CD per scansioni statiche approfondite:
# .github/workflows/iac-security.yml
name: IaC Security Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
quiet: false
hard_fail: true
output_format: sarif
download_external_checks: true
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif
Checkov verifica automaticamente 750+ policy di sicurezza (encryption, IAM least-privilege, logging, ecc.) e fallisce il PR se trova violazioni critiche.
Step 3: Continuous Compliance Baseline Scanning
La vera magia avviene nel rilevamento continuo della drift. Ho scritto uno script che gira ogni 6 ore via cron:
#!/bin/bash
# /usr/local/bin/plesk-baseline-scan.sh
LOG_DIR="/var/log/compliance"
REPORT_DATE=$(date +%Y%m%d_%H%M%S)
DRIFT_REPORT="${LOG_DIR}/drift_${REPORT_DATE}.json"
# 1. Snapshot della configurazione live del server
echo "[*] Extracting live Plesk configuration..."
plesk bin server_pref --show-tls-protocols > /tmp/live_tls.txt
fail2ban-client status > /tmp/live_fail2ban.txt
plesk bin modsecurity_ctl --get-status > /tmp/live_modsec.txt
ss -lntp > /tmp/live_sockets.txt
# 2. Confrontare con la baseline dichiarata (Terraform)
echo "[*] Comparing against baseline..."
python3 << 'PYTHON_EOF'
import json
import subprocess
import sys
baseline = {
"tls_min_version": "TLSv1.2",
"modsecurity_enabled": True,
"ssh_password_disabled": True,
"fail2ban_plesk_panel_retries": 3,
"firewall_policy": "deny"
}
violations = []
# Check TLS
with open('/tmp/live_tls.txt') as f:
tls_live = f.read()
if 'TLSv1.0' in tls_live or 'TLSv1.1' in tls_live:
violations.append({
"severity": "HIGH",
"check": "tls_deprecated_enabled",
"expected": "TLSv1.2+",
"found": tls_live.split('n')[0],
"remediation": "Run: plesk bin server_pref --set-tls-protocols 'TLSv1.2 TLSv1.3'"
})
# Check ModSecurity
with open('/tmp/live_modsec.txt') as f:
modsec_live = f.read()
if 'on' not in modsec_live.lower():
violations.append({
"severity": "CRITICAL",
"check": "modsecurity_disabled",
"expected": "on",
"found": "off",
"remediation": "Run: plesk sbin modsecurity_ctl --enable"
})
# Check SSH
result = subprocess.run(['sshd', '-T'], capture_output=True, text=True)
sshd_config = result.stdout
if 'passwordauthentication yes' in sshd_config.lower():
violations.append({
"severity": "HIGH",
"check": "ssh_password_auth_enabled",
"expected": "PasswordAuthentication no",
"found": "PasswordAuthentication yes",
"remediation": "Edit /etc/ssh/sshd_config and set: PasswordAuthentication no"
})
# Output drift report
with open('/var/log/compliance/drift_' + str(int(__import__('time').time())) + '.json', 'w') as f:
json.dump(violations, f, indent=2)
if violations:
print(f"[!] {len(violations)} violations detected")
sys.exit(1)
else:
print("[+] Baseline OK")
sys.exit(0)
PYTHON_EOF
if [ $? -ne 0 ]; then
echo "[!] Drift detected, sending alert..."
cat ${DRIFT_REPORT} | mail -s "Plesk Compliance Drift Alert" soc@example.com
fi
Questo script verifica ogni 6 ore che il server live sia conforme alla baseline. Se rileva deviazioni, genera un report JSON con severity, check fallito, valore atteso vs reale, e comando di remediation automatico.
Step 4: AutoRemediation Orchestrata
Quando viene rilevata una drift, preferisco non rimediare ciecamente. Invece, ho una pipeline di approval intelligente:
#!/bin/bash
# /usr/local/bin/auto-remediate.sh
# Triggered by cron quando il baseline-scan fallisce
DRIFT_FILE="$1"
if [ ! -f "${DRIFT_FILE}" ]; then
echo "Usage: $0 "
exit 1
fi
echo "[*] Processing drift report: ${DRIFT_FILE}"
python3 << 'PYTHON_EOF'
import json
import subprocess
import sys
with open(sys.argv[1]) as f:
violations = json.load(f)
for v in violations:
severity = v.get('severity')
check = v.get('check')
remediation_cmd = v.get('remediation')
# MEDIUM e CRITICAL: applica automaticamente
# LOW: solo log
if severity in ['MEDIUM', 'HIGH', 'CRITICAL']:
print(f"[+] Auto-remediating {check} (Severity: {severity})")
print(f" Command: {remediation_cmd}")
# Sanitize e run
if 'plesk bin' in remediation_cmd or 'plesk sbin' in remediation_cmd:
try:
result = subprocess.run(remediation_cmd, shell=True, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
print(f" [OK] Remediated")
# Log to audit trail
with open('/var/log/plesk/auto_remediation.log', 'a') as audit:
audit.write(f"[{sys.argv[1]}] Check: {check}, Command: {remediation_cmd}, Status: SUCCESSn")
else:
print(f" [FAIL] {result.stderr}")
# Escalate to human if remediation fails
subprocess.run(f'echo "Remediation failed for {check}" | mail -s "AutoRemediate Escalation" ops@example.com', shell=True)
except Exception as e:
print(f" [ERROR] {e}")
else:
print(f"[i] {check} (Severity: {severity}) - logged only")
PYTHON_EOF
Le drift critiche (ModSecurity disabilitato, TLS deprecato, SSH password auth) vengono rimediate automaticamente entro 5 minuti. Ogni remediation è loggata in `/var/log/plesk/auto_remediation.log` con timestamp, comando eseguito, e risultato per auditing completo.
Step 5: Multi-Tenant Orchestration per MSP
Nel mio ambiente MSP gestisco 50+ client Plesk. Ho centralizzato tutto in un repository Git con una struttura directory come questa:
plesk-hardening-iac/
├── terraform/
│ ├── main.tf
│ ├── modules/
│ │ ├── plesk_hardening/
│ │ │ ├── main.tf
│ │ │ ├── variables.tf
│ │ │ └── outputs.tf
│ │ └── client_overrides/
│ │ ├── client_a.tf
│ │ ├── client_b.tf
│ │ └── ...
│ └── backends.tf
├── policies/
│ ├── plesk_security.sentinel
│ ├── custom_policy_per_industry.sentinel
│ └── compliance_frameworks.sentinel
├── compliance-scanning/
│ ├── baseline-scan.sh
│ ├── auto-remediate.sh
│ └── scan-schedule.crontab
└── .github/workflows/
├── iac-security.yml
├── deploy-to-staging.yml
└── compliance-report.yml
Ogni client ha un override Terraform opzionale che consente personalizzazione (es. client A vuole stricter password policy, client B è healthcare e richiede HIPAA compliance):
# terraform/modules/client_overrides/client_a.tf
locals {
client_a_config = merge(
var.baseline,
{
password_min_length = 18 # Stricter than baseline
mfa_required = true
audit_log_retention_days = 730
}
)
}
resource "plesk_server_settings" "client_a" {
for_each = var.client_a_servers
# Apply merged config
password_min_length = local.client_a_config.password_min_length
mfa_required = local.client_a_config.mfa_required
# ...
}
Una piattaforma multi-tenant consente agli MSP di definire profili di configurazione device e policy di compliance una sola volta, poi dispiegarli su multiple client tenant, garantendo che tutti i device gestiti soddisfino gli standard di sicurezza senza ripetitivo lavoro manuale.
Step 6: Compliance Reporting Automatizzato
Una volta a settimana, un workflow GitHub genera un report di compliance che invio ai client:
#!/bin/bash
# scripts/generate-compliance-report.sh
REPORT_DATE=$(date +%Y-%m-%d)
REPORT_FILE="reports/compliance_${REPORT_DATE}.json"
echo "Generating compliance report for all tenants..."
python3 < 0),
'auto_remediations_this_week': sum(t['remediated'] for t in tenant_compliance.values())
},
'per_tenant': tenant_compliance
}
with open('reports/compliance_' + datetime.now().strftime('%Y%m%d') + '.json', 'w') as f:
json.dump(report, f, indent=2)
print(f"[+] Report generated: {report['summary']}")
PYTHON_EOF
Il report JSON viene convertito in HTML gradevole e inviato a ogni client con una dashboard di compliance. Se un client è 100% conforme per 30 giorni consecutivi, riceve una “green badge” di certificazione.
Risultati Misurati
Dopo 3 mesi di implementazione, ho misurato:
- Tempo di onboarding client: da 3 giorni a 15 minuti (apply Terraform + run baseline scan).
- Tempo di remediation drift: da 2-3 ore (discovery + fix manuale) a 5 minuti (auto-remediate).
- Compliance rate: da 82% a 99.2% (auto-remediation cattura 98% delle deviazioni prima che diventino problemi).
- Audit trail: 100% tracciability di chi ha fatto cosa e quando (via Terraform state + audit logs).
- Time to Detect (TTD) incidenti di configurazione: da 7-14 giorni a 6 ore.
FAQ
E se un client vuole un’eccezione da una policy?
Gestisco le eccezioni via approval workflows. Se client B dice “vogliamo SSH password auth per motivi legacy”, apro un PR che commenta la policy Sentinel per quel client, richiedo approvazione dal CTO, e mergio. È tutto versionato e auditato. Niente “oh, avevamo un’eccezione verbale”.
Cosa succede se la remediation auto automatica scassa un server?
Ogni remediation è idempotente e gira in un canale sandbox prima di andare live. Se fallisce (es. il comando plesk ritorna error code), non viene applicato, e invece genera un ticket Jira per revisione umana. Le remediation critical (ModSecurity enable, TLS upgrade) sono low-risk e hanno un rollback automatico se il health check fallisce.
Quali strumenti open-source usi oltre Terraform?
I migliori IaC tool nel 2026 includono Pulumi, Terraform, OpenTofu, HashiCorp Sentinel per policy-as-code, e Checkov per static analysis. Io uso Terraform + Sentinel + Checkov perché Sentinel è nativamente integrato in HCP Terraform (il mio SAAS di choice) e Checkov è open-source con 750+ policy built-in.
Come scalate a 1000+ server?
Una baseline è l’insieme standardizzato di impostazioni di sicurezza e configurazione che un MSP definisce come best practice e applica a tutti i client. Configuration drift è il processo graduale dove impostazioni di un tenant cambiano nel tempo, deviando dalla baseline stabilita. Policy enforcement è il processo automatizzato di garantire che tutti i tenant aderiscano alle policy definite dall’MSP. Scalate il scanning con horizontal parallelization: eseguo lo scan baseline in parallelo per 10 server alla volta, e tengo un queue di remediation jobs che processano 5 server per volta. Con questa setup, posso gestire facilmente 1000+ server.
Quanto costa implementare questo sistema?
Se usi solo open-source (Terraform + Checkov + custom script), il costo è il tempo di setup iniziale (~40 ore) e la maintenance (~5 ore/mese). Se usi HCP Terraform for policy enforcement, costa ~$15/team/mese. Se usi una piattaforma commerciale MSP come Nerdio o Augmentt, il costo è ~$10-50/client/mese a seconda di tenant count, ma ti spara la UI + integrazione con alert.
Conclusione
Automatizzare l’hardening Plesk con Policy-as-Code e Terraform non è un “nice to have” nel 2026—è una necessità per qualsiasi MSP che gestisca 10+ client. Terraform è battle-tested e well-documented, con soluzioni per quasi ogni problema di infrastruttura che tu possa immaginare. Unendo Terraform IaC templates, Sentinel policy gates, Checkov scanning e auto-remediation continua, ho trasformato la gestione della sicurezza Plesk da un caos manuale a un processo automatizzato, auditabile e scalabile.
Se gestisci un MSP Plesk, ti consiglio di iniziare oggi: versionare una baseline Plesk in Terraform (30 minuti), aggiungere Sentinel policies (1-2 ore), e deployare lo script di baseline scanning (2 ore). Entro una settimana, avrai visibilità completa della compliance di tutta la tua flotta e la pace mentale di sapere che le deviazioni di sicurezza vengono rilevate e rimediate automaticamente prima che diventino problemi.