SecurityMonitoringFactory.java
package com.cloudforgeci.api.observability;
import com.cloudforge.core.enums.TopologyType;
import com.cloudforge.core.enums.RuntimeType;
import com.cloudforge.core.enums.SecurityProfile;
import com.cloudforge.core.interfaces.ApplicationSpec;
import com.cloudforgeci.api.core.annotation.BaseFactory;
import com.cloudforge.core.annotation.SystemContext;
import software.amazon.awscdk.Duration;
import software.amazon.awscdk.services.cloudwatch.Alarm;
import software.amazon.awscdk.services.cloudwatch.ComparisonOperator;
import software.amazon.awscdk.services.cloudwatch.Metric;
import software.amazon.awscdk.services.cloudwatch.actions.SnsAction;
import software.amazon.awscdk.services.sns.Topic;
import software.constructs.Construct;
import java.util.Map;
import java.util.logging.Logger;
/**
* Factory for creating security monitoring and alerting configurations.
* Provides CloudWatch alarms and SNS notifications based on security profiles.
* Uses annotation-based context injection for cleaner code.
*/
public class SecurityMonitoringFactory extends BaseFactory {
private static final Logger LOG = Logger.getLogger(SecurityMonitoringFactory.class.getName());
@SystemContext("security")
private SecurityProfile security;
@SystemContext("applicationSpec")
private ApplicationSpec applicationSpec;
public SecurityMonitoringFactory(Construct scope, String id) {
super(scope, id);
}
@Override
public void create() {
// SecurityProfileConfiguration is now injected directly via annotation
LOG.info("Creating security monitoring for security profile: " + security);
// Only configure monitoring if enabled for this security profile
if (!config.isSecurityMonitoringEnabled()) {
LOG.info("Security monitoring disabled for security profile: " + security);
return;
}
// Create SNS topic for security alerts
Topic securityAlertsTopic = createSecurityAlertsTopic();
// Configure CloudWatch alarms
configureSecurityAlarms(securityAlertsTopic);
// Configure flow log monitoring if enabled
if (config.isFlowLogsEnabled()) {
configureFlowLogMonitoring(securityAlertsTopic);
}
LOG.info("Security monitoring configuration completed for profile: " + security);
}
/**
* Create SNS topic for security alerts.
*/
private Topic createSecurityAlertsTopic() {
String topicName = "security-alerts-" + security.name().toLowerCase();
Topic topic = Topic.Builder.create(this, "SecurityAlertsTopic")
.topicName(topicName)
.displayName("Security Alerts for " + security + " Environment")
.build();
LOG.info("Created security alerts SNS topic: " + topicName);
return topic;
}
/**
* Configure CloudWatch alarms for security monitoring.
*/
private void configureSecurityAlarms(Topic alertsTopic) {
LOG.info("Configuring CloudWatch security alarms for profile: " + security);
// Configure different alarm thresholds based on security profile
double highCpuThreshold = getHighCpuThreshold(security);
double highMemoryThreshold = getHighMemoryThreshold(security);
double highNetworkThreshold = getHighNetworkThreshold(security);
// High CPU Utilization Alarm
createCpuAlarm(alertsTopic, highCpuThreshold);
// High Memory Utilization Alarm
createMemoryAlarm(alertsTopic, highMemoryThreshold);
// High Network Traffic Alarm
createNetworkAlarm(alertsTopic, highNetworkThreshold);
// Failed Login Attempts Alarm (for production)
if (security == SecurityProfile.PRODUCTION) {
createFailedLoginAlarm(alertsTopic);
}
// Unusual API Activity Alarm (for staging and production)
if (security == SecurityProfile.STAGING || security == SecurityProfile.PRODUCTION) {
createUnusualApiActivityAlarm(alertsTopic);
}
}
/**
* Configure VPC Flow Log monitoring for security analysis.
*/
private void configureFlowLogMonitoring(Topic alertsTopic) {
LOG.info("Configuring VPC Flow Log monitoring for profile: " + security);
// Monitor for rejected connections (potential security threats)
createRejectedConnectionsAlarm(alertsTopic);
// Monitor for unusual traffic patterns (production only)
if (security == SecurityProfile.PRODUCTION) {
createUnusualTrafficPatternAlarm(alertsTopic);
}
}
/**
* Create CPU utilization alarm.
*/
private void createCpuAlarm(Topic alertsTopic, double threshold) {
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
Metric cpuMetric = Metric.Builder.create()
.namespace("AWS/ECS")
.metricName("CPUUtilization")
.dimensionsMap(Map.of("ServiceName", appId + "-" + security.name().toLowerCase()))
.statistic("Average")
.period(Duration.minutes(5))
.build();
Alarm cpuAlarm = Alarm.Builder.create(this, "HighCpuAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-high-cpu")
.alarmDescription("High CPU utilization detected in " + security + " environment")
.metric(cpuMetric)
.threshold(threshold)
.comparisonOperator(ComparisonOperator.GREATER_THAN_THRESHOLD)
.evaluationPeriods(2)
.build();
cpuAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created CPU alarm with threshold: " + threshold + "%");
}
/**
* Create memory utilization alarm.
*/
private void createMemoryAlarm(Topic alertsTopic, double threshold) {
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
Metric memoryMetric = Metric.Builder.create()
.namespace("AWS/ECS")
.metricName("MemoryUtilization")
.dimensionsMap(Map.of("ServiceName", appId + "-" + security.name().toLowerCase()))
.statistic("Average")
.period(Duration.minutes(5))
.build();
Alarm memoryAlarm = Alarm.Builder.create(this, "HighMemoryAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-high-memory")
.alarmDescription("High memory utilization detected in " + security + " environment")
.metric(memoryMetric)
.threshold(threshold)
.comparisonOperator(ComparisonOperator.GREATER_THAN_THRESHOLD)
.evaluationPeriods(2)
.build();
memoryAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created memory alarm with threshold: " + threshold + "%");
}
/**
* Create network traffic alarm.
*/
private void createNetworkAlarm(Topic alertsTopic, double threshold) {
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
Metric networkMetric = Metric.Builder.create()
.namespace("AWS/ECS")
.metricName("NetworkIn")
.dimensionsMap(Map.of("ServiceName", appId + "-" + security.name().toLowerCase()))
.statistic("Sum")
.period(Duration.minutes(5))
.build();
Alarm networkAlarm = Alarm.Builder.create(this, "HighNetworkAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-high-network")
.alarmDescription("High network traffic detected in " + security + " environment")
.metric(networkMetric)
.threshold(threshold)
.comparisonOperator(ComparisonOperator.GREATER_THAN_THRESHOLD)
.evaluationPeriods(3)
.build();
networkAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created network alarm with threshold: " + threshold + " bytes");
}
/**
* Create failed login attempts alarm (production only).
*/
private void createFailedLoginAlarm(Topic alertsTopic) {
if (!ctx.logs.get().isPresent()) {
LOG.warning("Cannot create failed login alarm - logs not configured");
return;
}
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
// Create metric filter for failed login attempts
Metric failedLoginMetric = Metric.Builder.create()
.namespace("CWLogs")
.metricName("FailedLoginAttempts")
.dimensionsMap(Map.of(
"Environment", security.name(),
"ApplicationId", appId
))
.statistic("Sum")
.period(Duration.minutes(5))
.build();
Alarm failedLoginAlarm = Alarm.Builder.create(this, "FailedLoginAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-failed-logins")
.alarmDescription("Multiple failed login attempts detected in " + security + " environment")
.metric(failedLoginMetric)
.threshold(5.0) // 5 failed attempts in 5 minutes
.comparisonOperator(ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD)
.evaluationPeriods(1)
.build();
failedLoginAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created failed login alarm for production environment");
}
/**
* Create unusual API activity alarm.
*/
private void createUnusualApiActivityAlarm(Topic alertsTopic) {
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
Metric apiMetric = Metric.Builder.create()
.namespace("AWS/ApplicationELB")
.metricName("RequestCount")
.dimensionsMap(Map.of("LoadBalancer", appId + "-" + security.name().toLowerCase()))
.statistic("Sum")
.period(Duration.minutes(5))
.build();
double threshold = security == SecurityProfile.PRODUCTION ? 1000.0 : 500.0;
Alarm apiAlarm = Alarm.Builder.create(this, "UnusualApiActivityAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-unusual-api-activity")
.alarmDescription("Unusual API activity detected in " + security + " environment")
.metric(apiMetric)
.threshold(threshold)
.comparisonOperator(ComparisonOperator.GREATER_THAN_THRESHOLD)
.evaluationPeriods(2)
.build();
apiAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created API activity alarm with threshold: " + threshold + " requests");
}
/**
* Create rejected connections alarm from VPC Flow Logs.
*/
private void createRejectedConnectionsAlarm(Topic alertsTopic) {
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
Metric rejectedMetric = Metric.Builder.create()
.namespace("CWLogs")
.metricName("RejectedConnections")
.dimensionsMap(Map.of("Environment", security.name()))
.statistic("Sum")
.period(Duration.minutes(5))
.build();
double threshold = security == SecurityProfile.PRODUCTION ? 50.0 : 100.0;
Alarm rejectedAlarm = Alarm.Builder.create(this, "RejectedConnectionsAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-rejected-connections")
.alarmDescription("High number of rejected connections detected in " + security + " environment")
.metric(rejectedMetric)
.threshold(threshold)
.comparisonOperator(ComparisonOperator.GREATER_THAN_THRESHOLD)
.evaluationPeriods(2)
.build();
rejectedAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created rejected connections alarm with threshold: " + threshold);
}
/**
* Create unusual traffic pattern alarm (production only).
*/
private void createUnusualTrafficPatternAlarm(Topic alertsTopic) {
String appId = applicationSpec != null ? applicationSpec.applicationId() : "app";
Metric trafficMetric = Metric.Builder.create()
.namespace("CWLogs")
.metricName("UnusualTrafficPatterns")
.dimensionsMap(Map.of("Environment", security.name()))
.statistic("Sum")
.period(Duration.minutes(15))
.build();
Alarm trafficAlarm = Alarm.Builder.create(this, "UnusualTrafficAlarm")
.alarmName(appId + "-" + security.name().toLowerCase() + "-unusual-traffic")
.alarmDescription("Unusual traffic patterns detected in " + security + " environment")
.metric(trafficMetric)
.threshold(10.0) // 10 unusual patterns in 15 minutes
.comparisonOperator(ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD)
.evaluationPeriods(1)
.build();
trafficAlarm.addAlarmAction(new SnsAction(alertsTopic));
LOG.info("Created unusual traffic pattern alarm for production environment");
}
/**
* Get CPU threshold based on security profile.
*/
private double getHighCpuThreshold(SecurityProfile profile) {
return switch (profile) {
case DEV -> 90.0; // Relaxed for dev
case STAGING -> 80.0; // Moderate for staging
case PRODUCTION -> 70.0; // Strict for production
};
}
/**
* Get memory threshold based on security profile.
*/
private double getHighMemoryThreshold(SecurityProfile profile) {
return switch (profile) {
case DEV -> 90.0; // Relaxed for dev
case STAGING -> 85.0; // Moderate for staging
case PRODUCTION -> 80.0; // Strict for production
};
}
/**
* Get network threshold based on security profile.
*/
private double getHighNetworkThreshold(SecurityProfile profile) {
return switch (profile) {
case DEV -> 1000000000.0; // 1GB for dev
case STAGING -> 500000000.0; // 500MB for staging
case PRODUCTION -> 100000000.0; // 100MB for production
};
}
}