diff --git a/gradle.properties b/gradle.properties index de09e636..fed9990e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,4 @@ group=com.nike artifactId=cerberus-lifecycle-cli -version=4.12.2 +version=4.13.0 diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index be3f22f9..4f02b140 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -19,7 +19,7 @@ allprojects { jcenter() } - def awsSDKVersion = '1.11.269' + def awsSDKVersion = '1.11.739' //noinspection GroovyAssignabilityCheck dependencies { @@ -38,6 +38,8 @@ allprojects { compile group: 'com.amazonaws', name: 'aws-java-sdk-rds', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-encryption-sdk-java', version: '1.3.1' compile group: 'com.amazonaws', name: 'aws-java-sdk-athena', version: awsSDKVersion + compile group: 'com.amazonaws', name: 'aws-java-sdk-waf', version: awsSDKVersion + compile group: 'com.amazonaws', name: 'aws-java-sdk-kinesis', version: awsSDKVersion compile 'com.nike:vault-client:1.4.1' compile 'com.squareup.okhttp3:okhttp:3.3.1' diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 482efc8c..af65f42a 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -213,6 +213,7 @@ private void registerAllCommands() { registerCommand(new GenerateCertificateFilesCommand()); registerCommand(new CreateVpcCommand()); registerCommand(new CreateWafCommand()); + registerCommand(new CreateWafLoggingCommand()); registerCommand(new CreateDatabaseCommand()); registerCommand(new CreateRoute53Command()); registerCommand(new CreateSecurityGroupsCommand()); diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index a4107bd3..ec7d59c0 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -150,6 +150,9 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig case CreateWafCommand.COMMAND_NAME: args = getCreateWafCommandArgs(environmentConfig); break; + case CreateWafLoggingCommand.COMMAND_NAME: + args = getCreateWafLoggingCommandArgs(environmentConfig); + break; case GenerateCertificateFilesCommand.COMMAND_NAME: args = getGenerateCertificatesCommandArgs(environmentConfig); break; @@ -348,6 +351,12 @@ private static List getCreateWafCommandArgs(EnvironmentConfig config) { .build(); } + private static List getCreateWafLoggingCommandArgs(EnvironmentConfig config) { + return ArgsBuilder.create() + .addAll(getGlobalTags(config)) + .build(); + } + private static List getGenerateCertificatesCommandArgs(EnvironmentConfig config) { ArgsBuilder args = ArgsBuilder.create() .addOption(GenerateCertificateFilesCommandParametersDelegate.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) diff --git a/src/main/java/com/nike/cerberus/client/aws/KinesisFirehoseAwsClientFactory.java b/src/main/java/com/nike/cerberus/client/aws/KinesisFirehoseAwsClientFactory.java new file mode 100644 index 00000000..901acc6a --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/aws/KinesisFirehoseAwsClientFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.client.aws; + +import com.amazonaws.regions.Regions; +import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehoseClient; +import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehoseClientBuilder; +import com.nike.cerberus.service.AwsClientFactory; + +public class KinesisFirehoseAwsClientFactory extends AwsClientFactory { + + @Override + public AmazonKinesisFirehoseClient getClient(Regions region) { + if (!clients.containsKey(region)) { + clients.put(region, createClient(region)); + } + return clients.get(region); + } + + private AmazonKinesisFirehoseClient createClient(Regions region) { + return (AmazonKinesisFirehoseClient) AmazonKinesisFirehoseClientBuilder.standard() + .withRegion(region) + .withCredentials(getAWSCredentialsProviderChain()) + .build(); + } + +} diff --git a/src/main/java/com/nike/cerberus/client/aws/WafAwsClientFactory.java b/src/main/java/com/nike/cerberus/client/aws/WafAwsClientFactory.java new file mode 100644 index 00000000..8c2a81ff --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/aws/WafAwsClientFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.client.aws; + +import com.amazonaws.regions.Regions; +import com.amazonaws.services.waf.AWSWAFRegionalClient; +import com.amazonaws.services.waf.AWSWAFRegionalClientBuilder; +import com.nike.cerberus.service.AwsClientFactory; + +public class WafAwsClientFactory extends AwsClientFactory { + + @Override + public AWSWAFRegionalClient getClient(Regions region) { + if (!clients.containsKey(region)) { + clients.put(region, createClient(region)); + } + return clients.get(region); + } + + private AWSWAFRegionalClient createClient(Regions region) { + return (AWSWAFRegionalClient) AWSWAFRegionalClientBuilder.standard() + .withRegion(region) + .withCredentials(getAWSCredentialsProviderChain()) + .build(); + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateWafLoggingCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateWafLoggingCommand.java new file mode 100644 index 00000000..a655f591 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateWafLoggingCommand.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.command.core; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.beust.jcommander.ParametersDelegate; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.domain.cloudformation.CloudFormationParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateWafLoggingOperation; + +import static com.nike.cerberus.command.core.CreateWafCommand.COMMAND_NAME; + +/** + * Command to create the WAF logging for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the Web Application Firewall (WAF) logging.") +public class CreateWafLoggingCommand implements Command { + + public static final String COMMAND_NAME = "create-waf-logging"; + + @ParametersDelegate + private CloudFormationParametersDelegate cloudFormationParametersDelegate = new CloudFormationParametersDelegate(); + + @Parameter(names = {"--skip-stack-creation", "-s"}, description = "Skips WAF logging stack creation.") + private boolean skipStackCreation; + + public boolean isSkipStackCreation() { + return skipStackCreation; + } + + public CloudFormationParametersDelegate getCloudFormationParametersDelegate() { + return cloudFormationParametersDelegate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateWafLoggingOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/WafLoggingOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/WafLoggingOutputs.java new file mode 100644 index 00000000..57c95ae8 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/WafLoggingOutputs.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.domain.cloudformation; + +/** + * Represents the WAF logging stack outputs. + */ +public class WafLoggingOutputs { + private String kinesisFirehoseDeliveryStreamARN; + + private String kinesisFirehoseDeliveryStreamName; + + public String getKinesisFirehoseDeliveryStreamName() { + return kinesisFirehoseDeliveryStreamName; + } + + public void setKinesisFirehoseDeliveryStreamName(String kinesisFirehoseDeliveryStreamName) { + this.kinesisFirehoseDeliveryStreamName = kinesisFirehoseDeliveryStreamName; + } + + public String getKinesisFirehoseDeliveryStreamARN() { + return kinesisFirehoseDeliveryStreamARN; + } + + public void setKinesisFirehoseDeliveryStreamARN(String kinesisFirehoseDeliveryStreamARN) { + this.kinesisFirehoseDeliveryStreamARN = kinesisFirehoseDeliveryStreamARN; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/WafLoggingParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/WafLoggingParameters.java new file mode 100644 index 00000000..10282c55 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/WafLoggingParameters.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.domain.cloudformation; + +/** + * Represents the WAF logging stack inputs. + */ +public class WafLoggingParameters { + private String environmentName; + + public String getEnvironmentName() { + return environmentName; + } + + public WafLoggingParameters setEnvironmentName(String environmentName) { + this.environmentName = environmentName; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/WafOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/WafOutputs.java index 59369314..645d234d 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/WafOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/WafOutputs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Nike, Inc. + * Copyright (c) 2020 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,17 +21,27 @@ */ public class WafOutputs { - private Integer autoBlockIPSetID; + private String autoBlockIPSetID; private String manualBlockIPSetID; private String whitelistIPSetID; - public Integer getAutoBlockIPSetID() { + private String webAclID; + + public String getWebAclID() { + return webAclID; + } + + public void setWebAclID(String webAclID) { + this.webAclID = webAclID; + } + + public String getAutoBlockIPSetID() { return autoBlockIPSetID; } - public WafOutputs setAutoBlockIPSetID(Integer autoBlockIPSetID) { + public WafOutputs setAutoBlockIPSetID(String autoBlockIPSetID) { this.autoBlockIPSetID = autoBlockIPSetID; return this; } diff --git a/src/main/java/com/nike/cerberus/domain/environment/Stack.java b/src/main/java/com/nike/cerberus/domain/environment/Stack.java index eb37bc5a..535b7330 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -42,6 +42,7 @@ public class Stack implements Comparable { public static final Stack WAF = new Stack("web-app-firewall", "web-app-firewall.yaml", false); public static final Stack ROUTE53 = new Stack("route53", "route53.yaml", false); public static final Stack AUDIT = new Stack("audit", "audit.yaml", false); + public static final Stack WAF_LOGGING = new Stack("waf-logging", "waf-logging.yaml", false); public static final ImmutableList ALL_STACKS = ImmutableList.of( IAM_ROLES, @@ -53,7 +54,8 @@ public class Stack implements Comparable { CMS, WAF, ROUTE53, - AUDIT + AUDIT, + WAF_LOGGING ); public static final ImmutableList ALL_STACK_NAMES = ImmutableList.copyOf(ALL_STACKS.stream().map(Stack::getName).collect(Collectors.toList())); diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index 03820d8e..62d4e5b1 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -23,6 +23,7 @@ import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancingClient; import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; +import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehoseClient; import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.lambda.AWSLambdaClient; import com.amazonaws.services.rds.AmazonRDSClient; @@ -30,6 +31,7 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.sns.AmazonSNSClient; +import com.amazonaws.services.waf.AWSWAFRegionalClient; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; @@ -46,6 +48,8 @@ import com.google.inject.name.Names; import com.google.inject.util.Providers; import com.nike.cerberus.client.aws.AthenaAwsClientFactory; +import com.nike.cerberus.client.aws.KinesisFirehoseAwsClientFactory; +import com.nike.cerberus.client.aws.WafAwsClientFactory; import com.nike.cerberus.command.CerberusCommand; import com.nike.cerberus.command.ProxyDelegate; import com.nike.cerberus.domain.environment.RegionDeserializer; @@ -125,6 +129,8 @@ private void bindAwsClientFactories() { bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); bind(new TypeLiteral>() {}).toInstance(new AthenaAwsClientFactory()); + bind(new TypeLiteral>() {}).toInstance(new WafAwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new KinesisFirehoseAwsClientFactory() {}); } /** diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateAlbLogAthenaDbAndTableOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateAlbLogAthenaDbAndTableOperation.java index 78d92c94..407994f4 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateAlbLogAthenaDbAndTableOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateAlbLogAthenaDbAndTableOperation.java @@ -35,6 +35,9 @@ import static com.nike.cerberus.module.CerberusModule.ENV_NAME; +/** + * Creates the Athena database and table for ALB log for Cerberus + */ public class CreateAlbLogAthenaDbAndTableOperation implements Operation { private final Logger log = LoggerFactory.getLogger(getClass()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafLoggingOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafLoggingOperation.java new file mode 100644 index 00000000..c088ae4e --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafLoggingOperation.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.operation.core; + +import com.amazonaws.regions.Regions; +import com.nike.cerberus.command.core.CreateWafLoggingCommand; +import com.nike.cerberus.domain.cloudformation.WafLoggingOutputs; +import com.nike.cerberus.domain.cloudformation.WafLoggingParameters; +import com.nike.cerberus.domain.cloudformation.WafOutputs; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.KinesisService; +import com.nike.cerberus.service.WafService; +import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.CloudFormationObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Map; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + +/** + * Creates WAF logging via CloudFormation. + */ +public class CreateWafLoggingOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final String environmentName; + + private final CloudFormationService cloudFormationService; + + private final CloudFormationObjectMapper cloudFormationObjectMapper; + + private final ConfigStore configStore; + + private final WafService wafService; + + private final KinesisService kinesisService; + + private final String webAclArnTemplate = "arn:aws:waf-regional:%s:%s:webacl/%s"; + + @Inject + public CreateWafLoggingOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + CloudFormationObjectMapper cloudFormationObjectMapper, + ConfigStore configStore, + WafService wafService, KinesisService kinesisService) { + + this.environmentName = environmentName; + this.cloudFormationService = cloudFormationService; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; + this.configStore = configStore; + this.wafService = wafService; + this.kinesisService = kinesisService; + } + + @Override + public void run(CreateWafLoggingCommand command) { + Regions region = command.getCloudFormationParametersDelegate().getStackRegion() + .orElse(configStore.getPrimaryRegion()); + + if (!command.isSkipStackCreation()) { + WafLoggingParameters wafLoggingParameters = new WafLoggingParameters() + .setEnvironmentName(environmentName); + + Map parameters = cloudFormationObjectMapper.convertValue(wafLoggingParameters); + + cloudFormationService.createStackAndWait( + region, + Stack.WAF_LOGGING, + parameters, + true, + command.getCloudFormationParametersDelegate().getTags() + ); + } + + WafLoggingOutputs wafLoggingOutputs = configStore.getStackOutputs(region, + Stack.WAF_LOGGING.getFullName(environmentName), WafLoggingOutputs.class); + kinesisService.enableEncryption(wafLoggingOutputs.getKinesisFirehoseDeliveryStreamName(), region); + WafOutputs wafOutputs = + configStore.getStackOutputs(region, + Stack.WAF.getFullName(environmentName), WafOutputs.class); + String webAclId = wafOutputs.getWebAclID(); + String webAclArn = String.format(webAclArnTemplate, region.getName(), configStore.getAccountId(), webAclId); + + wafService.enableWafLogging(wafLoggingOutputs.getKinesisFirehoseDeliveryStreamARN(), webAclArn, region); + } + + @Override + public boolean isRunnable(CreateWafLoggingCommand command) { + Regions region = command.getCloudFormationParametersDelegate().getStackRegion() + .orElse(configStore.getPrimaryRegion()); + try { + cloudFormationService.getStackId(region, Stack.WAF.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + logger.error("The web application firewall stack must exist to enable logging!"); + return false; + } + + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/service/KinesisService.java b/src/main/java/com/nike/cerberus/service/KinesisService.java new file mode 100644 index 00000000..90ed186e --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/KinesisService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.service; + +import com.amazonaws.regions.Regions; +import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehoseClient; +import com.amazonaws.services.kinesisfirehose.model.StartDeliveryStreamEncryptionRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + +/** + * Service wrapper for Kinesis. + */ +public class KinesisService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private AwsClientFactory kinesisFirehoseClientFactory; + + @Inject + public KinesisService(AwsClientFactory kinesisFirehoseClientFactory) { + this.kinesisFirehoseClientFactory = kinesisFirehoseClientFactory; + } + + public void enableEncryption(String deliveryStreamName, Regions region) { + AmazonKinesisFirehoseClient kinesisFirehoseClient = kinesisFirehoseClientFactory.getClient(region); + kinesisFirehoseClient.startDeliveryStreamEncryption(new StartDeliveryStreamEncryptionRequest() + .withDeliveryStreamName(deliveryStreamName)); + } +} diff --git a/src/main/java/com/nike/cerberus/service/WafService.java b/src/main/java/com/nike/cerberus/service/WafService.java new file mode 100644 index 00000000..3dc64250 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/WafService.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.service; + +import com.amazonaws.regions.Regions; +import com.amazonaws.services.waf.AWSWAFRegionalClient; +import com.amazonaws.services.waf.model.FieldToMatch; +import com.amazonaws.services.waf.model.LoggingConfiguration; +import com.amazonaws.services.waf.model.MatchFieldType; +import com.amazonaws.services.waf.model.PutLoggingConfigurationRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +/** + * Service wrapper for WAF. + */ +public class WafService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private AwsClientFactory wafClientFactory; + + private final List redactedFields; + + @Inject + public WafService(AwsClientFactory wafClientFactory) { + this.wafClientFactory = wafClientFactory; + redactedFields = getredactedFields("authorization", "x-amz-security-token", "x-cerberus-token", "x-vault-token"); + } + + private List getredactedFields(String... headerNames) { + List fieldToMatchList = new ArrayList<>(); + for (String headerName:headerNames){ + fieldToMatchList.add(new FieldToMatch().withType(MatchFieldType.HEADER).withData(headerName)); + } + return fieldToMatchList; + } + + public void enableWafLogging(String kinesisFirehoseArn, String webAclArn, Regions region) { + AWSWAFRegionalClient wafClient = wafClientFactory.getClient(region); + wafClient.putLoggingConfiguration(new PutLoggingConfigurationRequest().withLoggingConfiguration(new LoggingConfiguration() + .withRedactedFields(redactedFields).withResourceArn(webAclArn).withLogDestinationConfigs(kinesisFirehoseArn))); + } +} diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index d2eaacdd..af6b37ee 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -768,10 +768,7 @@ public void initializeEnvironment(String adminRoleArn, Regions primaryRegion, Map regionConfigOutputsMap) { - AWSSecurityTokenService securityTokenService = securityTokenServiceFactory.getClient(configRegion); - GetCallerIdentityResult callerIdentity = securityTokenService.getCallerIdentity( - new GetCallerIdentityRequest()); - String rootUserArn = String.format("arn:aws:iam::%s:root", callerIdentity.getAccount()); + String rootUserArn = String.format("arn:aws:iam::%s:root", getAccountId()); EnvironmentData environmentData = new EnvironmentData(); environmentData.setEnvironmentName(environmentName); @@ -792,6 +789,13 @@ public void initializeEnvironment(String adminRoleArn, saveEnvironmentData(environmentData); } + public String getAccountId() { + AWSSecurityTokenService securityTokenService = securityTokenServiceFactory.getClient(configRegion); + GetCallerIdentityResult callerIdentity = securityTokenService.getCallerIdentity( + new GetCallerIdentityRequest()); + return callerIdentity.getAccount(); + } + public String getConfigBucketForRegion(Regions region) { if (!getDecryptedEnvironmentData().getRegionData().containsKey(region)) { throw new RuntimeException("There is no region data for region: " + region.getName()); diff --git a/src/main/resources/cloudformation/waf-logging.yaml b/src/main/resources/cloudformation/waf-logging.yaml new file mode 100644 index 00000000..fcc2cbb5 --- /dev/null +++ b/src/main/resources/cloudformation/waf-logging.yaml @@ -0,0 +1,80 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Enables logging for the Web Application Firewall (WAF) +Outputs: + kinesisFirehoseDeliveryStreamARN: + Value: !GetAtt KinesisFirehoseDeliveryStream.Arn + kinesisFirehoseDeliveryStreamName: + Value: !Ref KinesisFirehoseDeliveryStream +Parameters: + environmentName: + Description: The Cerberus environment name. + Type: String +Resources: + KinesisFirehoseDeliveryStream: + Type: 'AWS::KinesisFirehose::DeliveryStream' + Properties: + DeliveryStreamName: !Join + - '' + - - 'aws-waf-logs-' + - !Ref environmentName + - '-CerberusWafLogDeliveryStream' + DeliveryStreamType: DirectPut + S3DestinationConfiguration: + BucketARN: !Join + - '' + - - 'arn:aws:s3:::' + - !Ref S3DestinationBucket + BufferingHints: + IntervalInSeconds: 300 + SizeInMBs: 5 + CompressionFormat: UNCOMPRESSED + Prefix: firehose/ + RoleARN: !GetAtt DeliveryRole.Arn + S3DestinationBucket: + Type: AWS::S3::Bucket + Properties: + AccessControl: Private + VersioningConfiguration: + Status: Enabled + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + DeliveryPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: firehose-delivery-policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 's3:AbortMultipartUpload' + - 's3:GetBucketLocation' + - 's3:GetObject' + - 's3:ListBucket' + - 's3:ListBucketMultipartUploads' + - 's3:PutObject' + Resource: + - !Join + - '' + - - 'arn:aws:s3:::' + - !Ref S3DestinationBucket + - !Join + - '' + - - 'arn:aws:s3:::' + - !Ref S3DestinationBucket + - '*' + Roles: + - !Ref DeliveryRole + DeliveryRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: '' + Effect: Allow + Principal: + Service: firehose.amazonaws.com + Action: 'sts:AssumeRole' \ No newline at end of file diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml index 0e34cd1d..a4876f40 100644 --- a/src/main/resources/cloudformation/web-app-firewall.yaml +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -9,6 +9,8 @@ Outputs: Value: !Ref 'WAFManualBlockSet' whitelistIPSetID: Value: !Ref 'WAFWhitelistSet' + webAclID: + Value: !Ref 'CerberusWAFWebAcl' Parameters: loadBalancerStackName: Description: The name of the Cerberus load balancer CloudFormation stack