npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE ATLAS
When to Use
- Designing backup architecture that withstands ransomware encryption and deletion attempts
- Migrating from traditional backup to ransomware-resilient backup with immutable storage
- Establishing RPO/RTO targets for critical systems and validating them through restore testing
- Isolating backup credentials and infrastructure from the production Active Directory domain
- Meeting cyber insurance requirements for backup resilience and tested recovery capabilities
Do not use as a substitute for endpoint protection, network segmentation, or incident response planning. Backups are a last line of defense, not a primary prevention control.
Prerequisites
- Inventory of critical systems, applications, and data classified by business impact (Tier 1/2/3)
- Defined RPO (Recovery Point Objective) and RTO (Recovery Time Objective) per tier
- Backup software supporting immutable repositories (Veeam 12+, Commvault, Rubrik, Cohesity)
- Isolated backup network segment or air-gapped storage infrastructure
- Separate backup admin credentials not joined to the production AD domain
Workflow
Step 1: Classify Assets and Define Recovery Objectives
Map all systems into recovery tiers based on business impact:
| Tier | Examples | RPO | RTO | Backup Frequency |
|---|---|---|---|---|
| Tier 1 (Critical) | Domain controllers, ERP, databases | 1 hour | 4 hours | Hourly incremental, daily full |
| Tier 2 (Important) | File servers, email, web apps | 4 hours | 12 hours | Every 4 hours incremental, daily full |
| Tier 3 (Standard) | Dev environments, archives | 24 hours | 48 hours | Daily incremental, weekly full |
Document dependencies between systems. Domain controllers and DNS must recover before application servers. Database servers before application tiers.
Step 2: Implement 3-2-1-1-0 Architecture
Configure backup storage following the extended 3-2-1-1-0 rule:
Copy 1 - Primary backup on local storage:
# Veeam backup job targeting local repository
# Fast restore for operational recovery
Backup Repository: Local NAS (CIFS/NFS) or SAN
Retention: 14 days of restore points
Encryption: AES-256 with password not stored in ADCopy 2 - Secondary backup on different media:
# Replicate to secondary site or cloud
# Veeam Backup Copy Job or Scale-Out Backup Repository
Target: AWS S3 / Azure Blob / Wasabi / tape library
Retention: 30 days
Transfer: Encrypted TLS 1.2+ in transitCopy 3 - Offsite copy:
# Geographically separated from primary and secondary
# Cloud object storage in different region or physical tape rotation
Target: Cross-region cloud storage or Iron Mountain tape vaulting
Retention: 90 days+1 - Immutable or air-gapped copy:
# Cannot be modified or deleted for defined retention period
# Veeam Hardened Repository on Linux with immutable flag
# Or AWS S3 Object Lock in Compliance mode
# Or physical air-gapped tape+0 - Zero errors on restore verification:
# Automated restore testing using Veeam SureBackup or equivalent
# Scheduled weekly for Tier 1, monthly for Tier 2/3
# Verify boot, network connectivity, and application healthStep 3: Isolate Backup Credentials
Ransomware operators target backup infrastructure by compromising backup admin credentials through Active Directory:
- Separate backup admin accounts from the production AD domain. Use local accounts on backup servers or a dedicated backup management domain.
- Dedicated backup network segment with firewall rules allowing only backup traffic (specific ports, specific source/destination IPs).
- MFA on backup console access using hardware tokens or authenticator apps, not SMS.
- Disable RDP on backup servers. Use out-of-band management (iLO/iDRAC/IPMI) for emergency access.
- Remove backup servers from domain or place in a dedicated OU with restricted GPO inheritance.
# Linux Hardened Repository - disable SSH password auth
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
# Set immutable flag on backup files (XFS filesystem)
sudo chattr +i /mnt/backup/repository/*
# Veeam Hardened Repository uses single-use credentials
# that are not stored on the Veeam server after initial setupStep 4: Configure Immutable Storage
Veeam Hardened Linux Repository:
# Minimal Ubuntu 22.04 LTS installation
# No GUI, no unnecessary services
# Veeam uses temporary SSH credentials during backup window only
# Configure XFS with reflink support
sudo mkfs.xfs -b size=4096 -m reflink=1 /dev/sdb1
sudo mount /dev/sdb1 /mnt/veeam-repo
# Create dedicated Veeam user with limited permissions
sudo useradd -m -s /bin/bash veeamuser
sudo mkdir -p /mnt/veeam-repo/backups
sudo chown veeamuser:veeamuser /mnt/veeam-repo/backupsAWS S3 Object Lock (Compliance Mode):
# Create bucket with Object Lock enabled
aws s3api create-bucket \
--bucket company-immutable-backups \
--object-lock-enabled-for-bucket \
--region us-east-1
# Set default retention - 30 days compliance mode
aws s3api put-object-lock-configuration \
--bucket company-immutable-backups \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "COMPLIANCE",
"Days": 30
}
}
}'Azure Immutable Blob Storage:
# Create storage account with immutable storage
az storage container immutability-policy create \
--account-name backupaccount \
--container-name immutable-backups \
--period 30
# Lock the policy (irreversible)
az storage container immutability-policy lock \
--account-name backupaccount \
--container-name immutable-backupsStep 5: Automate Restore Testing
Configure automated restore verification on a recurring schedule:
# Veeam SureBackup verification job (PowerShell)
# Tests VM boot, network ping, and application health
Add-PSSnapin VeeamPSSnapin
$backupJob = Get-VBRJob -Name "Tier1-DailyBackup"
$sureBackupJob = Get-VSBJob -Name "Tier1-RestoreTest"
# Verify last restore test completed successfully
$lastSession = Get-VSBSession -Job $sureBackupJob -Last
if ($lastSession.Result -ne "Success") {
Send-MailMessage -To "backup-team@company.com" `
-Subject "ALERT: SureBackup verification failed" `
-Body "Tier 1 restore test failed. Last result: $($lastSession.Result)" `
-SmtpServer "smtp.company.com"
}Document restore test results and maintain a recovery runbook with step-by-step procedures for each tier.
Key Concepts
| Term | Definition |
|---|---|
| 3-2-1-1-0 | Extended backup rule: 3 copies, 2 media types, 1 offsite, 1 immutable/air-gapped, 0 restore verification errors |
| RPO | Recovery Point Objective: maximum acceptable data loss measured in time (e.g., 1 hour RPO means max 1 hour of data loss) |
| RTO | Recovery Time Objective: maximum acceptable downtime before system must be operational |
| Immutable Backup | Backup copy that cannot be modified, encrypted, or deleted for a defined retention period, even by administrators |
| Air-Gapped Backup | Physically isolated backup with no network connectivity to production systems, providing strongest ransomware protection |
| Hardened Repository | Linux-based backup storage with minimal attack surface, no persistent SSH, and immutable file flags |
Tools & Systems
- Veeam Backup & Replication 12: Enterprise backup with Hardened Linux Repository, SureBackup verification, and immutable backup support
- Rubrik Security Cloud: Zero-trust backup platform with immutable snapshots, anomaly detection, and air-gapped recovery
- Commvault: Backup with Metallic air-gap protection, anomaly detection, and automated recovery orchestration
- AWS S3 Object Lock: Cloud-native immutable storage in Compliance or Governance mode for backup copies
- Cohesity DataProtect: Backup platform with DataLock immutability, anti-ransomware detection, and instant mass restore
Common Scenarios
Scenario: Financial Services Firm Implementing Ransomware-Resilient Backup
Context: A mid-size bank with 500 servers, 200TB of data, and regulatory requirements for 7-year retention must redesign backup after a peer institution was hit by ransomware. Current backups use a single Veeam repository on a Windows server joined to the production domain.
Approach:
- Classify all 500 servers into three tiers: 50 Tier 1 (core banking, AD, DNS), 200 Tier 2 (email, file shares, web), 250 Tier 3 (dev, test, archive)
- Deploy Veeam Hardened Linux Repository on dedicated Ubuntu 22.04 servers with XFS immutability for primary backup
- Configure S3 Object Lock in Compliance mode for 30-day immutable cloud copy with Veeam Scale-Out Repository capacity tier
- Establish quarterly tape rotation to Iron Mountain for 7-year regulatory retention
- Remove all backup servers from the production AD domain and create isolated backup admin accounts with hardware MFA tokens
- Deploy SureBackup jobs: weekly for Tier 1, monthly for Tier 2, quarterly for Tier 3
- Conduct annual full recovery drill restoring AD, DNS, core banking, and dependent applications to validate documented RTO
Pitfalls:
- Leaving backup admin credentials in the production AD domain where ransomware operators can compromise them via Kerberoasting or DCSync
- Configuring immutable retention periods shorter than the dwell time of typical ransomware (average 21 days), allowing attackers to wait for immutability to expire
- Testing only individual VM restores without testing full application stack recovery including dependencies
- Forgetting to back up backup server configuration (Veeam config database, encryption keys) separately from the backup infrastructure itself
Output Format
## Ransomware Backup Strategy Assessment
**Organization**: [Name]
**Assessment Date**: [Date]
**Assessor**: [Name]
### Current State
- Backup Solution: [Product/Version]
- Copies: [Number and locations]
- Immutable Copy: [Yes/No - Details]
- Air-Gapped Copy: [Yes/No - Details]
- Credential Isolation: [Yes/No - Details]
- Last Restore Test: [Date - Result]
### Gap Analysis
| Control | Current | Target | Gap | Priority |
|---------|---------|--------|-----|----------|
| Immutable backup | None | S3 Object Lock + Linux Hardened Repo | Missing | Critical |
| Credential isolation | Domain-joined | Standalone local accounts + MFA | Partial | Critical |
| Restore testing | Ad-hoc manual | Automated weekly SureBackup | Missing | High |
### Recommendations
1. [Priority] [Recommendation] - [Estimated effort]
2. ...
### Recovery Tier Summary
| Tier | Systems | RPO | RTO | Backup Schedule | Restore Test Frequency |
|------|---------|-----|-----|-----------------|----------------------|
| 1 | 50 | 1hr | 4hr | Hourly inc/Daily full | Weekly |
| 2 | 200 | 4hr | 12hr | 4hr inc/Daily full | Monthly |
| 3 | 250 | 24hr | 48hr | Daily inc/Weekly full | Quarterly |References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md6.5 KB
API Reference: Ransomware Backup Strategy Audit
Libraries Used
| Library | Purpose |
|---|---|
boto3 |
AWS SDK for S3, AWS Backup, and IAM auditing |
json |
Parse backup policies and compliance data |
subprocess |
Run local backup verification commands |
datetime |
Calculate backup age and RPO/RTO compliance |
Installation
pip install boto3Authentication
import boto3
import os
session = boto3.Session(
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
region_name=os.environ.get("AWS_REGION", "us-east-1"),
)
s3 = session.client("s3")
backup = session.client("backup")
iam = session.client("iam")AWS S3 Backup Audit
Check Bucket Versioning (Ransomware Recovery)
def audit_s3_versioning():
findings = []
buckets = s3.list_buckets()["Buckets"]
for bucket in buckets:
name = bucket["Name"]
versioning = s3.get_bucket_versioning(Bucket=name)
status = versioning.get("Status", "Disabled")
mfa_delete = versioning.get("MFADelete", "Disabled")
if status != "Enabled":
findings.append({
"bucket": name,
"issue": "Versioning not enabled",
"severity": "high",
"remediation": "Enable versioning for ransomware recovery",
})
if mfa_delete != "Enabled":
findings.append({
"bucket": name,
"issue": "MFA Delete not enabled",
"severity": "medium",
"remediation": "Enable MFA Delete to prevent bulk deletion",
})
return findingsCheck Object Lock (Immutable Backups)
def check_object_lock(bucket_name):
try:
config = s3.get_object_lock_configuration(Bucket=bucket_name)
lock = config["ObjectLockConfiguration"]
rule = lock.get("Rule", {}).get("DefaultRetention", {})
return {
"bucket": bucket_name,
"object_lock_enabled": lock.get("ObjectLockEnabled") == "Enabled",
"retention_mode": rule.get("Mode", "NONE"),
"retention_days": rule.get("Days", 0),
}
except s3.exceptions.ClientError:
return {"bucket": bucket_name, "object_lock_enabled": False}Check Cross-Region Replication
def check_cross_region_replication(bucket_name):
try:
repl = s3.get_bucket_replication(Bucket=bucket_name)
rules = repl["ReplicationConfiguration"]["Rules"]
return {
"bucket": bucket_name,
"replication_enabled": True,
"destinations": [
r["Destination"]["Bucket"] for r in rules if r["Status"] == "Enabled"
],
}
except s3.exceptions.ClientError:
return {"bucket": bucket_name, "replication_enabled": False}AWS Backup Service
List Backup Plans
def list_backup_plans():
plans = backup.list_backup_plans()["BackupPlansList"]
result = []
for plan in plans:
detail = backup.get_backup_plan(BackupPlanId=plan["BackupPlanId"])
rules = detail["BackupPlan"]["Rules"]
result.append({
"name": plan["BackupPlanName"],
"id": plan["BackupPlanId"],
"rules": [
{
"name": r["RuleName"],
"schedule": r.get("ScheduleExpression"),
"lifecycle_delete_days": r.get("Lifecycle", {}).get("DeleteAfterDays"),
"lifecycle_cold_days": r.get("Lifecycle", {}).get("MoveToColdStorageAfterDays"),
"target_vault": r["TargetBackupVaultName"],
}
for r in rules
],
})
return resultAudit Backup Vault Access Policy
def audit_vault_access(vault_name):
try:
policy = backup.get_backup_vault_access_policy(BackupVaultName=vault_name)
policy_doc = json.loads(policy["Policy"])
# Check for overly permissive policies
findings = []
for stmt in policy_doc.get("Statement", []):
if stmt.get("Effect") == "Allow" and stmt.get("Principal") == "*":
findings.append({
"vault": vault_name,
"issue": "Vault policy allows public access",
"severity": "critical",
})
return findings
except backup.exceptions.ClientError:
return [{"vault": vault_name, "issue": "No access policy set", "severity": "medium"}]List Recovery Points (Check Backup Freshness)
from datetime import datetime, timezone
def check_backup_freshness(vault_name, max_age_hours=24):
recovery_points = backup.list_recovery_points_by_backup_vault(
BackupVaultName=vault_name, MaxResults=100
)["RecoveryPoints"]
stale = []
for rp in recovery_points:
age = datetime.now(timezone.utc) - rp["CreationDate"]
if age.total_seconds() > max_age_hours * 3600:
stale.append({
"resource": rp["ResourceArn"],
"last_backup": rp["CreationDate"].isoformat(),
"age_hours": round(age.total_seconds() / 3600),
"status": rp["Status"],
})
return stale3-2-1 Backup Rule Audit
def audit_321_rule(bucket_name):
"""Verify the 3-2-1 backup rule: 3 copies, 2 media types, 1 offsite."""
versioning = s3.get_bucket_versioning(Bucket=bucket_name)
replication = check_cross_region_replication(bucket_name)
object_lock = check_object_lock(bucket_name)
score = {
"three_copies": versioning.get("Status") == "Enabled",
"two_media": replication["replication_enabled"],
"one_offsite": replication["replication_enabled"],
"immutable": object_lock["object_lock_enabled"],
}
score["compliant"] = all([score["three_copies"], score["two_media"], score["one_offsite"]])
return scoreOutput Format
{
"audit_date": "2025-01-15",
"backup_strategy": {
"total_buckets": 15,
"versioning_enabled": 12,
"object_lock_enabled": 5,
"cross_region_replication": 8,
"three_two_one_compliant": 4
},
"backup_plans": 3,
"recovery_points_stale": 2,
"findings": [
{
"resource": "critical-data-bucket",
"issue": "No Object Lock — vulnerable to ransomware deletion",
"severity": "high",
"remediation": "Enable S3 Object Lock in COMPLIANCE mode"
}
]
}standards.md2.5 KB
Standards & References - Ransomware Backup Strategy
Industry Standards
NIST SP 800-209: Security Guidelines for Storage Infrastructure
- Defines security controls for storage systems including backup infrastructure
- Covers access control, encryption, integrity verification, and audit logging for storage
- Section 5.3: Backup and recovery security controls
NIST IR 8374: Ransomware Risk Management
- Identifies backup as a critical control in the Recover function
- Recommends maintaining offline, encrypted backups with regular testing
- Emphasizes credential separation for backup administration
CISA #StopRansomware Guide (2023, updated 2025)
- Prescribes 3-2-1 backup rule as baseline, recommends extending to 3-2-1-1-0
- Mandates backup credential isolation from production domains
- Requires documented and tested recovery procedures
CIS Controls v8
- Control 11: Data Recovery
- 11.1: Establish and maintain a data recovery process
- 11.2: Perform automated backups
- 11.3: Protect recovery data (encryption, access control)
- 11.4: Establish and maintain an isolated instance of recovery data (air-gapped/immutable)
- 11.5: Test data recovery
ISO 27001:2022
- A.8.13: Information backup
- A.8.14: Redundancy of information processing facilities
Regulatory Requirements
PCI DSS v4.0
- Requirement 9.4.1: Backup media physically secured
- Requirement 12.10.1: Incident response plan includes recovery procedures
HIPAA Security Rule
- 45 CFR 164.308(a)(7): Contingency plan including data backup, disaster recovery, emergency mode operation
- 45 CFR 164.312(a)(2)(ii): Emergency access procedure
SOX
- Section 302/404: Internal controls over financial reporting must include IT controls for data backup and recovery
Vendor Documentation
Veeam
- Hardened Repository Guide: https://helpcenter.veeam.com/docs/backup/vsphere/hardened_repository.html
- SureBackup: https://helpcenter.veeam.com/docs/backup/vsphere/surebackup_job.html
- Immutability: https://helpcenter.veeam.com/docs/backup/vsphere/immutability.html
AWS
- S3 Object Lock: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html
- AWS Backup Vault Lock: https://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html
Azure
- Immutable Blob Storage: https://learn.microsoft.com/en-us/azure/storage/blobs/immutable-storage-overview
- Azure Backup Immutable Vault: https://learn.microsoft.com/en-us/azure/backup/backup-azure-immutable-vault-concept
workflows.md3.7 KB
Workflows - Ransomware Backup Strategy
Workflow 1: Initial Backup Architecture Design
Start
|
v
[Inventory all systems and data] --> Classify into Tier 1/2/3 by business impact
|
v
[Define RPO/RTO per tier] --> Document in recovery plan
|
v
[Select backup platform] --> Veeam / Rubrik / Commvault / Cohesity
|
v
[Design 3-2-1-1-0 architecture]
|-- Copy 1: Local repository (fast restore)
|-- Copy 2: Secondary site/cloud (different media)
|-- Copy 3: Offsite (geographic separation)
|-- +1: Immutable or air-gapped copy
|-- +0: Automated restore verification
|
v
[Isolate backup credentials]
|-- Remove from production AD
|-- Deploy MFA for backup admin access
|-- Segment backup network
|
v
[Configure immutable storage]
|-- Linux Hardened Repository (XFS immutability)
|-- S3 Object Lock / Azure Immutable Blob
|-- Tape air-gap rotation
|
v
[Set backup schedules per tier]
|
v
[Configure automated restore testing]
|-- SureBackup / SureReplica
|-- Verify boot, network, application health
|
v
[Document recovery runbook]
|
v
EndWorkflow 2: Restore Verification Process
Start (Scheduled - Weekly for Tier 1, Monthly for Tier 2)
|
v
[SureBackup job triggers VM restore to isolated sandbox]
|
v
[VM boots in isolated network segment]
|
v
[Heartbeat check] -- Fail --> Alert backup team
|
Pass
|
v
[Network ping check] -- Fail --> Alert backup team
|
Pass
|
v
[Application-specific check]
|-- AD: LDAP query test
|-- SQL: Database consistency check
|-- Web: HTTP 200 response
|-- Email: SMTP handshake
|
Fail --> Alert backup team with diagnostic details
|
Pass
|
v
[Log successful restore] --> Update compliance dashboard
|
v
[Clean up sandbox VMs]
|
v
EndWorkflow 3: Emergency Ransomware Recovery
Ransomware Incident Declared
|
v
[Isolate affected systems from network]
|
v
[Verify backup integrity]
|-- Check immutable copies are unaffected
|-- Validate backup timestamps predate infection
|-- Scan backup files for ransomware artifacts
|
v
[Determine recovery scope]
|-- Full environment rebuild vs. selective restore
|-- Prioritize by tier: AD/DNS first, then Tier 1, then Tier 2/3
|
v
[Rebuild infrastructure in clean environment]
|-- Deploy clean OS images
|-- Restore AD from immutable backup
|-- Validate AD integrity with ADRestore/DSInternals
|
v
[Restore applications in dependency order]
|-- Database servers before application servers
|-- Internal services before external-facing
|
v
[Validate restored systems]
|-- Application functionality testing
|-- Data integrity verification
|-- Security control validation
|
v
[Reconnect to network in phases]
|-- Monitor for re-infection indicators
|-- Validate no persistence mechanisms in restored systems
|
v
[Post-recovery documentation and lessons learned]
|
v
EndWorkflow 4: Backup Health Monitoring
Daily Automated Check
|
v
[Query backup job status via API/PowerShell]
|
v
[Check for failed or warning jobs]
|-- Failed --> Create P1 ticket, alert backup team
|-- Warning --> Create P3 ticket, investigate within 24hr
|-- Success --> Log and continue
|
v
[Verify backup repository capacity]
|-- >85% utilization --> Alert for capacity planning
|-- >95% utilization --> Critical alert, backup jobs at risk
|
v
[Check immutable copy synchronization]
|-- Verify last immutable copy is within RPO window
|-- Alert if immutable copy is stale
|
v
[Generate weekly backup health report]
|-- Success rate percentage
|-- Data protected volume
|-- Restore test results
|-- Capacity forecast
|
v
EndScripts 2
agent.py11.8 KB
#!/usr/bin/env python3
"""Ransomware backup strategy audit agent.
Audits backup infrastructure for ransomware resilience by checking
3-2-1 backup rule compliance, air-gapped/immutable backup presence,
backup encryption status, recovery point objectives (RPO), and
backup integrity verification schedules.
"""
import argparse
import json
import os
import subprocess
import sys
from datetime import datetime, timezone, timedelta
def check_veeam_backups(server_url, token):
"""Check Veeam backup status via REST API."""
try:
import requests
except ImportError:
return [{"check": "Veeam API", "status": "SKIP", "severity": "INFO",
"detail": "requests library not installed"}]
findings = []
headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
# Check backup jobs
try:
resp = requests.get(f"{server_url}/api/v1/jobs", headers=headers, timeout=30)
resp.raise_for_status()
jobs = resp.json().get("data", [])
for job in jobs:
job_name = job.get("name", "Unknown")
last_result = job.get("lastResult", "None")
schedule_enabled = job.get("scheduleEnabled", False)
if last_result == "Failed":
findings.append({
"check": f"Backup job: {job_name}",
"status": "FAIL",
"severity": "CRITICAL",
"detail": "Last backup failed",
})
elif not schedule_enabled:
findings.append({
"check": f"Backup job: {job_name}",
"status": "WARN",
"severity": "HIGH",
"detail": "Schedule disabled",
})
else:
findings.append({
"check": f"Backup job: {job_name}",
"status": "PASS",
"severity": "INFO",
"detail": f"Last result: {last_result}",
})
except Exception as e:
findings.append({"check": "Veeam job check", "status": "ERROR",
"severity": "HIGH", "detail": str(e)})
return findings
def check_restic_repository(repo_path, password_file=None):
"""Audit a Restic backup repository for integrity and freshness."""
findings = []
restic_bin = None
for name in ["restic", "restic.exe"]:
for d in os.environ.get("PATH", "").split(os.pathsep):
if os.path.isfile(os.path.join(d, name)):
restic_bin = os.path.join(d, name)
break
if not restic_bin:
findings.append({"check": "Restic binary", "status": "SKIP",
"severity": "INFO", "detail": "restic not found"})
return findings
env = dict(os.environ)
env["RESTIC_REPOSITORY"] = repo_path
if password_file:
env["RESTIC_PASSWORD_FILE"] = password_file
# Check snapshots
try:
result = subprocess.run(
[restic_bin, "snapshots", "--json"],
capture_output=True, text=True, timeout=120, env=env,
)
if result.returncode == 0:
snapshots = json.loads(result.stdout)
if not snapshots:
findings.append({"check": "Snapshots exist", "status": "FAIL",
"severity": "CRITICAL", "detail": "No snapshots found"})
else:
latest = max(snapshots, key=lambda s: s.get("time", ""))
latest_time = latest.get("time", "")[:19]
findings.append({"check": "Latest snapshot", "status": "PASS",
"severity": "INFO",
"detail": f"{latest_time} ({len(snapshots)} total)"})
try:
latest_dt = datetime.fromisoformat(latest_time.replace("Z", "+00:00"))
age_hours = (datetime.now(timezone.utc) - latest_dt).total_seconds() / 3600
if age_hours > 48:
findings.append({"check": "Backup freshness", "status": "FAIL",
"severity": "HIGH",
"detail": f"Latest backup is {age_hours:.0f}h old (>48h)"})
elif age_hours > 24:
findings.append({"check": "Backup freshness", "status": "WARN",
"severity": "MEDIUM",
"detail": f"Latest backup is {age_hours:.0f}h old (>24h)"})
except (ValueError, TypeError):
pass
else:
findings.append({"check": "Repository access", "status": "FAIL",
"severity": "CRITICAL", "detail": result.stderr[:200]})
except subprocess.TimeoutExpired:
findings.append({"check": "Repository access", "status": "FAIL",
"severity": "HIGH", "detail": "Command timed out"})
# Check repository integrity
try:
result = subprocess.run(
[restic_bin, "check", "--read-data-subset=1%"],
capture_output=True, text=True, timeout=300, env=env,
)
if result.returncode == 0:
findings.append({"check": "Repository integrity", "status": "PASS",
"severity": "INFO", "detail": "Integrity check passed"})
else:
findings.append({"check": "Repository integrity", "status": "FAIL",
"severity": "CRITICAL", "detail": "Integrity check failed"})
except subprocess.TimeoutExpired:
findings.append({"check": "Repository integrity", "status": "WARN",
"severity": "MEDIUM", "detail": "Integrity check timed out"})
return findings
def audit_321_rule(backup_config):
"""Audit compliance with the 3-2-1 backup rule."""
findings = []
copies = backup_config.get("copies", 0)
media_types = backup_config.get("media_types", [])
offsite_locations = backup_config.get("offsite_locations", [])
immutable = backup_config.get("immutable_backup", False)
air_gapped = backup_config.get("air_gapped", False)
# 3 copies
if copies >= 3:
findings.append({"check": "3-2-1: At least 3 copies", "status": "PASS",
"severity": "INFO", "detail": f"{copies} copies"})
else:
findings.append({"check": "3-2-1: At least 3 copies", "status": "FAIL",
"severity": "CRITICAL",
"detail": f"Only {copies} copies (need 3)"})
# 2 different media types
if len(media_types) >= 2:
findings.append({"check": "3-2-1: 2 different media types", "status": "PASS",
"severity": "INFO", "detail": ", ".join(media_types)})
else:
findings.append({"check": "3-2-1: 2 different media types", "status": "FAIL",
"severity": "HIGH",
"detail": f"Only {len(media_types)} type(s): {', '.join(media_types)}"})
# 1 offsite copy
if offsite_locations:
findings.append({"check": "3-2-1: 1 offsite copy", "status": "PASS",
"severity": "INFO", "detail": ", ".join(offsite_locations)})
else:
findings.append({"check": "3-2-1: 1 offsite copy", "status": "FAIL",
"severity": "CRITICAL", "detail": "No offsite backup"})
# Immutable backup (ransomware protection)
if immutable:
findings.append({"check": "Immutable backup", "status": "PASS",
"severity": "INFO", "detail": "WORM/immutable storage enabled"})
else:
findings.append({"check": "Immutable backup", "status": "FAIL",
"severity": "CRITICAL",
"detail": "No immutable backup - vulnerable to ransomware encryption"})
# Air-gapped backup
if air_gapped:
findings.append({"check": "Air-gapped backup", "status": "PASS",
"severity": "INFO", "detail": "Offline/air-gapped copy exists"})
else:
findings.append({"check": "Air-gapped backup", "status": "WARN",
"severity": "HIGH",
"detail": "No air-gapped backup - consider tape or offline storage"})
# Encryption
if backup_config.get("encrypted", False):
findings.append({"check": "Backup encryption", "status": "PASS",
"severity": "INFO"})
else:
findings.append({"check": "Backup encryption", "status": "FAIL",
"severity": "HIGH", "detail": "Backups are not encrypted"})
return findings
def format_summary(all_findings):
"""Print audit summary."""
print(f"\n{'='*60}")
print(f" Ransomware Backup Strategy Audit")
print(f"{'='*60}")
severity_counts = {}
for f in all_findings:
sev = f.get("severity", "INFO")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
pass_count = sum(1 for f in all_findings if f.get("status") == "PASS")
fail_count = sum(1 for f in all_findings if f.get("status") == "FAIL")
warn_count = sum(1 for f in all_findings if f.get("status") == "WARN")
print(f" Checks : {len(all_findings)}")
print(f" Passed : {pass_count}")
print(f" Failed : {fail_count}")
print(f" Warnings : {warn_count}")
print(f"\n By Severity:")
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"]:
count = severity_counts.get(sev, 0)
if count > 0:
print(f" {sev:10s}: {count}")
print(f"\n Detailed Results:")
for f in all_findings:
status_icon = "OK" if f["status"] == "PASS" else "!!" if f["status"] == "FAIL" else "~~"
detail = f.get("detail", "")
print(f" [{status_icon}] [{f['severity']:8s}] {f['check']}"
+ (f": {detail}" if detail else ""))
return severity_counts
def main():
parser = argparse.ArgumentParser(
description="Ransomware backup strategy audit agent"
)
parser.add_argument("--config", help="Backup configuration JSON file")
parser.add_argument("--restic-repo", help="Restic repository path to audit")
parser.add_argument("--restic-password-file", help="Restic password file")
parser.add_argument("--veeam-url", help="Veeam server URL")
parser.add_argument("--veeam-token", help="Veeam API token")
parser.add_argument("--output", "-o", help="Output JSON report path")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
all_findings = []
if args.config:
with open(args.config, "r") as f:
backup_config = json.load(f)
all_findings.extend(audit_321_rule(backup_config))
if args.restic_repo:
all_findings.extend(check_restic_repository(args.restic_repo, args.restic_password_file))
if args.veeam_url and args.veeam_token:
all_findings.extend(check_veeam_backups(args.veeam_url, args.veeam_token))
if not all_findings:
print("[!] No audit sources specified. Use --config, --restic-repo, or --veeam-url.",
file=sys.stderr)
parser.print_help()
sys.exit(1)
severity_counts = format_summary(all_findings)
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"tool": "Ransomware Backup Audit",
"findings": all_findings,
"severity_counts": severity_counts,
"risk_level": (
"CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
else "HIGH" if severity_counts.get("HIGH", 0) > 0
else "MEDIUM" if severity_counts.get("MEDIUM", 0) > 0
else "LOW"
),
}
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"\n[+] Report saved to {args.output}")
elif args.verbose:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()
process.py22.3 KB
#!/usr/bin/env python3
"""
Ransomware Backup Strategy Assessment and Monitoring Tool
Audits backup infrastructure for ransomware resilience by checking:
- 3-2-1-1-0 compliance (copies, media types, offsite, immutable, restore testing)
- Backup credential isolation from production AD
- Immutable storage configuration
- Restore test history and success rates
- RPO/RTO compliance per tier
Supports Veeam, AWS Backup, and Azure Backup via API integration.
"""
import json
import subprocess
import sys
import os
import socket
import ssl
import datetime
from dataclasses import dataclass, field, asdict
from typing import Optional
from pathlib import Path
@dataclass
class BackupCopy:
location: str
media_type: str
is_offsite: bool
is_immutable: bool
is_airgapped: bool
retention_days: int
last_successful: Optional[str] = None
encryption_algorithm: Optional[str] = None
@dataclass
class RecoveryTier:
name: str
tier_level: int
systems: list
rpo_hours: float
rto_hours: float
backup_frequency: str
restore_test_frequency: str
last_restore_test: Optional[str] = None
last_restore_result: Optional[str] = None
@dataclass
class BackupAssessment:
organization: str
assessment_date: str
backup_solution: str
copies: list = field(default_factory=list)
tiers: list = field(default_factory=list)
credential_isolation: dict = field(default_factory=dict)
findings: list = field(default_factory=list)
score: float = 0.0
class BackupStrategyAuditor:
"""Audits backup infrastructure for ransomware resilience."""
def __init__(self, org_name: str, backup_solution: str):
self.assessment = BackupAssessment(
organization=org_name,
assessment_date=datetime.datetime.now().strftime("%Y-%m-%d"),
backup_solution=backup_solution,
)
self.max_score = 0
self.earned_score = 0
def add_backup_copy(self, copy: BackupCopy):
self.assessment.copies.append(copy)
def add_recovery_tier(self, tier: RecoveryTier):
self.assessment.tiers.append(tier)
def set_credential_isolation(
self,
domain_joined: bool,
mfa_enabled: bool,
separate_network: bool,
rdp_disabled: bool,
dedicated_admin_accounts: bool,
):
self.assessment.credential_isolation = {
"domain_joined": domain_joined,
"mfa_enabled": mfa_enabled,
"separate_network": separate_network,
"rdp_disabled": rdp_disabled,
"dedicated_admin_accounts": dedicated_admin_accounts,
}
def _add_finding(self, severity: str, category: str, title: str, detail: str, recommendation: str):
self.assessment.findings.append({
"severity": severity,
"category": category,
"title": title,
"detail": detail,
"recommendation": recommendation,
})
def audit_321_10_compliance(self):
"""Check compliance with the 3-2-1-1-0 backup rule."""
copies = self.assessment.copies
# 3 - At least 3 copies
self.max_score += 20
if len(copies) >= 3:
self.earned_score += 20
else:
self._add_finding(
"CRITICAL", "3-2-1-1-0",
f"Insufficient backup copies: {len(copies)} of 3 required",
f"Only {len(copies)} backup copies configured. Minimum 3 required.",
"Add additional backup copies to meet 3-2-1-1-0 minimum.",
)
# 2 - At least 2 different media types
self.max_score += 15
media_types = set(c.media_type for c in copies)
if len(media_types) >= 2:
self.earned_score += 15
else:
self._add_finding(
"HIGH", "3-2-1-1-0",
f"Insufficient media diversity: {len(media_types)} type(s)",
f"All backups use {media_types}. Need at least 2 different media types.",
"Add backup copy on different media (e.g., tape, cloud object storage, SAN).",
)
# 1 - At least 1 offsite copy
self.max_score += 15
offsite_copies = [c for c in copies if c.is_offsite]
if offsite_copies:
self.earned_score += 15
else:
self._add_finding(
"CRITICAL", "3-2-1-1-0",
"No offsite backup copy",
"All backup copies are stored on-premises. A site-level disaster would destroy all copies.",
"Configure at least one offsite backup copy (cloud, remote site, or tape vaulting).",
)
# 1 - At least 1 immutable or air-gapped copy
self.max_score += 25
immutable_copies = [c for c in copies if c.is_immutable or c.is_airgapped]
if immutable_copies:
self.earned_score += 25
else:
self._add_finding(
"CRITICAL", "3-2-1-1-0",
"No immutable or air-gapped backup copy",
"No backup copies are protected against modification or deletion. "
"Ransomware operators routinely delete accessible backups.",
"Deploy immutable storage (S3 Object Lock, Hardened Linux Repository) "
"or air-gapped backup (tape with physical separation).",
)
# 0 - Restore testing with zero errors
self.max_score += 25
if self.assessment.tiers:
tested_tiers = [t for t in self.assessment.tiers if t.last_restore_result == "Success"]
all_tiers = len(self.assessment.tiers)
if len(tested_tiers) == all_tiers:
self.earned_score += 25
elif tested_tiers:
partial_score = int(25 * len(tested_tiers) / all_tiers)
self.earned_score += partial_score
self._add_finding(
"HIGH", "3-2-1-1-0",
f"Incomplete restore testing: {len(tested_tiers)}/{all_tiers} tiers verified",
f"Only {len(tested_tiers)} of {all_tiers} recovery tiers have successful restore tests.",
"Implement automated restore testing (SureBackup) for all tiers.",
)
else:
self._add_finding(
"CRITICAL", "3-2-1-1-0",
"No successful restore tests recorded",
"No recovery tiers have documented successful restore verification.",
"Implement automated restore testing immediately. Untested backups "
"should be considered unreliable.",
)
def audit_credential_isolation(self):
"""Check backup credential isolation from production AD."""
creds = self.assessment.credential_isolation
if not creds:
self._add_finding(
"CRITICAL", "Credential Isolation",
"Credential isolation not assessed",
"No information provided about backup credential isolation.",
"Assess backup admin account configuration and network isolation.",
)
return
self.max_score += 10
if not creds.get("domain_joined", True):
self.earned_score += 10
else:
self._add_finding(
"CRITICAL", "Credential Isolation",
"Backup servers joined to production AD domain",
"Backup infrastructure is domain-joined. Ransomware operators compromise "
"backup credentials via Kerberoasting, DCSync, or GPO manipulation.",
"Remove backup servers from production AD. Use local accounts or a "
"dedicated management domain.",
)
self.max_score += 5
if creds.get("mfa_enabled", False):
self.earned_score += 5
else:
self._add_finding(
"HIGH", "Credential Isolation",
"MFA not enabled for backup administration",
"Backup admin accounts do not require multi-factor authentication.",
"Enable MFA for all backup console access using hardware tokens.",
)
self.max_score += 5
if creds.get("separate_network", False):
self.earned_score += 5
else:
self._add_finding(
"HIGH", "Credential Isolation",
"Backup infrastructure not on separate network segment",
"Backup servers share the production network segment.",
"Segment backup infrastructure into a dedicated VLAN with strict firewall rules.",
)
self.max_score += 3
if creds.get("rdp_disabled", False):
self.earned_score += 3
else:
self._add_finding(
"MEDIUM", "Credential Isolation",
"RDP enabled on backup servers",
"Remote Desktop Protocol is accessible on backup servers.",
"Disable RDP and use out-of-band management (iLO/iDRAC) for emergency access.",
)
self.max_score += 2
if creds.get("dedicated_admin_accounts", False):
self.earned_score += 2
else:
self._add_finding(
"HIGH", "Credential Isolation",
"No dedicated backup admin accounts",
"Backup administration uses shared or production admin accounts.",
"Create dedicated backup admin accounts with least-privilege access.",
)
def audit_rpo_rto_compliance(self):
"""Check if backup frequency meets RPO targets and document RTO validation."""
for tier in self.assessment.tiers:
self.max_score += 5
# Check if restore test is recent enough
if tier.last_restore_test:
try:
last_test = datetime.datetime.strptime(tier.last_restore_test, "%Y-%m-%d")
days_since_test = (datetime.datetime.now() - last_test).days
frequency_map = {
"weekly": 7,
"monthly": 30,
"quarterly": 90,
}
expected_days = frequency_map.get(tier.restore_test_frequency.lower(), 30)
if days_since_test <= expected_days * 1.5:
self.earned_score += 5
else:
self._add_finding(
"HIGH", "RPO/RTO",
f"Tier {tier.tier_level} ({tier.name}): Restore test overdue",
f"Last test was {days_since_test} days ago. "
f"Expected frequency: {tier.restore_test_frequency}.",
f"Run restore test for {tier.name} tier immediately.",
)
except ValueError:
self._add_finding(
"MEDIUM", "RPO/RTO",
f"Tier {tier.tier_level}: Invalid restore test date format",
f"Date '{tier.last_restore_test}' could not be parsed.",
"Use YYYY-MM-DD format for restore test dates.",
)
else:
self._add_finding(
"CRITICAL", "RPO/RTO",
f"Tier {tier.tier_level} ({tier.name}): No restore test recorded",
"No restore test has been documented for this recovery tier.",
f"Implement {tier.restore_test_frequency} automated restore testing.",
)
def audit_encryption(self):
"""Check backup encryption configuration."""
for copy in self.assessment.copies:
self.max_score += 3
if copy.encryption_algorithm:
if copy.encryption_algorithm.upper() in ("AES-256", "AES256", "AES-256-GCM"):
self.earned_score += 3
else:
self._add_finding(
"MEDIUM", "Encryption",
f"Weak backup encryption: {copy.encryption_algorithm}",
f"Backup copy at {copy.location} uses {copy.encryption_algorithm}.",
"Upgrade to AES-256 encryption for all backup copies.",
)
else:
self._add_finding(
"HIGH", "Encryption",
f"Unencrypted backup: {copy.location}",
f"Backup copy at {copy.location} is not encrypted.",
"Enable AES-256 encryption for all backup copies, both at rest and in transit.",
)
def audit_immutable_retention(self):
"""Check immutable retention period against typical ransomware dwell time."""
avg_dwell_time_days = 21 # Average ransomware dwell time
for copy in self.assessment.copies:
if copy.is_immutable:
self.max_score += 5
if copy.retention_days > avg_dwell_time_days:
self.earned_score += 5
else:
self._add_finding(
"CRITICAL", "Immutable Retention",
f"Immutable retention too short: {copy.retention_days} days",
f"Immutable retention at {copy.location} is {copy.retention_days} days. "
f"Average ransomware dwell time is {avg_dwell_time_days} days. "
"Attackers may wait for immutability to expire.",
f"Increase immutable retention to at least {avg_dwell_time_days * 2} days.",
)
def run_full_audit(self) -> dict:
"""Execute all audit checks and calculate overall score."""
self.audit_321_10_compliance()
self.audit_credential_isolation()
self.audit_rpo_rto_compliance()
self.audit_encryption()
self.audit_immutable_retention()
if self.max_score > 0:
self.assessment.score = round((self.earned_score / self.max_score) * 100, 1)
return asdict(self.assessment)
def generate_report(self) -> str:
"""Generate human-readable assessment report."""
result = self.run_full_audit()
lines = []
lines.append("=" * 70)
lines.append("RANSOMWARE BACKUP STRATEGY ASSESSMENT REPORT")
lines.append("=" * 70)
lines.append(f"Organization: {result['organization']}")
lines.append(f"Date: {result['assessment_date']}")
lines.append(f"Backup Solution: {result['backup_solution']}")
lines.append(f"Overall Score: {result['score']}%")
lines.append("")
# Score interpretation
score = result["score"]
if score >= 90:
rating = "EXCELLENT - Ransomware-resilient backup architecture"
elif score >= 75:
rating = "GOOD - Minor gaps in ransomware resilience"
elif score >= 50:
rating = "FAIR - Significant gaps require remediation"
else:
rating = "POOR - Critical ransomware backup risks present"
lines.append(f"Rating: {rating}")
lines.append("")
# 3-2-1-1-0 Summary
lines.append("-" * 40)
lines.append("3-2-1-1-0 COMPLIANCE")
lines.append("-" * 40)
copies = result["copies"]
lines.append(f"Total copies: {len(copies)} (minimum 3)")
media_types = set(c["media_type"] for c in copies)
lines.append(f"Media types: {', '.join(media_types)} ({len(media_types)} types, minimum 2)")
offsite = sum(1 for c in copies if c["is_offsite"])
lines.append(f"Offsite copies: {offsite} (minimum 1)")
immutable = sum(1 for c in copies if c["is_immutable"] or c["is_airgapped"])
lines.append(f"Immutable/Air-gapped copies: {immutable} (minimum 1)")
lines.append("")
# Recovery Tiers
lines.append("-" * 40)
lines.append("RECOVERY TIERS")
lines.append("-" * 40)
for tier in result["tiers"]:
lines.append(f" Tier {tier['tier_level']}: {tier['name']}")
lines.append(f" Systems: {len(tier['systems'])}")
lines.append(f" RPO: {tier['rpo_hours']}h | RTO: {tier['rto_hours']}h")
lines.append(f" Backup: {tier['backup_frequency']}")
lines.append(f" Restore test: {tier['restore_test_frequency']} "
f"(Last: {tier['last_restore_test'] or 'Never'}, "
f"Result: {tier['last_restore_result'] or 'N/A'})")
lines.append("")
# Credential Isolation
lines.append("-" * 40)
lines.append("CREDENTIAL ISOLATION")
lines.append("-" * 40)
creds = result["credential_isolation"]
for key, val in creds.items():
status = "PASS" if (val if key != "domain_joined" else not val) else "FAIL"
lines.append(f" {key}: {'Yes' if val else 'No'} [{status}]")
lines.append("")
# Findings
lines.append("-" * 40)
lines.append(f"FINDINGS ({len(result['findings'])} total)")
lines.append("-" * 40)
severity_order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3, "INFO": 4}
sorted_findings = sorted(result["findings"], key=lambda f: severity_order.get(f["severity"], 5))
for i, finding in enumerate(sorted_findings, 1):
lines.append(f"\n [{finding['severity']}] #{i}: {finding['title']}")
lines.append(f" Category: {finding['category']}")
lines.append(f" Detail: {finding['detail']}")
lines.append(f" Recommendation: {finding['recommendation']}")
lines.append("")
lines.append("=" * 70)
lines.append("END OF REPORT")
lines.append("=" * 70)
return "\n".join(lines)
def check_veeam_api(server: str, port: int = 9419) -> dict:
"""Check Veeam Backup & Replication API availability."""
result = {"reachable": False, "tls": False, "api_version": None}
try:
sock = socket.create_connection((server, port), timeout=5)
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
ssock = context.wrap_socket(sock, server_hostname=server)
result["reachable"] = True
result["tls"] = True
ssock.close()
except (socket.timeout, ConnectionRefusedError, OSError):
try:
sock = socket.create_connection((server, port), timeout=5)
result["reachable"] = True
sock.close()
except (socket.timeout, ConnectionRefusedError, OSError):
pass
return result
def check_s3_object_lock(bucket_name: str) -> dict:
"""Check AWS S3 bucket for Object Lock configuration."""
result = {"bucket": bucket_name, "object_lock_enabled": False, "retention_mode": None, "retention_days": None}
try:
output = subprocess.run(
["aws", "s3api", "get-object-lock-configuration", "--bucket", bucket_name],
capture_output=True, text=True, timeout=30,
)
if output.returncode == 0:
config = json.loads(output.stdout)
lock_config = config.get("ObjectLockConfiguration", {})
result["object_lock_enabled"] = lock_config.get("ObjectLockEnabled") == "Enabled"
rule = lock_config.get("Rule", {}).get("DefaultRetention", {})
result["retention_mode"] = rule.get("Mode")
result["retention_days"] = rule.get("Days")
except (subprocess.TimeoutExpired, FileNotFoundError, json.JSONDecodeError):
pass
return result
def main():
"""Run example backup strategy assessment."""
auditor = BackupStrategyAuditor(
org_name="Example Financial Services",
backup_solution="Veeam Backup & Replication 12",
)
# Define backup copies
auditor.add_backup_copy(BackupCopy(
location="On-premises NAS (Building A)",
media_type="NAS/NFS",
is_offsite=False,
is_immutable=False,
is_airgapped=False,
retention_days=14,
last_successful="2026-02-22",
encryption_algorithm="AES-256",
))
auditor.add_backup_copy(BackupCopy(
location="Veeam Hardened Linux Repository",
media_type="Linux/XFS",
is_offsite=False,
is_immutable=True,
is_airgapped=False,
retention_days=30,
last_successful="2026-02-22",
encryption_algorithm="AES-256",
))
auditor.add_backup_copy(BackupCopy(
location="AWS S3 (us-west-2) Object Lock",
media_type="Cloud Object Storage",
is_offsite=True,
is_immutable=True,
is_airgapped=False,
retention_days=60,
last_successful="2026-02-22",
encryption_algorithm="AES-256",
))
# Define recovery tiers
auditor.add_recovery_tier(RecoveryTier(
name="Critical",
tier_level=1,
systems=["DC01", "DC02", "DNS01", "ERP-DB", "CoreBanking"],
rpo_hours=1,
rto_hours=4,
backup_frequency="Hourly incremental, Daily full",
restore_test_frequency="weekly",
last_restore_test="2026-02-20",
last_restore_result="Success",
))
auditor.add_recovery_tier(RecoveryTier(
name="Important",
tier_level=2,
systems=["Exchange", "FileServer01", "WebApp01", "SharePoint"],
rpo_hours=4,
rto_hours=12,
backup_frequency="4-hour incremental, Daily full",
restore_test_frequency="monthly",
last_restore_test="2026-02-01",
last_restore_result="Success",
))
auditor.add_recovery_tier(RecoveryTier(
name="Standard",
tier_level=3,
systems=["DevServer01", "TestDB", "ArchiveNAS"],
rpo_hours=24,
rto_hours=48,
backup_frequency="Daily incremental, Weekly full",
restore_test_frequency="quarterly",
last_restore_test="2025-12-15",
last_restore_result="Success",
))
# Set credential isolation status
auditor.set_credential_isolation(
domain_joined=False,
mfa_enabled=True,
separate_network=True,
rdp_disabled=True,
dedicated_admin_accounts=True,
)
# Generate and print report
report = auditor.generate_report()
print(report)
# Export JSON for integration
result = auditor.run_full_audit()
output_path = Path(__file__).parent / "assessment_result.json"
with open(output_path, "w") as f:
json.dump(result, f, indent=2)
print(f"\nJSON report saved to: {output_path}")
if __name__ == "__main__":
main()