DevSecurityConfiguration.java
package com.cloudforgeci.api.core.security;
import com.cloudforge.core.enums.TopologyType;
import com.cloudforge.core.enums.RuntimeType;
import com.cloudforge.core.enums.SecurityProfile;
import com.cloudforgeci.api.core.SystemContext;
import com.cloudforgeci.api.interfaces.SecurityConfiguration;
import com.cloudforgeci.api.interfaces.SecurityProfileConfiguration;
import com.cloudforgeci.api.interfaces.Rule;
import com.cloudforgeci.api.observability.WafFactory;
import software.amazon.awscdk.services.ec2.Peer;
import software.amazon.awscdk.services.ec2.Port;
import java.util.List;
import java.util.logging.Logger;
import static com.cloudforgeci.api.core.rules.RuleKit.require;
/**
* Development security configuration with relaxed security settings.
* Suitable for development environments with minimal security constraints.
* Integrates with SecurityProfileConfiguration for observability settings.
*/
public final class DevSecurityConfiguration implements SecurityConfiguration {
private static final Logger LOG = Logger.getLogger(DevSecurityConfiguration.class.getName());
public DevSecurityConfiguration() {
// SecurityProfileConfiguration will be set in SystemContext by SecurityRules
}
@Override
public SecurityProfile kind() {
return SecurityProfile.DEV;
}
@Override
public String id() {
return "security:DEV";
}
@Override
public List<Rule> rules(SystemContext c) {
var rules = new java.util.ArrayList<Rule>();
rules.add(require("vpc", x -> x.vpc));
// Instance security group is only required for EC2 runtime
if (c.runtime == RuntimeType.EC2) {
rules.add(require("instance security group", x -> x.instanceSg));
}
rules.add(require("alb security group", x -> x.albSg));
return rules;
}
@Override
public void wire(SystemContext c) {
LOG.info("Configuring DEV security profile with observability settings");
// Configure observability based on security profile
configureObservability(c);
// WAF Configuration - respects deployment context override
// WafFactory will only create WAF if config.isWafEnabled() returns true
// NOTE: Use stack-specific ID to avoid conflicts when switching security profiles
WafFactory wafFactory = new WafFactory(c, c.stackName + "-Waf");
wafFactory.create();
// Development security settings - minimal restrictions
// Allow broader access for development
whenBoth(c.vpc, c.instanceSg, (vpc, instanceSg) -> {
// Allow SSH from anywhere for development convenience
instanceSg.addIngressRule(
Peer.anyIpv4(),
Port.tcp(22),
"SSH_from_anywhere_(DEV)",
false
);
// Allow application port from anywhere for development
int appPort = c.applicationSpec.get().map(spec -> spec.applicationPort()).orElse(8080);
instanceSg.addIngressRule(
Peer.anyIpv4(),
Port.tcp(appPort),
"App_from_anywhere_(DEV)",
false
);
});
// ALB security group - allow HTTP/HTTPS from anywhere
whenBoth(c.vpc, c.albSg, (vpc, albSg) -> {
// Check if HTTPS strict mode is enabled (no HTTP listener)
boolean httpsStrict = c.securityProfileConfig.get()
.map(SecurityProfileConfiguration::isHttpsStrictEnabled)
.orElse(false);
if (!httpsStrict) {
albSg.addIngressRule(
Peer.anyIpv4(),
Port.tcp(80),
"HTTP_from_anywhere_(DEV)",
false
);
} else {
LOG.info("HTTPS strict mode enabled: Skipping port 80 ingress rule (DEV)");
}
albSg.addIngressRule(
Peer.anyIpv4(),
Port.tcp(443),
"HTTPS_from_anywhere_(DEV)",
false
);
});
// EFS security group - allow NFS from appropriate security group based on runtime
whenBoth(c.vpc, c.efsSg, (vpc, efsSg) -> {
if (c.runtime == RuntimeType.FARGATE) {
// For Fargate, allow NFS from Fargate service security group
if (c.fargateServiceSg.get().isPresent()) {
efsSg.addIngressRule(
Peer.securityGroupId(c.fargateServiceSg.get().orElseThrow().getSecurityGroupId()),
Port.tcp(2049),
"NFS_from_Fargate_tasks_(DEV)",
false
);
}
} else {
// For EC2, allow NFS from instance security group
if (c.instanceSg.get().isPresent()) {
String appId = (c.applicationSpec != null && c.applicationSpec.get().isPresent())
? c.applicationSpec.get().orElseThrow().applicationId()
: "app";
efsSg.addIngressRule(
Peer.securityGroupId(c.instanceSg.get().orElseThrow().getSecurityGroupId()),
Port.tcp(2049),
"NFS_from_" + appId + "_instances_(DEV)",
false
);
}
}
});
// Fargate security group - allow from ALB
whenBoth(c.vpc, c.fargateServiceSg, (vpc, fargateSg) -> {
int appPort = c.applicationSpec.get().map(spec -> spec.applicationPort()).orElse(8080);
fargateSg.addIngressRule(
Peer.securityGroupId(c.albSg.get().orElseThrow().getSecurityGroupId()),
Port.tcp(appPort),
"HTTP from ALB (DEV)",
false
);
});
// Create or lookup hosted zone if domain is provided
if (c.cfc.domain() != null && !c.cfc.domain().isBlank()) {
System.out.println("DevSecurityConfiguration: Setting up hosted zone for domain: " + c.cfc.domain());
if (c.zone.get().isPresent()) {
System.out.println("DevSecurityConfiguration: Hosted zone already exists");
return; // Already created
}
if (c.cfc.createZone()) {
// Create a new hosted zone when createZone = true
System.out.println("DevSecurityConfiguration: Creating new hosted zone (createZone = true)");
software.amazon.awscdk.services.route53.HostedZone zone =
software.amazon.awscdk.services.route53.HostedZone.Builder.create((software.constructs.Construct)c.getNode().getScope(), "DevZone")
.zoneName(c.cfc.domain())
.build();
c.zone.set(zone);
System.out.println("DevSecurityConfiguration: New hosted zone created and set in context");
} else {
// Use existing hosted zone when createZone = false
System.out.println("DevSecurityConfiguration: Looking up existing hosted zone (createZone = false)");
software.amazon.awscdk.services.route53.IHostedZone zone =
software.amazon.awscdk.services.route53.HostedZone.fromLookup((software.constructs.Construct)c.getNode().getScope(), "DevZoneLookup",
software.amazon.awscdk.services.route53.HostedZoneProviderProps.builder()
.domainName(c.cfc.domain())
.build());
c.zone.set(zone);
System.out.println("DevSecurityConfiguration: Existing hosted zone looked up and set in context");
}
}
}
/**
* Configure observability settings based on DEV security profile.
*/
private void configureObservability(SystemContext c) {
LOG.info("Configuring DEV observability settings");
// Get security profile configuration from SystemContext
if (!c.securityProfileConfig.get().isPresent()) {
LOG.warning("SecurityProfileConfiguration not available in SystemContext");
return;
}
SecurityProfileConfiguration profileConfig = c.securityProfileConfig.get().orElseThrow();
// Configure logging retention (minimal for dev)
if (profileConfig.getLogRetentionDays() != null) {
LOG.info("DEV profile configured with log retention: " + profileConfig.getLogRetentionDays());
}
// Configure flow logs (disabled by default for dev)
if (!profileConfig.isFlowLogsEnabled()) {
LOG.info("Flow logs disabled for DEV profile (cost optimization)");
}
// Configure security monitoring (minimal for dev)
if (!profileConfig.isSecurityMonitoringEnabled()) {
LOG.info("Security monitoring disabled for DEV profile");
}
// Configure encryption (basic enabled for dev)
if (profileConfig.isEbsEncryptionEnabled()) {
LOG.info("EBS encryption enabled for DEV profile");
}
// Configure backup (minimal for dev)
if (!profileConfig.isAutomatedBackupEnabled()) {
LOG.info("Automated backup disabled for DEV profile (manual backups)");
}
// Configure auto-scaling (disabled for dev)
if (!profileConfig.isAutoScalingEnabled()) {
LOG.info("Auto-scaling disabled for DEV profile");
}
}
private static <A,B> void whenBoth(com.cloudforgeci.api.core.Slot<A> a, com.cloudforgeci.api.core.Slot<B> b,
java.util.function.BiConsumer<A,B> fn) {
Runnable tryRun = () -> {
var ao = a.get(); var bo = b.get();
if (ao.isPresent() && bo.isPresent()) fn.accept(ao.get(), bo.get());
};
a.onSet(x -> tryRun.run()); b.onSet(y -> tryRun.run()); tryRun.run();
}
private boolean isTestEnvironment() {
// Check for test environment indicators
return System.getProperty("java.class.path").contains("test") ||
System.getProperty("maven.test.skip") != null ||
System.getProperty("surefire.test.class.path") != null ||
// Check for JUnit test context in stack trace
java.util.Arrays.stream(Thread.currentThread().getStackTrace())
.anyMatch(element -> element.getClassName().contains("junit") ||
element.getClassName().contains("test"));
}
}