Disaster Recovery Procedures

Disaster Recovery Procedures

Scenario 1: VPS Failure

If a VPS fails or needs to be replaced, SMTP credentials can be rapidly recovered from Vault:

Recovery Process:

sequenceDiagram
    participant Admin
    participant Backend
    participant Vault
    participant NewVPS
    participant MailU

    Admin->>Backend: Report VPS Failure
    Backend->>Backend: Provision New VPS
    Backend->>Vault: Retrieve SMTP Credentials
    Vault-->>Backend: Return Encrypted Credentials
    Backend->>Backend: Decrypt Password
    Backend->>NewVPS: Configure MailU
    Backend->>MailU: Set Admin Credentials
    MailU-->>Backend: Confirmation
    Backend->>Backend: Update DNS Records
    Backend-->>Admin: VPS Restored
    Note over Admin,Backend: RTO: 1 hour, RPO: 0 (no data loss)

Implementation:

// Recover SMTP credentials to new VPS
async function recoverSmtpCredentialsToNewVps(
  tenantId: string,
  newVpsIp: string
): Promise<void> {
  // Retrieve credentials from Vault
  const vaultData = await vaultClient.read(`smtp/${tenantId}/admin`);
  const password = await decryptPassword(vaultData.password, tenantId);

  // Configure MailU on new VPS
  await configureMailU(newVpsIp, vaultData.username, password);

  // Verify MailU is accessible
  const isAccessible = await verifyMailUAccess(
    vaultData.webmail_url,
    vaultData.username,
    password
  );

  if (!isAccessible) {
    throw new Error('Failed to verify MailU access after recovery');
  }

  // Log recovery event
  await auditLog.create({
    event: 'smtp_credentials_recovered',
    tenant_id: tenantId,
    user_id: 'system',
    timestamp: new Date().toISOString(),
    details: {
      old_vps_ip: tenant.vps_ip,
      new_vps_ip: newVpsIp,
      recovery_type: 'vps_failure'
    }
  });
}

Recovery Time Objective (RTO): 1 hour Recovery Point Objective (RPO): 0 (no data loss)

Scenario 2: Vault Backup Restoration

If Vault itself fails, credentials can be restored from encrypted backups:

Backup Strategy:

  • Frequency: Daily at 02:00 UTC

  • Retention: 30 daily backups, 12 monthly backups

  • Storage: Encrypted S3 bucket (AES-256-GCM)

  • Encryption Key: Stored separately from backups

Restoration Process:

// Restore Vault from backup
async function restoreVaultFromBackup(
  backupDate: string
): Promise<void> {
  // Download encrypted backup from S3
  const encryptedBackup = await s3.getObject({
    Bucket: 'penguinmails-vault-backups',
    Key: `backups/${backupDate}/vault-snapshot.enc`
  });

  // Decrypt backup
  const decryptedBackup = await decryptBackup(
    encryptedBackup,
    backupEncryptionKey
  );

  // Restore Vault snapshot
  await vaultClient.sys.restore(decryptedBackup);

  // Verify all secrets accessible
  const testTenantId = 'test-tenant-id';
  const testSecret = await vaultClient.read(`smtp/${testTenantId}/admin`);

  if (!testSecret) {
    throw new Error('Vault restoration verification failed');
  }

  // Log restoration event
  await auditLog.create({
    event: 'vault_restored_from_backup',
    user_id: 'system',
    timestamp: new Date().toISOString(),
    details: {
      backup_date: backupDate,
      restoration_time: new Date().toISOString()
    }
  });
}

Recovery Time Objective (RTO): 30 minutes Recovery Point Objective (RPO): 24 hours (daily backups)

Scenario 3: Credential Compromise

If SMTP credentials are compromised, immediate rotation is required:

Compromise Response:

// Respond to credential compromise
async function respondToCredentialCompromise(
  tenantId: string,
  incidentId: string,
  compromiseDetails: string
): Promise<void> {
  // Immediately rotate credentials
  await emergencyResetSmtpCredentials(
    tenantId,
    'system',
    incidentId,
    `Credential compromise: ${compromiseDetails}`
  );

  // Notify tenant
  await sendTenantNotification(tenantId, {
    type: 'security_incident',
    subject: 'SMTP Credentials Rotated - Security Incident',
    message: `Your SMTP credentials have been rotated due to a security incident.
              Incident ID: ${incidentId}.
              No action required on your part.`
  });

  // Notify security team
  await sendSecurityAlert({
    type: 'credential_compromise_response',
    tenant_id: tenantId,
    incident_id: incidentId,
    details: compromiseDetails,
    action_taken: 'emergency_rotation'
  });

  // Log incident response
  await auditLog.create({
    event: 'credential_compromise_response',
    tenant_id: tenantId,
    user_id: 'system',
    timestamp: new Date().toISOString(),
    severity: 'critical',
    details: {
      incident_id: incidentId,
      compromise_details: compromiseDetails,
      action_taken: 'emergency_rotation'
    }
  });
}

Response Time: Immediate (< 5 minutes) Impact: Zero downtime (MailU supports immediate password change)