package org.zalando.stups.fullstop.jobs.rds;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.rds.AmazonRDSClient;
import com.amazonaws.services.rds.model.DBInstance;
import com.amazonaws.services.rds.model.DescribeDBInstancesRequest;
import com.amazonaws.services.rds.model.DescribeDBInstancesResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.zalando.stups.fullstop.aws.ClientProvider;
import org.zalando.stups.fullstop.jobs.FullstopJob;
import org.zalando.stups.fullstop.jobs.common.AccountIdSupplier;
import org.zalando.stups.fullstop.jobs.config.JobsProperties;
import org.zalando.stups.fullstop.violation.Violation;
import org.zalando.stups.fullstop.violation.ViolationBuilder;
import org.zalando.stups.fullstop.violation.ViolationSink;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Optional;
import static com.google.common.collect.Maps.newHashMap;
import static org.apache.commons.lang3.StringUtils.trimToNull;
import static org.zalando.stups.fullstop.violation.ViolationType.UNSECURED_PUBLIC_ENDPOINT;
@Component
public class FetchRdsJob implements FullstopJob {
private static final String EVENT_ID = "checkRdsJob";
private final Logger log = LoggerFactory.getLogger(FetchRdsJob.class);
private final AccountIdSupplier allAccountIds;
private final ClientProvider clientProvider;
private final JobsProperties jobsProperties;
private final ViolationSink violationSink;
@Autowired
public FetchRdsJob(final AccountIdSupplier allAccountIds, final ClientProvider clientProvider,
final JobsProperties jobsProperties,
final ViolationSink violationSink) {
this.allAccountIds = allAccountIds;
this.clientProvider = clientProvider;
this.jobsProperties = jobsProperties;
this.violationSink = violationSink;
}
@PostConstruct
public void init() {
log.info("{} initialized", getClass().getSimpleName());
}
@Scheduled(fixedRate = 300_000)
public void run() {
for (final String accountId : allAccountIds.get()) {
for (final String region : jobsProperties.getWhitelistedRegions()) {
try {
final AmazonRDSClient amazonRDSClient = clientProvider.getClient(AmazonRDSClient.class, accountId,
Region.getRegion(Regions.fromName(region)));
Optional<String> marker = Optional.empty();
do {
final DescribeDBInstancesRequest request = new DescribeDBInstancesRequest();
marker.ifPresent(request::setMarker);
final DescribeDBInstancesResult result = amazonRDSClient.describeDBInstances(request);
marker = Optional.ofNullable(trimToNull(result.getMarker()));
result.getDBInstances().stream()
.filter(DBInstance::getPubliclyAccessible)
.filter(dbInstance -> dbInstance.getEndpoint() != null)
.forEach(dbInstance -> {
final Map<String, Object> metadata = newHashMap();
metadata.put("unsecuredDatabase", dbInstance.getEndpoint().getAddress());
metadata.put("errorMessages", "Unsecured Database! Your DB can be reached from outside");
writeViolation(accountId, region, metadata, dbInstance.getEndpoint().getAddress());
});
} while (marker.isPresent());
} catch (final AmazonServiceException a) {
log.error(a.getMessage(), a);
}
}
}
}
private void writeViolation(final String account, final String region, final Object metaInfo, final String rdsEndpoint) {
final ViolationBuilder violationBuilder = new ViolationBuilder();
final Violation violation = violationBuilder.withAccountId(account)
.withRegion(region)
.withPluginFullyQualifiedClassName(FetchRdsJob.class)
.withType(UNSECURED_PUBLIC_ENDPOINT)
.withMetaInfo(metaInfo)
.withEventId(EVENT_ID)
.withInstanceId(rdsEndpoint)
.build();
violationSink.put(violation);
}
}