Audit Logging
Audit Logging
Logged Events
All SMTP credential operations are logged in the audit trail:
Event Types:
-
smtp_credentials_stored- Initial credential storage during VPS provisioning -
smtp_credentials_accessed- Admin retrieval for troubleshooting -
smtp_credentials_rotated- Automated or manual rotation -
smtp_credentials_emergency_reset- Emergency credential reset -
smtp_credentials_viewed- Credential viewing in UI -
smtp_credentials_copied- Copy to clipboard action
Audit Log Schema:
interface SmtpCredentialAuditEvent {
id: string; // UUID v4
event: string; // Event type
tenant_id: string; // Tenant UUID
user_id: string; // Admin user ID or 'system'
timestamp: string; // ISO 8601 timestamp
ip_address?: string; // Client IP address
user_agent?: string; // Client user agent
severity: 'info' | 'warning' | 'critical';
details: {
rotation_type?: 'automated' | 'manual';
reason?: string;
incident_id?: string;
previous_rotation?: string;
};
}
Audit Log Query Examples:
// Get all credential access events for a tenant
const accessEvents = await auditLog.query({
tenant_id: tenantId,
event: 'smtp_credentials_accessed',
order_by: 'timestamp DESC',
limit: 50
});
// Get all emergency resets in the last 30 days
const emergencyResets = await auditLog.query({
event: 'smtp_credentials_emergency_reset',
timestamp_gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
order_by: 'timestamp DESC'
});
// Get all credential operations by a specific admin
const adminOperations = await auditLog.query({
user_id: adminUserId,
event_in: [
'smtp_credentials_accessed',
'smtp_credentials_rotated',
'smtp_credentials_emergency_reset'
],
order_by: 'timestamp DESC',
limit: 100
});
Audit Alerts
Configure alerts for suspicious activity:
Alert Triggers:
-
Multiple credential access attempts (>5 in 1 hour)
-
Credential access outside business hours (10pm-6am)
-
Emergency reset without incident ID
-
Failed authentication attempts (>3 in 5 minutes)
Alert Implementation:
// Monitor audit log for suspicious activity
async function monitorSmtpCredentialAccess(): Promise<void> {
// Check for multiple access attempts
const recentAccess = await auditLog.query({
event: 'smtp_credentials_accessed',
timestamp_gte: new Date(Date.now() - 60 * 60 * 1000), // Last hour
group_by: 'user_id'
});
for (const [userId, count] of Object.entries(recentAccess)) {
if (count > 5) {
await sendSecurityAlert({
type: 'suspicious_credential_access',
user_id: userId,
access_count: count,
time_window: '1 hour'
});
}
}
// Check for after-hours access
const currentHour = new Date().getHours();
if (currentHour >= 22 || currentHour <= 6) {
const afterHoursAccess = await auditLog.query({
event: 'smtp_credentials_accessed',
timestamp_gte: new Date(Date.now() - 5 * 60 * 1000) // Last 5 minutes
});
if (afterHoursAccess.length > 0) {
await sendSecurityAlert({
type: 'after_hours_credential_access',
events: afterHoursAccess
});
}
}
}