From 0b869c44f21f8eba2e6fe5d4355bfdaa089eae54 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Fri, 29 Sep 2017 11:27:36 -0700 Subject: [PATCH 01/69] Adding PKCS8 key to upload-certs command --- .../com/nike/cerberus/ConfigConstants.java | 2 ++ .../core/UploadCertFilesOperation.java | 4 ++- .../com/nike/cerberus/store/ConfigStore.java | 33 +++++++++++-------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 1c26b761..c6687831 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -40,6 +40,8 @@ public class ConfigConstants { public static final String CERT_PART_CERT = "cert.pem"; + public static final String CERT_PART_PKCS8_KEY = "pkcs8-key.pem"; + public static final String CERT_PART_KEY = "key.pem"; public static final String CERT_PART_PUBKEY = "pubkey.pem"; diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index 161357dc..8c938d67 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -46,6 +46,7 @@ public class UploadCertFilesOperation implements Operation EXPECTED_FILE_NAMES = ImmutableSet.of(CA_FILE_NAME, CERT_FILE_NAME, KEY_FILE_NAME, PUB_KEY_FILE_NAME); @@ -77,6 +78,7 @@ public void run(final UploadCertFilesCommand command) { final String caContents = getFileContents(certPath, CA_FILE_NAME); final String certContents = getFileContents(certPath, CERT_FILE_NAME); final String keyContents = getFileContents(certPath, KEY_FILE_NAME); + final String pkcs8KeyContents = getFileContents(certPath, PKCS8_KEY_FILE_NAME); final String pubKeyContents = getFileContents(certPath, PUB_KEY_FILE_NAME); final String certificateName = stackName.getName() + "_" + uuidSupplier.get(); @@ -87,7 +89,7 @@ public void run(final UploadCertFilesCommand command) { logger.info("Cert ID: {}", id); logger.info("Uploading certificate parts to the configuration bucket."); - configStore.storeCert(stackName, certificateName, caContents, certContents, keyContents, pubKeyContents); + configStore.storeCert(stackName, certificateName, caContents, certContents, keyContents, pkcs8KeyContents, pubKeyContents); logger.info("Uploading certificate completed."); } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 353e4ab6..ada73289 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -44,10 +44,10 @@ import com.nike.cerberus.domain.configuration.VaultAclEntry; import com.nike.cerberus.domain.configuration.VaultConfiguration; import com.nike.cerberus.domain.environment.BackupRegionInfo; +import com.nike.cerberus.domain.environment.CloudFrontLogProcessingLambdaConfig; import com.nike.cerberus.domain.environment.Environment; import com.nike.cerberus.domain.environment.Secrets; import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.domain.environment.CloudFrontLogProcessingLambdaConfig; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.IdentityManagementService; import com.nike.cerberus.service.S3StoreService; @@ -70,6 +70,7 @@ import static com.nike.cerberus.ConfigConstants.CERT_PART_CA; import static com.nike.cerberus.ConfigConstants.CERT_PART_CERT; import static com.nike.cerberus.ConfigConstants.CERT_PART_KEY; +import static com.nike.cerberus.ConfigConstants.CERT_PART_PKCS8_KEY; import static com.nike.cerberus.ConfigConstants.CERT_PART_PUBKEY; import static com.nike.cerberus.ConfigConstants.CMS_ROLE_ARN_KEY; import static com.nike.cerberus.ConfigConstants.JDBC_PASSWORD_KEY; @@ -160,7 +161,7 @@ public void storeAzs(final String az1, final String az2, final String az3) { * Stores the stack ID for a specific component. * * @param stackName Stack component - * @param stackId Stack ID + * @param stackId Stack ID */ public void storeStackId(final StackName stackName, final String stackId) { synchronized (envDataLock) { @@ -501,22 +502,24 @@ public Optional getCertPart(final StackName stackName, final String part /** * Stores the certificate files encrypted and adds the certificate name to the environment data. * - * @param stackName Stack that the cert is for + * @param stackName Stack that the cert is for * @param certificateName Certificate name in IAM - * @param caContents CA chain - * @param certContents Certificate body - * @param keyContents Certificate key - * @param pubKeyContents Certificate public key + * @param caContents CA chain + * @param certContents Certificate body + * @param keyContents Certificate key + * @param pubKeyContents Certificate public key */ public void storeCert(final StackName stackName, final String certificateName, final String caContents, final String certContents, final String keyContents, + final String pkcs8KeyContents, final String pubKeyContents) { saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_CA), caContents); saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_CERT), certContents); saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_KEY), keyContents); + saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_PKCS8_KEY), pkcs8KeyContents); saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_PUBKEY), pubKeyContents); synchronized (envDataLock) { @@ -542,6 +545,7 @@ public Optional getCmsEnvConfig() { /** * Get the CMS environment properties + * * @return CMS properties */ public Properties getAllExistingCmsEnvProperties() { @@ -564,6 +568,7 @@ public Properties getAllExistingCmsEnvProperties() { /** * Get generated CMS properties that are not set by the user + * * @return - System configured properties */ public Properties getCmsSystemProperties() { @@ -602,6 +607,7 @@ public String getCerberusBaseUrl() { /** * Get existing CMS properties configured by the user + * * @return - User configured properties */ public Properties getExistingCmsUserProperties() { @@ -610,7 +616,7 @@ public Properties getExistingCmsUserProperties() { Properties existingProperties = getAllExistingCmsEnvProperties(); existingProperties.forEach((key, value) -> { - if (! SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(key)) { + if (!SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(key)) { properties.put(key, value); } }); @@ -620,6 +626,7 @@ public Properties getExistingCmsUserProperties() { /** * Return configuration file contents + * * @param path - Path to configuration file (e.g. 'config/environment.properties') */ public Optional getConfigProperties(String path) { @@ -720,9 +727,9 @@ public GatewayOutputs getGatewayStackOutputs() { /** * Get the stack outputs for a specific stack name. * - * @param stackName Stack name + * @param stackName Stack name * @param outputClass Outputs class - * @param Outputs type + * @param Outputs type * @return Outputs */ public M getStackOutputs(final StackName stackName, final Class outputClass) { @@ -742,9 +749,9 @@ public M getStackOutputs(final StackName stackName, final Class outputCla /** * Get the stack parameters for a specific stack name. * - * @param stackName Stack name + * @param stackName Stack name * @param parameterClass Parameters class - * @param Parameters type + * @param Parameters type * @return Parameters */ public M getStackParameters(final StackName stackName, final Class parameterClass) { @@ -947,7 +954,7 @@ public Optional getBackupInfoForRegion(String region) { } } - public Map getRegionBackupBucketMap() { + public Map getRegionBackupBucketMap() { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); return environment.getRegionBackupBucketMap(); From b2936dd805490d818ba7fdadcaf1a3d34dc8ee57 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Tue, 3 Oct 2017 13:03:11 -0700 Subject: [PATCH 02/69] Adding create-cms-cmk command --- .../com/nike/cerberus/ConfigConstants.java | 5 +- .../com/nike/cerberus/cli/CerberusRunner.java | 2 + .../command/cms/CreateCmsCmkCommand.java | 69 ++++++++ .../operation/cms/CreateCmsCmkOperation.java | 166 ++++++++++++++++++ .../cerberus/service/KmsClientFactory.java | 51 ++++++ .../cerberus/service/KmsPolicyGenerator.java | 131 ++++++++++++++ .../com/nike/cerberus/service/KmsService.java | 114 ++++++++++++ .../com/nike/cerberus/store/ConfigStore.java | 26 ++- 8 files changed, 559 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java create mode 100644 src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java create mode 100644 src/main/java/com/nike/cerberus/service/KmsClientFactory.java create mode 100644 src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java create mode 100644 src/main/java/com/nike/cerberus/service/KmsService.java diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index c6687831..6347a9ee 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -100,6 +100,8 @@ public class ConfigConstants { public static final String JDBC_PASSWORD_KEY ="JDBC.password"; + public static final String CMK_ARNS_KEY = "cms.encryption.cmk.arns"; + public static final ImmutableSet SYSTEM_CONFIGURED_CMS_PROPERTIES = ImmutableSet.of( VAULT_ADDR_KEY, VAULT_TOKEN_KEY, @@ -108,7 +110,8 @@ public class ConfigConstants { CMS_ROLE_ARN_KEY, JDBC_URL_KEY, JDBC_USERNAME_KEY, - JDBC_PASSWORD_KEY); + JDBC_PASSWORD_KEY, + CMK_ARNS_KEY); public static final String CERBERUS_AMI_TAG_NAME = "tag:cerberus_component"; diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index ca6513e7..075a0e6f 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -28,6 +28,7 @@ import com.nike.cerberus.command.CerberusCommand; import com.nike.cerberus.command.Command; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; +import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; @@ -191,6 +192,7 @@ private void registerAllCommands() { registerCommand(new CreateCmsVaultTokenCommand()); registerCommand(new CreateCmsConfigCommand()); registerCommand(new CreateCmsClusterCommand()); + registerCommand(new CreateCmsCmkCommand()); registerCommand(new CreateGatewayConfigCommand()); registerCommand(new CreateGatewayClusterCommand()); registerCommand(new UpdateStackCommand()); diff --git a/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java b/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java new file mode 100644 index 00000000..b6406535 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 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.cms; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.cms.CreateCmsCmkOperation; + +import java.util.List; + +import static com.nike.cerberus.command.cms.UpdateCmsConfigCommand.COMMAND_NAME; + +/** + * Command to create the CMS cluster. + */ +@Parameters(commandNames = COMMAND_NAME, commandDescription = "Updates the CMS config.") +public class CreateCmsCmkCommand implements Command { + + public static final String COMMAND_NAME = "create-cms-cmk"; + public static final String ADDITIONAL_REGIONS_ARG = "--additional-regions"; + public static final String ROTATE_ARG = "--rotate"; + + @Parameter(names = ADDITIONAL_REGIONS_ARG, + description = "Additional regions to create the CMS CMK in. At least one additional region, other than the primary region where Cerberus will be running, is required.", + variableArity = true, + required = true + ) + private List additionalRegions; + + @Parameter(names = ROTATE_ARG, + description = "Manually rotates the CMKs while leaving the old CMKs in place so they can still be used. " + + "This is generally unnecessary since AWS will automatically rotate keys previously created with this command. " + ) + private boolean rotate = false; + + public List getAdditionalRegions() { + return additionalRegions; + } + + public boolean isRotate() { + return rotate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateCmsCmkOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java new file mode 100644 index 00000000..23b24ab7 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 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.cms; + +import com.amazonaws.regions.Regions; +import com.beust.jcommander.ParameterException; +import com.google.common.collect.Lists; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.cms.CreateCmsCmkCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.KmsPolicyGenerator; +import com.nike.cerberus.service.KmsService; +import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.Date; +import java.util.List; +import java.util.Properties; + +import static com.nike.cerberus.ConfigConstants.CMK_ARNS_KEY; + +/** + * Create CMKs in multiple regions for CMS to use. + *

+ * We don't want this as part of the base CloudFormation YAML because we are operating on multiple regions. + */ +public class CreateCmsCmkOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final ConfigStore configStore; + private final EnvironmentMetadata environmentMetadata; + private final KmsService kmsService; + + @Inject + public CreateCmsCmkOperation(final ConfigStore configStore, + final EnvironmentMetadata environmentMetadata, + final KmsService kmsService) { + this.configStore = configStore; + this.environmentMetadata = environmentMetadata; + this.kmsService = kmsService; + } + + @Override + public void run(final CreateCmsCmkCommand command) { + // load the existing configuration + logger.info("Retrieving configuration data from the configuration bucket."); + final Properties cmsConfigProperties = configStore.getAllExistingCmsEnvProperties(); + + // additional argument validation + validateRegionsArg(command); + validateRotateArg(command, cmsConfigProperties); + + // create the CMKs + List cmkArns = createCmks(command, cmsConfigProperties); + + // store the new configuration + cmsConfigProperties.put(CMK_ARNS_KEY, StringUtils.join(cmkArns, ",")); + logger.info("Uploading the CMS configuration to the configuration bucket."); + configStore.storeCmsEnvConfig(cmsConfigProperties); + logger.info("Uploading complete."); + } + + @Override + public boolean isRunnable(final CreateCmsCmkCommand command) { + boolean isRunnable = configStore.getCmsEnvConfig().isPresent(); + + if (!isRunnable) { + logger.warn("CMS config does not exist, please use 'create-cms-config' command first."); + } + + return isRunnable; + } + + private void validateRotateArg(CreateCmsCmkCommand command, Properties cmsConfigProperties) { + if (command.isRotate()) { + if (cmsConfigProperties.containsKey(CMK_ARNS_KEY)) { + logger.info("Overwriting existing CMK ARNs property: " + cmsConfigProperties.getProperty(CMK_ARNS_KEY)); + logger.info("Existing CMKs will not be deleted and may be needed to decrypt existing secrets"); + } else { + throw new ParameterException(CreateCmsCmkCommand.ROTATE_ARG + " was specified but there is no existing CMK to rotate"); + } + } else { + if (cmsConfigProperties.containsKey(CMK_ARNS_KEY)) { + logger.info("Property already exists: " + CMK_ARNS_KEY + "=" + cmsConfigProperties.get(CMK_ARNS_KEY)); + throw new ParameterException("CMK already exists but " + CreateCmsCmkCommand.ROTATE_ARG + " was not specified. Generally, manual rotation is not necessary."); + } else { + logger.info("Will add CMK ARNs property: " + CMK_ARNS_KEY); + } + } + } + + private void validateRegionsArg(CreateCmsCmkCommand command) { + String primaryRegion = environmentMetadata.getRegionName(); + List regions = command.getAdditionalRegions(); + if (regions.contains(primaryRegion)) { + throw new ParameterException("Additional regions should not contain the primary region for the environment"); + } + } + + private List createCmks(CreateCmsCmkCommand command, Properties cmsConfigProperties) { + String envName = environmentMetadata.getName(); + String primaryRegion = environmentMetadata.getRegionName(); + + String alias = generateAliasName(envName, primaryRegion); + + String description = "Generated for the Cerberus " + envName + " environment running in " + primaryRegion; + String policyAsJson = generateKeyPolicy(cmsConfigProperties, description); + logger.info("Generated the following policy:\n" + policyAsJson); + + List cmkRegions = getTargetRegions(command); + return kmsService.createKeysAndAliases(cmkRegions, alias, policyAsJson, description); + } + + private String generateAliasName(String envName, String primaryRegion) { + return "alias/cerberus/cms-" + envName + "-" + primaryRegion + "-" + DateFormatUtils.format(new Date(), "yyyy-MM-dd-HH-mm"); + } + + private String generateKeyPolicy(Properties cmsConfigProperties, String description) { + String rootUserArn = getRequiredProperty(cmsConfigProperties, ConfigConstants.ROOT_USER_ARN_KEY); + String adminUserArn = getRequiredProperty(cmsConfigProperties, ConfigConstants.ADMIN_ROLE_ARN_KEY); + String cmsRoleArn = getRequiredProperty(cmsConfigProperties, ConfigConstants.CMS_ROLE_ARN_KEY); + + KmsPolicyGenerator generator = new KmsPolicyGenerator() + .withDescription(description) + .withAdminArns(Lists.newArrayList(rootUserArn, adminUserArn)) + .withCmsArn(cmsRoleArn); + + return generator.generatePolicyJson(); + } + + private String getRequiredProperty(Properties cmsConfigProperties, String propertyName) { + String value = cmsConfigProperties.getProperty(propertyName); + Validate.notBlank(value, "CMS config value " + propertyName + " was not found!"); + return value; + } + + private List getTargetRegions(CreateCmsCmkCommand command) { + // make sure primary region is first in the list + List cmkRegions = Lists.newArrayList(environmentMetadata.getRegions()); + for (String region : command.getAdditionalRegions()) { + cmkRegions.add(Regions.fromName(region)); + } + return cmkRegions; + } +} diff --git a/src/main/java/com/nike/cerberus/service/KmsClientFactory.java b/src/main/java/com/nike/cerberus/service/KmsClientFactory.java new file mode 100644 index 00000000..0f23ec1c --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/KmsClientFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 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.kms.AWSKMS; +import com.amazonaws.services.kms.AWSKMSClientBuilder; +import com.beust.jcommander.internal.Maps; + +import javax.inject.Singleton; +import java.util.Map; + +/** + * Factory that caches kmsClients for re-use. + */ +@Singleton +public class KmsClientFactory { + + /** + * Cache of kmsClients by region + */ + private Map kmsClients = Maps.newHashMap(); + + public AWSKMS getClient(String regionName) { + return getClient(Regions.fromName(regionName)); + } + + /** + * Factory that caches kmsClients for re-use. + */ + public AWSKMS getClient(Regions region) { + if (!kmsClients.containsKey(region)) { + kmsClients.put(region, AWSKMSClientBuilder.standard().withRegion(region).build()); + } + return kmsClients.get(region); + } +} diff --git a/src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java b/src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java new file mode 100644 index 00000000..96256c21 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017 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.auth.policy.Policy; +import com.amazonaws.auth.policy.Principal; +import com.amazonaws.auth.policy.Resource; +import com.amazonaws.auth.policy.Statement; +import com.amazonaws.auth.policy.actions.KMSActions; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Generate a Key Policy for Customer Master Key (CMK) in KMS. + */ +public class KmsPolicyGenerator { + + private static final String AWS_PROVIDER = "AWS"; + private static final String KEY_ADMINS = "Key admins can manage this CMK"; + private static final String CMS_USER = "CMS can use this CMK"; + + private static final Principal[] NONE = new Principal[0]; + + private String description; + private List adminArns; + private String cmsArn; + + /** + * Sid description + */ + public KmsPolicyGenerator withDescription(String description) { + this.description = description; + return this; + } + + public KmsPolicyGenerator withAdminArns(List adminArns) { + this.adminArns = adminArns; + return this; + } + + public KmsPolicyGenerator withCmsArn(String cmsArn) { + this.cmsArn = cmsArn; + return this; + } + + /** + * Generate policy as a JSON String + */ + public String generatePolicyJson() { + Policy policy = generatePolicy(); + + // Let AWS libraries generate the JSON + String awsJson = policy.toJson(); + + // Pretty print the AWS JSON for our users + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + return gson.toJson(gson.fromJson(awsJson, JsonObject.class)); + } + + /** + * Generate a Key Policy + */ + protected Policy generatePolicy() { + Policy policy = new Policy(); + policy.withId(description); + + Statement keyAdminsStatement = new Statement(Statement.Effect.Allow); + keyAdminsStatement.withId(KEY_ADMINS); + keyAdminsStatement.withPrincipals(arnsToPrincipals(adminArns)); + keyAdminsStatement.withActions(KMSActions.AllKMSActions); + keyAdminsStatement.withResources(new Resource("*")); + + Statement cmsStatement = new Statement(Statement.Effect.Allow); + cmsStatement.withId(CMS_USER); + cmsStatement.withPrincipals(arnToPrincipal(cmsArn)); + cmsStatement.withActions( + // To Use the Key + KMSActions.Encrypt, + KMSActions.Decrypt, + KMSActions.ReEncryptTo, + KMSActions.ReEncryptFrom, + KMSActions.GenerateDataKey, + KMSActions.GenerateDataKeyWithoutPlaintext + ); + cmsStatement.withResources(new Resource("*")); + + policy.withStatements( + keyAdminsStatement, + cmsStatement + ); + + return policy; + } + + /** + * Convert ARNs to principal objects + */ + protected Principal[] arnsToPrincipals(List arns) { + if (arns.isEmpty()) { + return NONE; + } else { + return arns.stream() + .map(this::arnToPrincipal) + .collect(Collectors.toList()) + .toArray(NONE); + } + } + + protected Principal arnToPrincipal(String arn) { + return new Principal(AWS_PROVIDER, arn, false); + } +} diff --git a/src/main/java/com/nike/cerberus/service/KmsService.java b/src/main/java/com/nike/cerberus/service/KmsService.java new file mode 100644 index 00000000..d89b3002 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/KmsService.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 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.kms.AWSKMS; +import com.amazonaws.services.kms.model.CreateAliasRequest; +import com.amazonaws.services.kms.model.CreateKeyRequest; +import com.amazonaws.services.kms.model.CreateKeyResult; +import com.amazonaws.services.kms.model.EnableKeyRotationRequest; +import com.amazonaws.services.kms.model.KeyMetadata; +import com.beust.jcommander.internal.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.List; + +/** + * Convenience methods for calls to KMS + */ +@Singleton +public class KmsService { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private KmsClientFactory kmsClientFactory; + + @Inject + public KmsService(KmsClientFactory kmsClientFactory) { + this.kmsClientFactory = kmsClientFactory; + } + + public List createKeysAndAliases(List regions, String alias, String policy, String description) { + List keyArns = Lists.newArrayList(); + for (Regions region : regions) { + String keyArn = createKey(region, policy, description, alias); + keyArns.add(keyArn); + } + return keyArns; + } + + /** + * Create a CMK in the supplied region with the supplied policy and description + * + * @param region the target region + * @param policy the default key poicy + * @param description the Key description + * @param alias the CMK alias to generate for this key + * @return cmkArn + */ + private String createKey(Regions region, String policy, String description, String alias) { + + AWSKMS client = kmsClientFactory.getClient(region); + + CreateKeyRequest request = new CreateKeyRequest(); + request.setPolicy(policy); + request.setDescription(description); + CreateKeyResult result = client.createKey(request); + KeyMetadata keyMetadata = result.getKeyMetadata(); + + String keyArn = keyMetadata.getArn(); + String keyId = keyMetadata.getKeyId(); + + logger.info(String.format("Created CMK '%s' in region '%s'", keyArn, region)); + + enableKeyRotation(region, keyId); + createAlias(region, keyId, alias); + + return keyArn; + } + + + /** + * Ensure CMK key rotation is enabled for the supplied key + */ + private void enableKeyRotation(Regions region, String keyId) { + AWSKMS client = kmsClientFactory.getClient(region); + + client.enableKeyRotation(new EnableKeyRotationRequest().withKeyId(keyId)); + logger.info(String.format("Enabled key rotation for '%s' in '%s'", keyId, region)); + + } + + /** + * Create a CMK Alias for the supplied region and keyId + */ + private void createAlias(Regions region, String keyId, String alias) { + AWSKMS client = kmsClientFactory.getClient(region); + + CreateAliasRequest request = new CreateAliasRequest(); + request.setAliasName(alias); + request.setTargetKeyId(keyId); + client.createAlias(request); + + logger.info(String.format("Created Alias '%s' for key '%s' in region '%s'", alias, keyId, region)); + } + +} diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index ada73289..b7087289 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -559,19 +559,17 @@ public Properties getAllExistingCmsEnvProperties() { } catch (IOException ioe) { throw new IllegalStateException("Failed to read CMS properties"); } - } else { - throw new IllegalStateException("Failed to read CMS properties"); } return properties; } /** - * Get generated CMS properties that are not set by the user + * Generate CMS properties that are not set by the user * * @return - System configured properties */ - public Properties getCmsSystemProperties() { + private Properties generateBaseCmsSystemProperties() { final BaseOutputs baseOutputs = getBaseStackOutputs(); final BaseParameters baseParameters = getBaseStackParameters(); @@ -605,6 +603,26 @@ public String getCerberusBaseUrl() { return String.format("https://%s", getGatewayStackParamters().getHostname()); } + /** + * System properties not set with -P param + */ + public Properties getCmsSystemProperties() { + + Properties properties = new Properties(); + Properties existingProperties = getAllExistingCmsEnvProperties(); + + // overwrite any of the automatically generated properties that may have changed + existingProperties.putAll(generateBaseCmsSystemProperties()); + + existingProperties.forEach((key, value) -> { + if (SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(key)) { + properties.put(key, value); + } + }); + + return properties; + } + /** * Get existing CMS properties configured by the user * From ae33b2bcc8ac0b91e928278f4abc953899b9ad31 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Tue, 3 Oct 2017 15:11:58 -0700 Subject: [PATCH 03/69] Adding automatic salt generation --- .../com/nike/cerberus/ConfigConstants.java | 5 ++++- .../nike/cerberus/service/SaltGenerator.java | 22 +++++++++++++++++++ .../com/nike/cerberus/store/ConfigStore.java | 11 ++++++++++ .../cerberus/service/SaltGeneratorTest.java | 20 +++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/nike/cerberus/service/SaltGenerator.java create mode 100644 src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 6347a9ee..415365e7 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -102,6 +102,8 @@ public class ConfigConstants { public static final String CMK_ARNS_KEY = "cms.encryption.cmk.arns"; + public static final String HASH_SALT = "auth.token.hashSalt"; + public static final ImmutableSet SYSTEM_CONFIGURED_CMS_PROPERTIES = ImmutableSet.of( VAULT_ADDR_KEY, VAULT_TOKEN_KEY, @@ -111,7 +113,8 @@ public class ConfigConstants { JDBC_URL_KEY, JDBC_USERNAME_KEY, JDBC_PASSWORD_KEY, - CMK_ARNS_KEY); + CMK_ARNS_KEY, + HASH_SALT); public static final String CERBERUS_AMI_TAG_NAME = "tag:cerberus_component"; diff --git a/src/main/java/com/nike/cerberus/service/SaltGenerator.java b/src/main/java/com/nike/cerberus/service/SaltGenerator.java new file mode 100644 index 00000000..b19a4872 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/SaltGenerator.java @@ -0,0 +1,22 @@ +package com.nike.cerberus.service; + +import java.security.SecureRandom; +import java.util.Base64; +import java.util.Random; + +/** + * Generate a Salt + */ +public class SaltGenerator { + + private Random random = new SecureRandom(); + + /** + * Generate a Salt as a Base64 encoded String we can store in a properties file + */ + public String generateSalt() { + byte[] salt = new byte[64]; + random.nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } +} diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index b7087289..3b48d26e 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -51,6 +51,7 @@ import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.IdentityManagementService; import com.nike.cerberus.service.S3StoreService; +import com.nike.cerberus.service.SaltGenerator; import com.nike.cerberus.service.StoreService; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -60,6 +61,7 @@ import javax.inject.Named; import java.io.IOException; import java.io.StringReader; +import java.security.SecureRandom; import java.util.List; import java.util.Map; import java.util.Optional; @@ -73,6 +75,7 @@ import static com.nike.cerberus.ConfigConstants.CERT_PART_PKCS8_KEY; import static com.nike.cerberus.ConfigConstants.CERT_PART_PUBKEY; import static com.nike.cerberus.ConfigConstants.CMS_ROLE_ARN_KEY; +import static com.nike.cerberus.ConfigConstants.HASH_SALT; import static com.nike.cerberus.ConfigConstants.JDBC_PASSWORD_KEY; import static com.nike.cerberus.ConfigConstants.JDBC_URL_KEY; import static com.nike.cerberus.ConfigConstants.JDBC_USERNAME_KEY; @@ -104,6 +107,8 @@ public class ConfigStore { private final EnvironmentMetadata environmentMetadata; + private final SaltGenerator saltGenerator; + private final Object envDataLock = new Object(); private final Object secretsDataLock = new Object(); @@ -120,6 +125,7 @@ public ConfigStore(final AmazonS3 s3Client, final IdentityManagementService iamService, final AWSSecurityTokenService securityTokenService, final EnvironmentMetadata environmentMetadata, + final SaltGenerator saltGenerator, @Named(CONFIG_OBJECT_MAPPER) final ObjectMapper configObjectMapper, @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudFormationObjectMapper) { @@ -129,6 +135,7 @@ public ConfigStore(final AmazonS3 s3Client, this.cloudFormationObjectMapper = cloudFormationObjectMapper; this.s3Client = s3Client; this.environmentMetadata = environmentMetadata; + this.saltGenerator = saltGenerator; this.securityTokenService = securityTokenService; } @@ -620,6 +627,10 @@ public Properties getCmsSystemProperties() { } }); + if (!properties.containsKey(HASH_SALT)) { + properties.put(HASH_SALT, saltGenerator.generateSalt()); + } + return properties; } diff --git a/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java b/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java new file mode 100644 index 00000000..aa1f6094 --- /dev/null +++ b/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java @@ -0,0 +1,20 @@ +package com.nike.cerberus.service; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class SaltGeneratorTest { + + @Test + public void sanityTestSalt() { + SaltGenerator generator = new SaltGenerator(); + String s1 = generator.generateSalt(); + String s2 = generator.generateSalt(); + assertFalse(StringUtils.equals(s1, s2)); + assertEquals(88, s1.length()); + assertEquals(88, s2.length()); + } +} \ No newline at end of file From 223022123ad8b1e7825a07e79c3cc43c532acf4b Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Thu, 5 Oct 2017 11:47:13 -0700 Subject: [PATCH 04/69] Adding required file to list --- .../nike/cerberus/operation/core/UploadCertFilesOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index 8c938d67..974ee597 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -48,7 +48,7 @@ public class UploadCertFilesOperation implements Operation EXPECTED_FILE_NAMES = ImmutableSet.of(CA_FILE_NAME, CERT_FILE_NAME, KEY_FILE_NAME, PUB_KEY_FILE_NAME); + public static final Set EXPECTED_FILE_NAMES = ImmutableSet.of(CA_FILE_NAME, CERT_FILE_NAME, KEY_FILE_NAME, PKCS8_KEY_FILE_NAME, PUB_KEY_FILE_NAME); private final Logger logger = LoggerFactory.getLogger(getClass()); From 079d5555260f12cbff7e7a225a40c1dc8bbb5343 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 20 Oct 2017 13:52:02 -0700 Subject: [PATCH 05/69] Remove troposphere in favor of static CF templates (#66) * Remove troposphere in favor of static CF templates --- .gitignore | 5 - build.gradle | 55 - smaas-cf/requirements.txt | 2 - smaas-cf/smaas/__init__.py | 1 - ...front-elb-security-group-updater-lambda.py | 148 -- smaas-cf/smaas/cms-cluster.py | 333 ---- smaas-cf/smaas/consul-cluster.py | 210 --- smaas-cf/smaas/support/constants.py | 3 - smaas-cf/smaas/support/network.py | 98 -- smaas-cf/smaas/support/tags.py | 70 - smaas-cf/smaas/vault-cluster.py | 379 ----- smaas-cf/smaas/vpc-and-base.py | 1418 ----------------- src/main/resources/cloudformation/.gitkeep | 1 - src/main/resources/cloudformation/base.yaml | 161 ++ .../resources/cloudformation/cms-cluster.yaml | 131 ++ .../resources/cloudformation/database.yaml | 108 ++ .../cloudformation/load-balancer.yaml | 147 ++ .../resources/cloudformation/route53.yaml | 28 + .../cloudformation/security-groups.yaml | 145 ++ src/main/resources/cloudformation/vpc.yaml | 212 +++ .../cloudformation/web-app-firewall.yaml | 179 +++ 21 files changed, 1111 insertions(+), 2723 deletions(-) delete mode 100644 smaas-cf/requirements.txt delete mode 100644 smaas-cf/smaas/__init__.py delete mode 100644 smaas-cf/smaas/cloudfront-elb-security-group-updater-lambda.py delete mode 100644 smaas-cf/smaas/cms-cluster.py delete mode 100644 smaas-cf/smaas/consul-cluster.py delete mode 100644 smaas-cf/smaas/support/constants.py delete mode 100644 smaas-cf/smaas/support/network.py delete mode 100644 smaas-cf/smaas/support/tags.py delete mode 100644 smaas-cf/smaas/vault-cluster.py delete mode 100644 smaas-cf/smaas/vpc-and-base.py delete mode 100644 src/main/resources/cloudformation/.gitkeep create mode 100644 src/main/resources/cloudformation/base.yaml create mode 100644 src/main/resources/cloudformation/cms-cluster.yaml create mode 100644 src/main/resources/cloudformation/database.yaml create mode 100644 src/main/resources/cloudformation/load-balancer.yaml create mode 100644 src/main/resources/cloudformation/route53.yaml create mode 100644 src/main/resources/cloudformation/security-groups.yaml create mode 100644 src/main/resources/cloudformation/vpc.yaml create mode 100644 src/main/resources/cloudformation/web-app-firewall.yaml diff --git a/.gitignore b/.gitignore index 8900dc55..8741515e 100644 --- a/.gitignore +++ b/.gitignore @@ -74,8 +74,3 @@ gradle-app.setting # Python cache **/__pycache__/ - -# Ignore generated CF templates -src/main/resources/cloudformation/** -!src/main/resources/cloudformation/.gitkeep -/smaas-cf/.idea/ diff --git a/build.gradle b/build.gradle index 1ed6a58a..4ff6d3c6 100644 --- a/build.gradle +++ b/build.gradle @@ -35,32 +35,6 @@ task wrapper (type: Wrapper) { * limitations under the License. */ -/** - * Executes the python code that uses Troposphere to generate the cloud formation needed to - */ -task createCloudFormation() { - try { - def requirementsPath = "${getProject().getRootDir()}${File.separator}smaas-cf${File.separator}requirements.txt" - executeOnShell "pip3 install -r ${requirementsPath}" - - [ - 'consul-cluster.py', - 'vault-cluster.py', - 'vpc-and-base.py', - 'gateway-cluster.py', - 'cms-cluster.py', - 'cloudfront-elb-security-group-updater-lambda.py' - ].each { String script -> - executeOnShell " PYTHONPATH=${getProject().getRootDir()}${File.separator}smaas-cf python3 ${script}", new File("${getProject().getRootDir()}${File.separator}smaas-cf${File.separator}smaas${File.separator}"), true - } - } catch (Throwable t) { - logger.lifecycle t.getMessage() - } -} - -tasks.shadowJar.dependsOn createCloudFormation -tasks.assemble.dependsOn createCloudFormation - shadowJar { def releaseVersion = version doFirst { @@ -72,32 +46,3 @@ shadowJar { } tasks.assemble.finalizedBy shadowJar - -/** - * Runs a command on the command line, streaming the output to STDOUT - */ -def executeOnShell(String command, File workingDir = new File("./"), boolean print = false) { - logger.debug "Executing command: ${command}" - def commandArray = new String[3] - commandArray[0] = "sh" - commandArray[1] = "-c" - commandArray[2] = command - def process = new ProcessBuilder(commandArray) - .directory(workingDir) - .redirectErrorStream(true) - .start() - def stdout = [] - process.inputStream.eachLine { line -> - stdout.add line - if (print) { - logger.lifecycle line - } - } - process.waitFor(); - if (process.exitValue()) { - def msg = "Failed to execute command exiting" - logger.lifecycle msg - throw new GradleException(msg) - } - return stdout -} diff --git a/smaas-cf/requirements.txt b/smaas-cf/requirements.txt deleted file mode 100644 index 1011592b..00000000 --- a/smaas-cf/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -boto3==1.3.1 -troposphere==1.8.1 diff --git a/smaas-cf/smaas/__init__.py b/smaas-cf/smaas/__init__.py deleted file mode 100644 index 480e9246..00000000 --- a/smaas-cf/smaas/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Placeholder to force package structure diff --git a/smaas-cf/smaas/cloudfront-elb-security-group-updater-lambda.py b/smaas-cf/smaas/cloudfront-elb-security-group-updater-lambda.py deleted file mode 100644 index 05169795..00000000 --- a/smaas-cf/smaas/cloudfront-elb-security-group-updater-lambda.py +++ /dev/null @@ -1,148 +0,0 @@ -### -# Copyright (c) 2016 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. -### - -from io import open -from troposphere import Ref, Template, GetAtt, AWS_ACCOUNT_ID, Parameter, Output -from troposphere.iam import Role, Policy -from troposphere.awslambda import Function, Code, Permission - -CF_FILE = '../../src/main/resources/cloudformation/cloudfront-elb-security-group-updater-lambda.json' -CLOUD_FRONT_PREFIX = 'cf-logs/' - -# Create Template -template = Template() -template.description = "Launches the gateway cluster in the Cerberus VPC" -template.version = '2010-09-09' - -### -# -# Inputs -# -### - -lambda_bucket = template.add_parameter(Parameter( - "lambdaBucket", - Type="String", - Description="S3 Bucket for lambda function artifact" -)) - -lambda_key = template.add_parameter(Parameter( - "lambdaKey", - Type="String", - Description="Key for lambda function artifact" -)) - - -### -# -# IAM roles -# -### - -cloud_front_origin_elb_sg_ip_sync_lambda_iam_role = template.add_resource(Role( - "CloudFrontOriginElbSgIpSyncLambdaIamRole", - AssumeRolePolicyDocument={ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": ["lambda.amazonaws.com"] - }, - "Action": ["sts:AssumeRole"] - }] - }, - Policies=[ - Policy( - PolicyName="cloud_front_origin_elb_sg_ip_sync_lambda_iam_role_policy", - PolicyDocument={ - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Resource": "arn:aws:logs:*:*:*" - }, - { - "Effect": "Allow", - "Action": [ - "ec2:DescribeSecurityGroups", - "ec2:AuthorizeSecurityGroupIngress", - "ec2:RevokeSecurityGroupIngress" - ], - "Resource": "*" - } - ] - } - ) - ], - Path="/" -)) - -### -# -# Lambda Function -# -### - -cloud_front_origin_elb_sg_ip_sync_function = template.add_resource(Function( - "CloudFrontOriginElbSgIpSyncFunction", - Description="Lambda for syncing AWSs IPs for Cloud Front to origin ELB SGs", - Handler="update_security_groups.lambda_handler", - Role=GetAtt(cloud_front_origin_elb_sg_ip_sync_lambda_iam_role, "Arn"), - Code=Code( - S3Bucket=Ref(lambda_bucket), - S3Key=Ref(lambda_key) - ), - Runtime="python2.7", - MemorySize="128", - Timeout="5" -)) - -lambda_invoke_permission = template.add_resource(Permission( - "LambdaInvokePermission", - DependsOn="CloudFrontOriginElbSgIpSyncFunction", - FunctionName=GetAtt(cloud_front_origin_elb_sg_ip_sync_function, "Arn"), - Action="lambda:*", - Principal="sns.amazonaws.com", - SourceAccount=Ref(AWS_ACCOUNT_ID) -)) - -### -# -# Outputs -# -### - -template.add_output(Output( - "cloudFrontOriginElbSgIpSyncFunctionArn", - Value=GetAtt(cloud_front_origin_elb_sg_ip_sync_function, "Arn"), -)) - -### -# -# Print! -# -### - -cf_file = open(CF_FILE, 'w') -cf_file.truncate() -cf_file.write(template.to_json(indent=0)) -cf_file.close() - -print("CloudFormation template written to: {0}".format(CF_FILE)) diff --git a/smaas-cf/smaas/cms-cluster.py b/smaas-cf/smaas/cms-cluster.py deleted file mode 100644 index a75aa8f1..00000000 --- a/smaas-cf/smaas/cms-cluster.py +++ /dev/null @@ -1,333 +0,0 @@ -### -# Copyright (c) 2017 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. -### - -from io import open -from troposphere import Ref, Template, Parameter, Output, GetAtt -from troposphere.autoscaling import AutoScalingGroup, LaunchConfiguration -from troposphere.elasticloadbalancing import LoadBalancer, ConnectionDrainingPolicy, ConnectionSettings, HealthCheck, \ - Listener, Policy -from troposphere.policies import AutoScalingRollingUpdate, UpdatePolicy -from troposphere.route53 import RecordSetType - -from smaas.support.tags import CerberusTags - -CF_FILE = '../../src/main/resources/cloudformation/cms-cluster.json' - -# Create Template -template = Template() -template.description = "Launches the CMS cluster in the Cerberus VPC" -template.version = '2010-09-09' - -### -# -# Inputs -# -### - -cerberus_tags = CerberusTags() -cerberus_tags.add_tag_parameters(template) - -cert_public_key_param = template.add_parameter(Parameter( - "certPublicKey", - Description="TLS certificate public key to be used for backend authentication", - Type="String" -)) - -ssl_certificate_arn_param = template.add_parameter(Parameter( - "sslCertificateArn", - Description="TLS certificate ARN for the CMS ELB", - Type="String" -)) - -vpc_id_param = template.add_parameter(Parameter( - "vpcId", - Description="The ID of the VPC", - Type="AWS::EC2::VPC::Id" -)) - -instance_profile_name_param = template.add_parameter(Parameter( - "instanceProfileName", - Description="The name for the CMS instance profile", - Type="String" -)) - -ami_id_param = template.add_parameter(Parameter( - "amiId", - Description="The AMI ID for the CMS instances", - Type="String" -)) - -instance_size_param = template.add_parameter(Parameter( - "instanceSize", - Description="The instance size for the CMS instances", - Type="String" -)) - -cms_elb_sg_id_param = template.add_parameter(Parameter( - "cmsElbSgId", - Description="CMS ELB Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -cms_sg_id_param = template.add_parameter(Parameter( - "cmsSgId", - Description="CMS Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -tools_ingress_sg_id_param = template.add_parameter(Parameter( - "toolsIngressSgId", - Description="Tools Ingress Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -subnet_id_refs = [] -for zone_identifier in range(1, 4): - vpc_subnet_id = template.add_parameter(Parameter( - "vpcSubnetIdForAz{0}".format(zone_identifier), - Description="VPC Subnet ID for Zone {0}".format(zone_identifier), - Type="String" - )) - - subnet_id_refs.append(Ref(vpc_subnet_id)) - -key_pair_name_param = template.add_parameter(Parameter( - "keyPairName", - Description="The key pair to be associated with the EC2 instances", - Type="String", - Default="cpe-cerberus" -)) - -user_data_param = template.add_parameter(Parameter( - "userData", - Description="CMS user data", - Type="String" -)) - -hosted_zone_id_param = template.add_parameter(Parameter( - "hostedZoneId", - Description="The hosted zone id to associate the CNAME record with", - Type="String" -)) - -cname_param = template.add_parameter(Parameter( - "cname", - Description="The CNAME to be created for the CMS ELB", - Type="String" -)) - -desired_instances_param = template.add_parameter(Parameter( - "desiredInstances", - Description="Desired Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -maximum_instances_param = template.add_parameter(Parameter( - "maximumInstances", - Description="Maximum Number of Auto Scaling Instances (must be larger than min)", - Type="Number", - Default="4" -)) - -minimum_instances_param = template.add_parameter(Parameter( - "minimumInstances", - Description="Minimum Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -pause_time_param = template.add_parameter(Parameter( - "pauseTime", - Description="Pause time for AutoScalingRollingUpdate e.g PT15M", - Type="String", - Default="PT15M" -)) - -wait_on_resource_signals_param = template.add_parameter(Parameter( - "waitOnResourceSignals", - Description="Enabling WaitOnResourceSignals allows CloudFormation to wait until you have received a success signal before performing the next scaling action.", - Type="String", - Default="True" -)) - -### -# -# Elastic Load Balancers -# -### - -load_balancer = template.add_resource(LoadBalancer( - "CmsElasticLoadBalancer", - ConnectionDrainingPolicy=ConnectionDrainingPolicy( - Enabled=True, - Timeout=10 - ), - ConnectionSettings=ConnectionSettings( - IdleTimeout=10 - ), - CrossZone=True, - HealthCheck=HealthCheck( - HealthyThreshold=2, - Interval=5, - Target="HTTP:8080/healthcheck", - Timeout=2, - UnhealthyThreshold=2 - ), - Listeners=[ - Listener( - InstancePort=8080, - InstanceProtocol="HTTP", - LoadBalancerPort=443, - PolicyNames=[ - "CmsTLSNegotiationPolicy" - ], - Protocol="HTTPS", - SSLCertificateId=Ref(ssl_certificate_arn_param) - ) - ], - Policies=[ - Policy( - PolicyName="CmsTLSNegotiationPolicy", - PolicyType="SSLNegotiationPolicyType", - Attributes=[ - { - "Name": "Reference-Security-Policy", - "Value": "ELBSecurityPolicy-TLS-1-2-2017-01" - } - ] - ) - ], - Scheme="internal", - SecurityGroups=[ - Ref(cms_elb_sg_id_param) - ], - Subnets=subnet_id_refs, - Tags=cerberus_tags.get_tags_as_list() -)) - -### -# -# Launch Configuration and Auto Scaling Group for CMS -# -### - -launch_config = template.add_resource(LaunchConfiguration( - "CmsLaunchConfiguration", - AssociatePublicIpAddress=True, - IamInstanceProfile=Ref(instance_profile_name_param), - ImageId=Ref(ami_id_param), - InstanceMonitoring=True, - InstanceType=Ref(instance_size_param), - KeyName=Ref(key_pair_name_param), - SecurityGroups=[ - Ref(tools_ingress_sg_id_param), - Ref(cms_sg_id_param) - ], - UserData=Ref(user_data_param) -)) - -autoscaling_group = template.add_resource(AutoScalingGroup( - "CmsAutoScalingGroup", - DesiredCapacity=Ref(desired_instances_param), - HealthCheckGracePeriod=900, - HealthCheckType="ELB", - LaunchConfigurationName=Ref(launch_config), - LoadBalancerNames=[ - Ref(load_balancer) - ], - MaxSize=Ref(maximum_instances_param), - MinSize=Ref(minimum_instances_param), - UpdatePolicy=UpdatePolicy( - AutoScalingRollingUpdate=AutoScalingRollingUpdate( - MaxBatchSize=1, - MinInstancesInService=Ref(minimum_instances_param), - PauseTime=Ref(pause_time_param), - WaitOnResourceSignals=Ref(wait_on_resource_signals_param) - ) - ), - VPCZoneIdentifier=subnet_id_refs, - Tags=cerberus_tags.get_autoscaling_tags_as_list() -)) - -### -# -# Record Set for CMS CNAME -# -### - -record_set = template.add_resource(RecordSetType( - "CmsRecordSet", - HostedZoneId=Ref(hosted_zone_id_param), - Name=Ref(cname_param), - TTL=30, - Type="CNAME", - ResourceRecords=[GetAtt(load_balancer, "DNSName")] -)) - -### -# -# Outputs -# -### - -template.add_output(Output( - "autoscalingGroupLogicalId", - Value=Ref(autoscaling_group) -)) - -template.add_output(Output( - "launchConfigurationLogicalId", - Value=Ref(launch_config) -)) - -template.add_output(Output( - "elbLogicalId", - Value=Ref(load_balancer) -)) - -template.add_output(Output( - "elbCanonicalHostedZoneNameId", - Value=GetAtt(load_balancer, "CanonicalHostedZoneNameID") -)) - -template.add_output(Output( - "elbDnsName", - Value=GetAtt(load_balancer, "DNSName") -)) - -template.add_output(Output( - "elbSourceSecurityGroupName", - Value=GetAtt(load_balancer, "SourceSecurityGroup.GroupName") -)) - -template.add_output(Output( - "elbSourceSecurityGroupOwnerAlias", - Value=GetAtt(load_balancer, "SourceSecurityGroup.OwnerAlias") -)) - -### -# -# Print! -# -### - -cf_file = open(CF_FILE, 'w') -cf_file.truncate() -cf_file.write(template.to_json(indent=0)) -cf_file.close() - -print("CloudFormation template written to: {0}".format(CF_FILE)) diff --git a/smaas-cf/smaas/consul-cluster.py b/smaas-cf/smaas/consul-cluster.py deleted file mode 100644 index 0c71314d..00000000 --- a/smaas-cf/smaas/consul-cluster.py +++ /dev/null @@ -1,210 +0,0 @@ -### -# Copyright (c) 2017 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. -### - -from io import open -from troposphere.autoscaling import AutoScalingGroup, LaunchConfiguration -from troposphere import Ref, Template, Parameter, Output, GetAtt -from troposphere.policies import AutoScalingRollingUpdate, UpdatePolicy - -from smaas.support.tags import CerberusTags - -CF_FILE = '../../src/main/resources/cloudformation/consul-cluster.json' - -# Create Template -template = Template() -template.description = "Launches the Consul cluster in the Cerberus VPC" -template.version = '2010-09-09' - -### -# -# Inputs -# -### - -cerberus_tags = CerberusTags() -cerberus_tags.add_tag_parameters(template) - -vpc_id_param = template.add_parameter(Parameter( - "vpcId", - Description="The ID of the VPC", - Type="AWS::EC2::VPC::Id" -)) - -instance_profile_param = template.add_parameter(Parameter( - "instanceProfileName", - Description="The name for the Consul instance profile", - Type="String" -)) - -ami_id_param = template.add_parameter(Parameter( - "amiId", - Description="The AMI ID for the Consul server instances", - Type="String" -)) - -instance_size_param = template.add_parameter(Parameter( - "instanceSize", - Description="The instance size for the Consul server instances", - Type="String" -)) - -tools_ingress_sg_id_param = template.add_parameter(Parameter( - "toolsIngressSgId", - Description="Tools Ingress Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -consul_client_sg_id_param = template.add_parameter(Parameter( - "consulClientSgId", - Description="Consul Client Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -consul_server_sg_id_param = template.add_parameter(Parameter( - "consulServerSgId", - Description="Consul Server Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -key_pair_name_param = template.add_parameter(Parameter( - "keyPairName", - Description="The key pair to be associated with the EC2 instances", - Type="String", - Default="cpe-cerberus" -)) - -user_data_param = template.add_parameter(Parameter( - "userData", - Description="Consul User Data", - Type="String" -)) - -desired_instances_param = template.add_parameter(Parameter( - "desiredInstances", - Description="Desired Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -maximum_instances_param = template.add_parameter(Parameter( - "maximumInstances", - Description="Maximum Number of Auto Scaling Instances (must be larger than min)", - Type="Number", - Default="4" -)) - -minimum_instances_param = template.add_parameter(Parameter( - "minimumInstances", - Description="Minimum Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -pause_time_param = template.add_parameter(Parameter( - "pauseTime", - Description="Pause time for AutoScalingRollingUpdate e.g PT15M", - Type="String", - Default="PT15M" -)) - -wait_on_resource_signals_param = template.add_parameter(Parameter( - "waitOnResourceSignals", - Description="Enabling WaitOnResourceSignals allows CloudFormation to wait until you have received a success signal before performing the next scaling action.", - Type="String", - Default="True" -)) - -subnet_id_refs = [] -for zone_identifier in range(1, 4): - vpc_subnet_id = template.add_parameter(Parameter( - "vpcSubnetIdForAz{0}".format(zone_identifier), - Description="VPC Subnet ID for Zone {0}".format(zone_identifier), - Type="String" - )) - - subnet_id_refs.append(Ref(vpc_subnet_id)) - - -### -# -# EC2 Consul Server Instances -# -### - -consul_launch_config = template.add_resource(LaunchConfiguration( - "ConsulLaunchConfiguration", - AssociatePublicIpAddress=True, - IamInstanceProfile=Ref(instance_profile_param), - ImageId=Ref(ami_id_param), - InstanceMonitoring=True, - InstanceType=Ref(instance_size_param), - KeyName=Ref(key_pair_name_param), - SecurityGroups=[ - Ref(tools_ingress_sg_id_param), - Ref(consul_client_sg_id_param), - Ref(consul_server_sg_id_param) - ], - UserData=Ref(user_data_param) -)) - -consul_autoscaling_group = template.add_resource(AutoScalingGroup( - "ConsulAutoScalingGroup", - DesiredCapacity=Ref(desired_instances_param), - HealthCheckGracePeriod=60, - HealthCheckType="EC2", - LaunchConfigurationName=Ref(consul_launch_config), - MaxSize=Ref(maximum_instances_param), - MinSize=Ref(minimum_instances_param), - UpdatePolicy=UpdatePolicy( - AutoScalingRollingUpdate=AutoScalingRollingUpdate( - MaxBatchSize=1, - MinInstancesInService=Ref(minimum_instances_param), - PauseTime=Ref(pause_time_param), - WaitOnResourceSignals=Ref(wait_on_resource_signals_param) - ) - ), - VPCZoneIdentifier=subnet_id_refs, - Tags=cerberus_tags.get_autoscaling_tags_as_list() -)) - -### -# -# Outputs -# -### - -template.add_output(Output( - "autoscalingGroupLogicalId", - Value=Ref(consul_autoscaling_group) -)) - -template.add_output(Output( - "launchConfigurationLogicalId", - Value=Ref(consul_launch_config) -)) - -### -# -# Print! -# -### - -cf_file = open(CF_FILE, 'w') -cf_file.truncate() -cf_file.write(template.to_json(indent=0)) -cf_file.close() - -print("CloudFormation template written to: {0}".format(CF_FILE)) diff --git a/smaas-cf/smaas/support/constants.py b/smaas-cf/smaas/support/constants.py deleted file mode 100644 index ec8f6361..00000000 --- a/smaas-cf/smaas/support/constants.py +++ /dev/null @@ -1,3 +0,0 @@ -from troposphere import Ref - -aws_region_ref = Ref("AWS::Region") \ No newline at end of file diff --git a/smaas-cf/smaas/support/network.py b/smaas-cf/smaas/support/network.py deleted file mode 100644 index b860f251..00000000 --- a/smaas-cf/smaas/support/network.py +++ /dev/null @@ -1,98 +0,0 @@ -from troposphere import Parameter, Template, Output - -VPC_SUBNET_CIDRS = { - 1: "172.20.0.0/24", - 2: "172.20.4.0/24", - 3: "172.20.8.0/24" -} - - -class CerberusNetwork: - gateway_cidr_block_param = None - vpc_cidr_block_param = None - subnet_cidr_block_param_by_az_map = {} - subnet_cidr_block_for_az1_output = None - subnet_cidr_block_for_az2_output = None - subnet_cidr_block_for_az3_output = None - - def __init__(self): - self.gateway_cidr_block_param = Parameter( - "gatewayCidrBlock", - Description="The internet gateway CIDR block for where traffic is allowed from", - Type="String", - MinLength="9", - MaxLength="20", - AllowedPattern="[0-9./]*", - Default="0.0.0.0/0" - ) - - self.vpc_cidr_block_param = Parameter( - "vpcCidrBlock", - Description="The VPC's CIDR block for internal IPv4 addressing", - Type="String", - MinLength="9", - MaxLength="20", - AllowedPattern="[0-9./]*", - Default="172.20.0.0/20" - ) - - self.subnet_cidr_block_param_by_az_map[1] = Parameter( - "subnetCidrBlockForAz1", - Description="Cidr block for subnet in AZ '1'", - Type="String", - MinLength="9", - MaxLength="20", - AllowedPattern="[0-9./]*", - Default=VPC_SUBNET_CIDRS[1] - ) - - self.subnet_cidr_block_param_by_az_map[2] = Parameter( - "subnetCidrBlockForAz2", - Description="Cidr block for subnet in AZ '2'", - Type="String", - MinLength="9", - MaxLength="20", - AllowedPattern="[0-9./]*", - Default=VPC_SUBNET_CIDRS[2] - ) - - self.subnet_cidr_block_param_by_az_map[3] = Parameter( - "subnetCidrBlockForAz3", - Description="Cidr block for subnet in AZ '3'", - Type="String", - MinLength="9", - MaxLength="20", - AllowedPattern="[0-9./]*", - Default=VPC_SUBNET_CIDRS[3] - ) - - self.subnet_cidr_block_for_az1_output = Output( - "subnetCidrBlockForAz1", - Description="Cidr block for subnet in AZ '1'", - Value=VPC_SUBNET_CIDRS[1] - ) - - self.subnet_cidr_block_for_az2_output = Output( - "subnetCidrBlockForAz2", - Description="Cidr block for subnet in AZ '2'", - Value=VPC_SUBNET_CIDRS[2] - ) - - self.subnet_cidr_block_for_az3_output = Output( - "subnetCidrBlockForAz3", - Description="Cidr block for subnet in AZ '3'", - Value=VPC_SUBNET_CIDRS[3] - ) - - def add_parameters(self, template: Template): - template.add_parameter(self.gateway_cidr_block_param) - template.add_parameter(self.vpc_cidr_block_param) - - for subnet_cidr_block in self.subnet_cidr_block_param_by_az_map.values(): - template.add_parameter(subnet_cidr_block) - - - def add_outputs(self, template: Template): - template.add_output(self.subnet_cidr_block_for_az1_output) - template.add_output(self.subnet_cidr_block_for_az2_output) - template.add_output(self.subnet_cidr_block_for_az3_output) diff --git a/smaas-cf/smaas/support/tags.py b/smaas-cf/smaas/support/tags.py deleted file mode 100644 index d491dfad..00000000 --- a/smaas-cf/smaas/support/tags.py +++ /dev/null @@ -1,70 +0,0 @@ -from troposphere import Parameter, Ref, Tags, Template -from troposphere.ec2 import Tag -from troposphere.autoscaling import Tag as AutoScalingTag - - -class CerberusTags: - tag_map = {} - - def __init__(self): - self.tag_map["tagName"] = Parameter( - "tagName", - Description="Name assigned to the stack. Format: {appGroup}-{appName}", - Type="String" - ) - - self.tag_map["tagEmail"] = Parameter( - "tagEmail", - Description="E-mail address for group or person responsible for the stack.", - Type="String" - ) - - self.tag_map["tagClassification"] = Parameter( - "tagClassification", - Description="Denotes which category of Data Classification the instance is grouped under.", - Type="String", - Default="Gold" - ) - - self.tag_map["tagCostcenter"] = Parameter( - "tagCostcenter", - Description="Represents the Cost Center associated with the team/project.", - Type="String" - ) - - def add_tag_parameters(self, template: Template): - for param in self.tag_map.values(): - template.add_parameter(param) - - def get_tags_as_list(self): - return [ - Tag("Name", Ref(self.tag_map["tagName"])), - Tag("email", Ref(self.tag_map["tagEmail"])), - Tag("classification", Ref(self.tag_map["tagClassification"])), - Tag("costcenter", Ref(self.tag_map["tagCostcenter"])) - ] - - def get_gateway_tags_as_list(self): - return [ - Tag("Name", "cloudfront"), - Tag("AutoUpdate", "true"), - Tag("email", Ref(self.tag_map["tagEmail"])), - Tag("classification", Ref(self.tag_map["tagClassification"])), - Tag("costcenter", Ref(self.tag_map["tagCostcenter"])) - ] - - def get_autoscaling_tags_as_list(self): - return [ - AutoScalingTag("Name", Ref(self.tag_map["tagName"]), True), - AutoScalingTag("email", Ref(self.tag_map["tagEmail"]), True), - AutoScalingTag("classification", Ref(self.tag_map["tagClassification"]), True), - AutoScalingTag("costcenter", Ref(self.tag_map["tagCostcenter"]), True) - ] - - def get_tags(self): - return Tags( - Name=Ref(self.tag_map["tagName"]), - email=Ref(self.tag_map["tagEmail"]), - classification=Ref(self.tag_map["tagClassification"]), - costcenter=Ref(self.tag_map["tagCostcenter"]) - ) diff --git a/smaas-cf/smaas/vault-cluster.py b/smaas-cf/smaas/vault-cluster.py deleted file mode 100644 index 659aa2a6..00000000 --- a/smaas-cf/smaas/vault-cluster.py +++ /dev/null @@ -1,379 +0,0 @@ -### -# Copyright (c) 2017 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. -### - -from io import open -from troposphere import Ref, Template, Parameter, Output, GetAtt -from troposphere.autoscaling import AutoScalingGroup, LaunchConfiguration -from troposphere.elasticloadbalancing import LoadBalancer, ConnectionDrainingPolicy, ConnectionSettings, HealthCheck, \ - Listener, Policy -from troposphere.route53 import RecordSetType -from troposphere.policies import AutoScalingRollingUpdate, UpdatePolicy - -from smaas.support.tags import CerberusTags - -CF_FILE = '../../src/main/resources/cloudformation/vault-cluster.json' - -# Create Template -template = Template() -template.description = "Launches the Vault stack in the Cerberus VPC" -template.version = '2010-09-09' - -### -# -# Inputs -# -### - -cerberus_tags = CerberusTags() -cerberus_tags.add_tag_parameters(template) - -cert_public_key_param = template.add_parameter(Parameter( - "certPublicKey", - Description="TLS certificate public key to be used for backend authentication", - Type="String" -)) - -ssl_certificate_arn_param = template.add_parameter(Parameter( - "sslCertificateArn", - Description="TLS certificate ARN for the ELB", - Type="String" -)) - -vpc_id_param = template.add_parameter(Parameter( - "vpcId", - Description="The ID of the VPC", - Type="AWS::EC2::VPC::Id" -)) - -instance_profile_name_param = template.add_parameter(Parameter( - "instanceProfileName", - Description="The name for the Vault Instance Profile", - Type="String" -)) - -ami_id_param = template.add_parameter(Parameter( - "amiId", - Description="The AMI ID for the Vault Instances", - Type="String" -)) - -instance_size_param = template.add_parameter(Parameter( - "instanceSize", - Description="The instance size for the Vault instances", - Type="String" -)) - -vault_server_elb_sg_id_param = template.add_parameter(Parameter( - "vaultServerElbSgId", - Description="Vault Server ELB Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -tools_ingress_sg_id_param = template.add_parameter(Parameter( - "toolsIngressSgId", - Description="Tools Ingress Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -vault_client_sg_id_param = template.add_parameter(Parameter( - "vaultClientSgId", - Description="Vault Security Group ID for clients", - Type="AWS::EC2::SecurityGroup::Id" -)) - -vault_server_sg_id_param = template.add_parameter(Parameter( - "vaultServerSgId", - Description="Vault Security Group ID for servers", - Type="AWS::EC2::SecurityGroup::Id" -)) - -consul_client_sg_id_param = template.add_parameter(Parameter( - "consulClientSgId", - Description="Consul Client Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -consul_server_sg_id_param = template.add_parameter(Parameter( - "consulServerSgId", - Description="Consul Server Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -subnet_id_refs = [] -for zone_identifier in range(1, 4): - vpc_subnet_id = template.add_parameter(Parameter( - "vpcSubnetIdForAz{0}".format(zone_identifier), - Description="VPC Subnet ID for Zone {0}".format(zone_identifier), - Type="String" - )) - - subnet_id_refs.append(Ref(vpc_subnet_id)) - -key_pair_name_param = template.add_parameter(Parameter( - "keyPairName", - Description="The key pair to be associated with the EC2 instances", - Type="String", - Default="cpe-cerberus" -)) - -vault_user_data_param = template.add_parameter(Parameter( - "userData", - Description="Vault User Data", - Type="String" -)) - -hosted_zone_id_param = template.add_parameter(Parameter( - "hostedZoneId", - Description="The hosted zone id to associate the CNAME record with", - Type="String" -)) - -cname_param = template.add_parameter(Parameter( - "cname", - Description="The CNAME to be created", - Type="String" -)) - -desired_instances_param = template.add_parameter(Parameter( - "desiredInstances", - Description="Desired Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -maximum_instances_param = template.add_parameter(Parameter( - "maximumInstances", - Description="Maximum Number of Auto Scaling Instances (must be larger than min)", - Type="Number", - Default="4" -)) - -minimum_instances_param = template.add_parameter(Parameter( - "minimumInstances", - Description="Minimum Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - - -pause_time_param = template.add_parameter(Parameter( - "pauseTime", - Description="Pause time for AutoScalingRollingUpdate e.g PT15M", - Type="String", - Default="PT15M" -)) - -wait_on_resource_signals_param = template.add_parameter(Parameter( - "waitOnResourceSignals", - Description="Enabling WaitOnResourceSignals allows CloudFormation to wait until you have received a success signal before performing the next scaling action.", - Type="String", - Default="True" -)) - - -### -# -# Elastic Load Balancer -# -### - -vault_load_balancer = template.add_resource(LoadBalancer( - "VaultElasticLoadBalancer", - ConnectionDrainingPolicy=ConnectionDrainingPolicy( - Enabled=True, - Timeout=10 - ), - ConnectionSettings=ConnectionSettings( - IdleTimeout=10 - ), - CrossZone=True, - HealthCheck=HealthCheck( - HealthyThreshold=2, - Interval=5, - Target="HTTPS:8200/v1/sys/health?standbyok", - Timeout=2, - UnhealthyThreshold=2 - ), - Listeners=[ - Listener( - InstancePort=8200, - InstanceProtocol="HTTPS", - LoadBalancerPort=443, - PolicyNames=[ - "VaultTLSNegotiationPolicy" - ], - Protocol="HTTPS", - SSLCertificateId=Ref(ssl_certificate_arn_param) - ) - ], - Policies=[ - Policy( - PolicyName="VaultTLSNegotiationPolicy", - PolicyType="SSLNegotiationPolicyType", - Attributes=[ - { - "Name": "Reference-Security-Policy", - "Value": "ELBSecurityPolicy-TLS-1-2-2017-01" - } - ] - ), - Policy( - PolicyName="VaultPublicKeyPolicy", - PolicyType="PublicKeyPolicyType", - Attributes=[ - { - "Name": "PublicKey", - "Value": Ref(cert_public_key_param) - } - ] - ), - Policy( - PolicyName="VaultBackendServerAuthenticationPolicy", - PolicyType="BackendServerAuthenticationPolicyType", - Attributes=[ - { - "Name": "PublicKeyPolicyName", - "Value": "VaultPublicKeyPolicy" - } - ], - InstancePorts=[ - "8200" - ] - ) - ], - Scheme="internal", - SecurityGroups=[ - Ref(vault_server_elb_sg_id_param) - ], - Subnets=subnet_id_refs, - Tags=cerberus_tags.get_tags_as_list() -)) - -### -# -# Launch Configuration and Auto Scaling Group for Vault -# -### - -vault_launch_config = template.add_resource(LaunchConfiguration( - "VaultLaunchConfiguration", - AssociatePublicIpAddress=True, - IamInstanceProfile=Ref(instance_profile_name_param), - ImageId=Ref(ami_id_param), - InstanceMonitoring=True, - InstanceType=Ref(instance_size_param), - KeyName=Ref(key_pair_name_param), - SecurityGroups=[ - Ref(tools_ingress_sg_id_param), - Ref(vault_client_sg_id_param), - Ref(vault_server_sg_id_param), - Ref(consul_client_sg_id_param), - Ref(consul_server_sg_id_param) - ], - UserData=Ref(vault_user_data_param) -)) - -vault_autoscaling_group = template.add_resource(AutoScalingGroup( - "VaultAutoScalingGroup", - DesiredCapacity=Ref(desired_instances_param), - HealthCheckGracePeriod=60, - HealthCheckType="EC2", - LaunchConfigurationName=Ref(vault_launch_config), - LoadBalancerNames=[ - Ref(vault_load_balancer) - ], - MaxSize=Ref(maximum_instances_param), - MinSize=Ref(minimum_instances_param), - UpdatePolicy=UpdatePolicy( - AutoScalingRollingUpdate=AutoScalingRollingUpdate( - MaxBatchSize=1, - MinInstancesInService=Ref(minimum_instances_param), - PauseTime=Ref(pause_time_param), - WaitOnResourceSignals=Ref(wait_on_resource_signals_param) - ) - ), - VPCZoneIdentifier=subnet_id_refs, - Tags=cerberus_tags.get_autoscaling_tags_as_list() -)) - -### -# -# Record Set for Vault CNAME -# -### - -vault_record_set = template.add_resource(RecordSetType( - "VaultRecordSet", - HostedZoneId=Ref(hosted_zone_id_param), - Name=Ref(cname_param), - TTL=30, - Type="CNAME", - ResourceRecords=[GetAtt(vault_load_balancer, "DNSName")] -)) - -### -# -# Outputs -# -### - -template.add_output(Output( - "autoscalingGroupLogicalId", - Value=Ref(vault_autoscaling_group) -)) - -template.add_output(Output( - "launchConfigurationLogicalId", - Value=Ref(vault_launch_config) -)) - -template.add_output(Output( - "elbLogicalId", - Value=Ref(vault_load_balancer) -)) - -template.add_output(Output( - "elbCanonicalHostedZoneNameId", - Value=GetAtt(vault_load_balancer, "CanonicalHostedZoneNameID") -)) - -template.add_output(Output( - "elbDnsName", - Value=GetAtt(vault_load_balancer, "DNSName") -)) - -template.add_output(Output( - "elbSourceSecurityGroupName", - Value=GetAtt(vault_load_balancer, "SourceSecurityGroup.GroupName") -)) - -template.add_output(Output( - "elbSourceSecurityGroupOwnerAlias", - Value=GetAtt(vault_load_balancer, "SourceSecurityGroup.OwnerAlias") -)) - -### -# -# Print! -# -### - -cf_file = open(CF_FILE, 'w') -cf_file.truncate() -cf_file.write(template.to_json(indent=0)) -cf_file.close() - -print("CloudFormation template written to: {0}".format(CF_FILE)) diff --git a/smaas-cf/smaas/vpc-and-base.py b/smaas-cf/smaas/vpc-and-base.py deleted file mode 100644 index a4b27ac4..00000000 --- a/smaas-cf/smaas/vpc-and-base.py +++ /dev/null @@ -1,1418 +0,0 @@ -### -# Copyright (c) 2016 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. -### - -from io import open -from troposphere import Ref, Template, Parameter, Output, GetAtt, Join, AWS_ACCOUNT_ID, Equals, If -from troposphere.ec2 import VPC -from troposphere.ec2 import DHCPOptions -from troposphere.ec2 import VPCDHCPOptionsAssociation -from troposphere.ec2 import RouteTable -from troposphere.ec2 import InternetGateway -from troposphere.ec2 import Route -from troposphere.ec2 import VPCGatewayAttachment -from troposphere.ec2 import Subnet -from troposphere.ec2 import SubnetRouteTableAssociation -from troposphere.ec2 import SecurityGroup -from troposphere.ec2 import SecurityGroupIngress -from troposphere.iam import Role, InstanceProfile, PolicyType, Policy -from troposphere.kms import Key -from troposphere.rds import DBInstance, DBSubnetGroup, DBParameterGroup -from troposphere.route53 import HostedZone, HostedZoneVPCs, HostedZoneConfiguration -from troposphere.s3 import Bucket, Private, BucketPolicy, PublicRead, WebsiteConfiguration, VersioningConfiguration - -from smaas.support.constants import aws_region_ref -from smaas.support.tags import CerberusTags -from smaas.support.network import CerberusNetwork - -CF_FILE = '../../src/main/resources/cloudformation/vpc-and-base.json' - -# Create Template -template = Template() -template.description = "Creates the Cerberus VPC and foundational resources" -template.version = '2010-09-09' - -cerberus_tags = CerberusTags() -cerberus_tags.add_tag_parameters(template) - -cerberus_network = CerberusNetwork() -cerberus_network.add_parameters(template) - -### -# -# Parameters -# -### - -# Required account admin ARN for KMS key administration -account_admin_arn_param = template.add_parameter(Parameter( - "accountAdminArn", - Description="The ARN for a IAM user, group or role that can create this stack.", - Type="String" -)) - -# Availability Zones: We use a more generic 1, 2, 3 to represent the actual zones for abstraction -az1_param = template.add_parameter(Parameter( - "az1", - Description="An availability zone identifier for zone '1'", - Type="String", - AllowedPattern="[a-z]{2}-[a-z]+-\d\w" -)) - -az2_param = template.add_parameter(Parameter( - "az2", - Description="An availability zone identifier for zone '2'", - Type="String", - AllowedPattern="[a-z]{2}-[a-z]+-\d\w" -)) - -az3_param = template.add_parameter(Parameter( - "az3", - Description="An availability zone identifier for zone '3'", - Type="String", - AllowedPattern="[a-z]{2}-[a-z]+-\d\w" -)) - -# RDS instance parameters for the management DB -cms_db_allocated_storage_param = template.add_parameter(Parameter( - "cmsDbAllocatedStorage", - Description="The allocated storage for the RDS instance.", - Type="String", - Default="100" -)) - -cms_db_instance_size_param = template.add_parameter(Parameter( - "cmsDbInstanceSize", - Description="MySQL DB instance class", - Type="String", - Default="db.r3.large" -)) - -cms_db_name_param = template.add_parameter(Parameter( - "cmsDbName", - Description="The name of the database initially create on the RDS instance", - Type="String" -)) - -cms_db_master_username_param = template.add_parameter(Parameter( - "cmsDbMasterUsername", - Description="Master username for the cms RDS instance", - Type="String" -)) - -cms_db_master_password_param = template.add_parameter(Parameter( - "cmsDbMasterPassword", - Description="Master password for the cms RDS instance", - Type="String" -)) - -cms_db_port_param = template.add_parameter(Parameter( - "cmsDbPort", - Description="Port for the cms DB instance", - Type="Number", - Default="3306" -)) - -# Route 53 hosted zone and record sets for VPC -vpc_hosted_zone_name_param = template.add_parameter(Parameter( - "vpcHostedZoneName", - Description="The hosted zone name used in the Cerberus VPC", - Type="String" -)) - -az_by_identifier_map = { - 1: az1_param, - 2: az2_param, - 3: az3_param -} - -### -# -# VPC and Supporting Networking -# -### - -# Create the VPC -cerberus_vpc = template.add_resource(VPC( - "CerberusVpc", - CidrBlock=Ref(cerberus_network.vpc_cidr_block_param), - EnableDnsSupport=True, - EnableDnsHostnames=True, - Tags=cerberus_tags.get_tags_as_list() -)) - -# DHCP Options -# http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html -# If you're using AmazonProvidedDNS in us-east-1, specify ec2.internal. -# If you're using AmazonProvidedDNS in another region, specify region.compute.internal -# (for example, ap-northeast-1.compute.internal). Otherwise, specify a domain name -# (for example, MyCompany.com). This value is used to complete unqualified DNS hostnames. -template.add_condition("RegionEqualsEastOne", Equals(aws_region_ref, "us-east-1")) -cerberus_dhcp_options = template.add_resource(DHCPOptions( - "CerberusDhcpOptions", - DomainName=If("RegionEqualsEastOne", "ec2.internal", Join(".", [aws_region_ref, "compute.internal"])), - DomainNameServers=["AmazonProvidedDNS"], - Tags=cerberus_tags.get_tags_as_list() -)) - -cerberus_dhcp_options_association = template.add_resource(VPCDHCPOptionsAssociation( - "CerberusVpcDhcpOptionsAssociation", - DhcpOptionsId=Ref(cerberus_dhcp_options), - VpcId=Ref(cerberus_vpc) -)) - -# Routing, Subnets and Gateways -cerberus_route_table = template.add_resource(RouteTable( - "CerberusRouteTable", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -cerberus_internet_gateway = template.add_resource(InternetGateway( - "CerberusInternetGateway", - Tags=cerberus_tags.get_tags_as_list() -)) - -cerberus_route_internet_gateway = template.add_resource(Route( - "CerberusRouteInternetGateway", - DestinationCidrBlock=Ref(cerberus_network.gateway_cidr_block_param), - GatewayId=Ref(cerberus_internet_gateway), - RouteTableId=Ref(cerberus_route_table) -)) - -cerberus_vpc_gateway_attachment = template.add_resource(VPCGatewayAttachment( - "CerberusVpcGatewayAttachment", - InternetGatewayId=Ref(cerberus_internet_gateway), - VpcId=Ref(cerberus_vpc) -)) - -# Associate the three subnets to the route table -vpc_subnet_resources_by_zone_identifier = {} -for zone_identifier, subnet_cidr_block in cerberus_network.subnet_cidr_block_param_by_az_map.items(): - subnet = template.add_resource(Subnet( - "CerberusSubnetForAz{0}".format(zone_identifier), - AvailabilityZone=Ref(az_by_identifier_map[zone_identifier]), - CidrBlock=Ref(subnet_cidr_block), - MapPublicIpOnLaunch=True, - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() - )) - - vpc_subnet_resources_by_zone_identifier[zone_identifier] = subnet - - template.add_resource(SubnetRouteTableAssociation( - "CerberusSubnetRouteTableAssociationForAz{0}".format(zone_identifier), - RouteTableId=Ref(cerberus_route_table), - SubnetId=Ref(subnet) - )) - -### -# -# Security Groups -# -### - -tools_ingress_sg = template.add_resource(SecurityGroup( - "ToolsIngressSg", - GroupDescription="Administration ingress from tools NAT boxes", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -gateway_elb_sg = template.add_resource(SecurityGroup( - "GatewayElbSg", - GroupDescription="Gateway ELB Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_gateway_tags_as_list() -)) - -gateway_server_sg = template.add_resource(SecurityGroup( - "GatewayServerSg", - GroupDescription="Gateway Server Instance Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -cms_elb_sg = template.add_resource(SecurityGroup( - "CmsElbSg", - GroupDescription="Management Server ELB Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -cms_sg = template.add_resource(SecurityGroup( - "CmsSg", - GroupDescription="Management Server Instance Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -cms_db_sg = template.add_resource(SecurityGroup( - "CmsDbSg", - GroupDescription="Management Server Database Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -vault_server_elb_sg = template.add_resource(SecurityGroup( - "VaultServerElbSg", - GroupDescription="Vault ServerELB Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -vault_client_sg = template.add_resource(SecurityGroup( - "VaultClientSg", - GroupDescription="Vault Client Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -vault_server_sg = template.add_resource(SecurityGroup( - "VaultServerSg", - GroupDescription="Vault Server Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -consul_client_sg = template.add_resource(SecurityGroup( - "ConsulClientSg", - GroupDescription="Consul Client Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -consul_server_sg = template.add_resource(SecurityGroup( - "ConsulServerSg", - GroupDescription="Consul Server Security Group", - VpcId=Ref(cerberus_vpc), - Tags=cerberus_tags.get_tags_as_list() -)) - -### -# -# Security Group Ingress Rules -# -### - -# Allow HTTPS through from the internet to the ELB -template.add_resource(SecurityGroupIngress( - "GatewayElbIngressFromInternetSg443", - GroupId=Ref(gateway_elb_sg), - CidrIp=Ref(cerberus_network.gateway_cidr_block_param), - FromPort=443, - IpProtocol="tcp", - ToPort=443 -)) - -# Allow the internet facing gateway ELB to talk to the gateway instances over HTTPS (443) -template.add_resource(SecurityGroupIngress( - "GatewayServerIngressFromGatewayElb443", - GroupId=Ref(gateway_server_sg), - SourceSecurityGroupId=Ref(gateway_elb_sg), - FromPort=443, - IpProtocol="tcp", - ToPort=443 -)) - -# Allow gateway server instances to talk to the management server ELB over HTTPS (443) -template.add_resource(SecurityGroupIngress( - "CmsElbFromGatewayServer443", - GroupId=Ref(cms_elb_sg), - SourceSecurityGroupId=Ref(gateway_server_sg), - FromPort=443, - IpProtocol="tcp", - ToPort=443 -)) - -# Allow gateway server instances to talk to the vault ELB over HTTPS (443) -template.add_resource(SecurityGroupIngress( - "VaultServerElbFromGatewayServer443", - GroupId=Ref(vault_server_elb_sg), - SourceSecurityGroupId=Ref(gateway_server_sg), - FromPort=443, - IpProtocol="tcp", - ToPort=443 -)) - -# Allow the management server ELB to talk to the management server instances over HTTPS (8443) -template.add_resource(SecurityGroupIngress( - "CmsIngressFromCmsElb8443", - GroupId=Ref(cms_sg), - SourceSecurityGroupId=Ref(cms_elb_sg), - FromPort=8443, - IpProtocol="tcp", - ToPort=8443 -)) - -# Allow the management server ELB to talk to the management server instances over HTTP (8080) -template.add_resource(SecurityGroupIngress( - "CmsIngressFromCmsElb8080", - GroupId=Ref(cms_sg), - SourceSecurityGroupId=Ref(cms_elb_sg), - FromPort=8080, - IpProtocol="tcp", - ToPort=8080 -)) - -# Allow the management server ELB to talk to the management server instances over HTTP (8077) -template.add_resource(SecurityGroupIngress( - "CmsIngressFromCmsElb8077", - GroupId=Ref(cms_sg), - SourceSecurityGroupId=Ref(cms_elb_sg), - FromPort=8077, - IpProtocol="tcp", - ToPort=8077 -)) - -# Allow the management server instances to talk to the Vault ELB over HTTPS (443) -template.add_resource(SecurityGroupIngress( - "VaultElbIngressFromCms443", - GroupId=Ref(vault_server_elb_sg), - SourceSecurityGroupId=Ref(cms_sg), - FromPort=443, - IpProtocol="tcp", - ToPort=443 -)) - -# Allow the management server instances to access the management db instance -template.add_resource(SecurityGroupIngress( - "CmsDbFromCms", - GroupId=Ref(cms_db_sg), - SourceSecurityGroupId=Ref(cms_sg), - FromPort=Ref(cms_db_port_param), - IpProtocol="tcp", - ToPort=Ref(cms_db_port_param) -)) - -# Allow the Vault server ELB to talk to Vault server instances over HTTPS (8200) -template.add_resource(SecurityGroupIngress( - "VaultServerIngressFromVaultElb8200", - GroupId=Ref(vault_server_sg), - SourceSecurityGroupId=Ref(vault_server_elb_sg), - FromPort=8200, - IpProtocol="tcp", - ToPort=8200 -)) - -# Allow Vault server instances to talk to other Vault server instances on 8201 -# Vault does internal communication by default on the port above the normal listening port, in this case 8201 -template.add_resource(SecurityGroupIngress( - "VaultServerIngress8201", - GroupId=Ref(vault_server_sg), - SourceSecurityGroupId=Ref(vault_client_sg), - FromPort=8201, - IpProtocol="tcp", - ToPort=8201 -)) - -# Allow Vault server instances to talk to other Vault server instances on 8200 -template.add_resource(SecurityGroupIngress( - "VaultServerIngress8200", - GroupId=Ref(vault_server_sg), - SourceSecurityGroupId=Ref(vault_client_sg), - FromPort=8200, - IpProtocol="tcp", - ToPort=8200 -)) - -# Allow Consul agents to talk on 8300, 8301 and 8302 -template.add_resource(SecurityGroupIngress( - "ConsulServerIngress8300", - GroupId=Ref(consul_server_sg), - SourceSecurityGroupId=Ref(consul_client_sg), - FromPort=8300, - IpProtocol="tcp", - ToPort=8300 -)) - -for port in [8301, 8302]: - template.add_resource(SecurityGroupIngress( - "ConsulServerIngressTcp{0}".format(port), - GroupId=Ref(consul_server_sg), - SourceSecurityGroupId=Ref(consul_client_sg), - FromPort=port, - IpProtocol="tcp", - ToPort=port - )) - - template.add_resource(SecurityGroupIngress( - "ConsulServerIngressUdp{0}".format(port), - GroupId=Ref(consul_server_sg), - SourceSecurityGroupId=Ref(consul_client_sg), - FromPort=port, - IpProtocol="udp", - ToPort=port - )) - -### -# -# IAM roles -# -### - -cloud_front_log_processor_lambda_iam_role = template.add_resource(Role( - "WAFLambdaRole", - AssumeRolePolicyDocument={ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": ["lambda.amazonaws.com"] - }, - "Action": ["sts:AssumeRole"] - }] - }, - Policies=[ - Policy( - PolicyName="WAFAccess", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "waf:*", - ], - "Resource": "*" - }], - } - ), - Policy( - PolicyName="S3BucketList", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "s3:ListAllMyBuckets", - ], - "Resource": "*" - }], - } - ), - Policy( - PolicyName="LogsAccess", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "logs:*", - ], - "Resource": "*" - }], - } - ), - Policy( - PolicyName="LambdaAccess", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "lambda:*", - ], - "Resource": "*" - }], - } - ), - Policy( - PolicyName="CloudFormationAccess", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "cloudformation:DescribeStacks", - "cloudformation:ListStacks", - ], - "Resource": "*" - }], - } - ), - Policy( - PolicyName="CloudWatchAccess", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "cloudwatch:PutMetricData", - ], - "Resource": "*" - }], - } - ) - ], - Path="/" -)) - -gateway_iam_role = template.add_resource(Role( - "GatewayIamRole", - AssumeRolePolicyDocument={ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"] - }, - "Action": ["sts:AssumeRole"] - }] - }, - Policies=[ - Policy( - PolicyName="gatewayPolicy", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "EC2:Describe*", - "cloudformation:SignalResource" - ], - "Resource": "*" - }], - } - ) - ], - Path="/" -)) - -cms_iam_role = template.add_resource(Role( - "CmsIamRole", - AssumeRolePolicyDocument={ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"] - }, - "Action": ["sts:AssumeRole"] - }] - }, - Policies=[ - Policy( - PolicyName="cmsPolicy", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "EC2:Describe*", - "cloudformation:SignalResource" - ], - "Resource": "*" - }], - } - ) - ], - Path="/" -)) - -vault_iam_role = template.add_resource(Role( - "VaultIamRole", - AssumeRolePolicyDocument={ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"] - }, - "Action": ["sts:AssumeRole"] - }] - }, - Policies=[ - Policy( - PolicyName="consulPolicy", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "EC2:Describe*", - "cloudformation:SignalResource" - ], - "Resource": "*" - }], - } - ) - ], - Path="/" -)) - -consul_iam_role = template.add_resource(Role( - "ConsulIamRole", - AssumeRolePolicyDocument={ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": ["ec2.amazonaws.com"] - }, - "Action": ["sts:AssumeRole"] - }] - }, - Policies=[ - Policy( - PolicyName="consulPolicy", - PolicyDocument={ - "Statement": [{ - "Effect": "Allow", - "Action": [ - "EC2:Describe*", - "cloudformation:DescribeStacks", - "cloudformation:SignalResource" - ], - "Resource": "*" - }], - } - ) - ], - Path="/" -)) - -gateway_instance_profile = template.add_resource(InstanceProfile( - "GatewayInstanceProfile", - Path="/", - Roles=[ - Ref(gateway_iam_role) - ] -)) - -cms_instance_profile = template.add_resource(InstanceProfile( - "CmsInstanceProfile", - Path="/", - Roles=[ - Ref(cms_iam_role) - ] -)) - -vault_instance_profile = template.add_resource(InstanceProfile( - "VaultInstanceProfile", - Path="/", - Roles=[ - Ref(vault_iam_role) - ] -)) - -consul_instance_profile = template.add_resource(InstanceProfile( - "ConsulInstanceProfile", - Path="/", - Roles=[ - Ref(consul_iam_role) - ] -)) - -### -# -# S3 Bucket for the dashboard client-side application -# -### - -dashboard_bucket = template.add_resource(Bucket( - "DashboardBucket", - AccessControl=PublicRead, - WebsiteConfiguration=WebsiteConfiguration( - IndexDocument="index.html", - ErrorDocument="error.html" - ), - Tags=cerberus_tags.get_tags() -)) - -dashboard_bucket_access_policy = template.add_resource(BucketPolicy( - "DashboardBucketAccessPolicy", - Bucket=Ref(dashboard_bucket), - PolicyDocument={ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "Allow-Bucket-Access", - "Effect": "Allow", - "Principal": "*", - "Action": [ - "s3:GetObject" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(dashboard_bucket), - "/*" - ] - ] - } - ] - } - ] - } -)) - -### -# -# S3 Bucket for runtime configuration files for Vault and Consul -# -### - -config_bucket = template.add_resource(Bucket( - "CerberusConfigBucket", - AccessControl=Private, - VersioningConfiguration=VersioningConfiguration( - Status="Enabled" - ), - Tags=cerberus_tags.get_tags() -)) - -config_bucket_access_policy = template.add_resource(BucketPolicy( - "CerberusConfigBucketAccessPolicy", - Bucket=Ref(config_bucket), - PolicyDocument={ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "Allow-ListBucket-Access", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(gateway_iam_role, "Arn"), - GetAtt(cms_iam_role, "Arn"), - GetAtt(vault_iam_role, "Arn"), - GetAtt(consul_iam_role, "Arn"), - GetAtt(cloud_front_log_processor_lambda_iam_role, "Arn") - ] - }, - "Action": [ - "s3:ListBucket" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket) - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/*" - ] - ] - } - ] - }, - { - "Sid": "Allow-Bucket-Access-For-Gateway", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(gateway_iam_role, "Arn") - ] - }, - "Action": [ - "s3:*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/gateway/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/gateway" - ] - ] - } - ] - }, - { - "Sid": "Allow-Bucket-Access-For-CMS", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(cms_iam_role, "Arn") - ] - }, - "Action": [ - "s3:*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/cms/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/cms" - ] - ] - } - ] - }, - { - "Sid": "Allow-Bucket-Access-For-Log-Processing-Lambda", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(cloud_front_log_processor_lambda_iam_role, "Arn") - ] - }, - "Action": [ - "s3:*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/cloud-front-log-processor/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/cloud-front-log-processor" - ] - ] - } - ] - }, - { - "Sid": "Allow-Bucket-Access-For-Vault", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(vault_iam_role, "Arn") - ] - }, - "Action": [ - "s3:*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/vault/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/vault" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/consul/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/consul" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/config/secrets.json" - ] - ] - } - ] - }, - { - "Sid": "Allow-Bucket-Access-For-Consul", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(consul_iam_role, "Arn") - ] - }, - "Action": [ - "s3:*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/consul/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/data/consul" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/consul/*" - ] - ] - }, - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(config_bucket), - "/consul" - ] - ] - } - ] - } - ] - } -)) - -### -# -# KMS for config file decryption by vault and consul instances -# -### - -config_file_key = template.add_resource(Key( - "ConfigFileKey", - Description="Cerberus encryption key for storing config files in an encrypted state.", - Enabled=True, - KeyPolicy={ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "Allow-Root-User", - "Effect": "Allow", - "Principal": { - "AWS": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:iam::", - Ref(AWS_ACCOUNT_ID), - ":root" - ] - ] - } - ] - }, - "Action": [ - "kms:*" - ], - "Resource": "*" - }, - { - "Sid": "Allow-Decrypt-From-Instances", - "Effect": "Allow", - "Principal": { - "AWS": [ - GetAtt(gateway_iam_role, "Arn"), - GetAtt(cms_iam_role, "Arn"), - GetAtt(vault_iam_role, "Arn"), - GetAtt(consul_iam_role, "Arn") - ] - }, - "Action": [ - "kms:Decrypt" - ], - "Resource": "*" - }, - { - "Sid": "Allow-Account-Admin", - "Effect": "Allow", - "Principal": { - "AWS": [ - Ref(account_admin_arn_param) - ] - }, - "Action": [ - "kms:*" - ], - "Resource": "*" - } - ] - } -)) - -### -# -# Policy for the CMS role being able to manage KMS keys -# -### - -cms_kms_policy = template.add_resource(PolicyType( - "CmsKmsPolicy", - PolicyName="CmsKmsPolicy", - Roles=[ - Ref(cms_iam_role) - ], - PolicyDocument={ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "kms:CreateAlias", - "kms:CreateKey", - "kms:DeleteAlias", - "kms:DescribeKey", - "kms:DisableKey", - "kms:DisableKeyRotation", - "kms:EnableKey", - "kms:EnableKeyRotation", - "kms:GetKeyPolicy", - "kms:GetKeyRotationStatus", - "kms:ListAliases", - "kms:ListKeyPolicies", - "kms:ListKeys", - "kms:PutKeyPolicy", - "kms:UpdateAlias", - "kms:UpdateKeyDescription" - ], - "Resource": [ - "*" - ] - } - ] - } -)) - -### -# -# RDS for the management service -# -### - -cms_db_subnet_group = template.add_resource(DBSubnetGroup( - "CmsDatabaseSubnetGroup", - DBSubnetGroupDescription="DB Subnet Group for management DB", - SubnetIds=[ - Ref(vpc_subnet_resources_by_zone_identifier[1]), - Ref(vpc_subnet_resources_by_zone_identifier[2]), - Ref(vpc_subnet_resources_by_zone_identifier[3]) - ] -)) - -cms_db_param_group = template.add_resource(DBParameterGroup( - "CmsDatabaseParamGroup", - Description="Default parameters for the cms DB", - Family="mysql5.6", - Parameters={ - "slow_query_log": 1, - "log_output": "TABLE" - } -)) - -cms_db_instance = template.add_resource(DBInstance( - "CmsDatabase", - AllocatedStorage=Ref(cms_db_allocated_storage_param), - AllowMajorVersionUpgrade=True, - AutoMinorVersionUpgrade=True, - BackupRetentionPeriod=14, - DBInstanceClass=Ref(cms_db_instance_size_param), - DBName=Ref(cms_db_name_param), - DBParameterGroupName=Ref(cms_db_param_group), - DBSubnetGroupName=Ref(cms_db_subnet_group), - Engine="MySQL", - MasterUsername=Ref(cms_db_master_username_param), - MasterUserPassword=Ref(cms_db_master_password_param), - VPCSecurityGroups=[ - Ref(tools_ingress_sg), - Ref(cms_db_sg) - ], - MultiAZ=True, - PreferredBackupWindow="13:14-13:44", - PreferredMaintenanceWindow="tue:06:48-tue:07:18", - PubliclyAccessible=False, - StorageEncrypted=True, - StorageType="gp2", - Port=Ref(cms_db_port_param), - Tags=cerberus_tags.get_tags_as_list() -)) - -### -# -# VPC Hosted Zone and Record Sets -# -### - -vpc_hosted_zone = template.add_resource(HostedZone( - "CerberusInternalHostedZone", - Name=Join(".", [aws_region_ref, Ref(vpc_hosted_zone_name_param)]), - VPCs=[ - HostedZoneVPCs( - VPCId=Ref(cerberus_vpc), - VPCRegion=aws_region_ref - ) - ], - HostedZoneTags=cerberus_tags.get_tags(), - HostedZoneConfig=HostedZoneConfiguration( - Comment="The hosted zone for the Cerberus VPC" - ) -)) - -### -# -# Outputs -# -### - -template.add_output(Output( - "vpcId", - Value=Ref(cerberus_vpc) -)) - -template.add_output(Output( - "gatewayIamRoleArn", - Value=GetAtt(gateway_iam_role, "Arn") -)) - -template.add_output(Output( - "cmsIamRoleArn", - Value=GetAtt(cms_iam_role, "Arn") -)) - -template.add_output(Output( - "consulIamRoleArn", - Value=GetAtt(consul_iam_role, "Arn") -)) - -template.add_output(Output( - "vaultIamRoleArn", - Value=GetAtt(vault_iam_role, "Arn") -)) - -template.add_output(Output( - "cloudFrontLogProcessorLambdaIamRoleArn", - Value=GetAtt(cloud_front_log_processor_lambda_iam_role, "Arn") -)) - -template.add_output(Output( - "gatewayInstanceProfileName", - Value=Ref(gateway_instance_profile) -)) - -template.add_output(Output( - "cmsInstanceProfileName", - Value=Ref(cms_instance_profile) -)) - -template.add_output(Output( - "consulInstanceProfileName", - Value=Ref(consul_instance_profile) -)) - -template.add_output(Output( - "vaultInstanceProfileName", - Value=Ref(vault_instance_profile) -)) - -template.add_output(Output( - "toolsIngressSgId", - Value=GetAtt(tools_ingress_sg, "GroupId") -)) - -template.add_output(Output( - "gatewayElbSgId", - Value=GetAtt(gateway_elb_sg, "GroupId") -)) - -template.add_output(Output( - "gatewayServerSgId", - Value=GetAtt(gateway_server_sg, "GroupId") -)) - -template.add_output(Output( - "cmsElbSgId", - Value=GetAtt(cms_elb_sg, "GroupId") -)) - -template.add_output(Output( - "cmsSgId", - Value=GetAtt(cms_sg, "GroupId") -)) - -template.add_output(Output( - "cmsDbSgId", - Value=GetAtt(cms_db_sg, "GroupId") -)) - -template.add_output(Output( - "vaultServerElbSgId", - Value=GetAtt(vault_server_elb_sg, "GroupId") -)) - -template.add_output(Output( - "vaultClientSgId", - Value=GetAtt(vault_client_sg, "GroupId") -)) - -template.add_output(Output( - "vaultServerSgId", - Value=GetAtt(vault_server_sg, "GroupId") -)) - -template.add_output(Output( - "consulClientSgId", - Value=GetAtt(consul_client_sg, "GroupId") -)) - -template.add_output(Output( - "consulServerSgId", - Value=GetAtt(consul_server_sg, "GroupId") -)) - -template.add_output(Output( - "configFileKeyId", - Value=Ref(config_file_key) -)) - -template.add_output(Output( - "dashboardBucketName", - Value=Ref(dashboard_bucket) -)) - -template.add_output(Output( - "dashboardBucketWebsiteUrl", - Value=GetAtt(dashboard_bucket, "WebsiteURL") -)) - -template.add_output(Output( - "configBucketName", - Value=Ref(config_bucket) -)) - -template.add_output(Output( - "configBucketDomainName", - Value=GetAtt(config_bucket, "DomainName") -)) - -template.add_output(Output( - "cmsDbId", - Value=Ref(cms_db_instance) -)) - -template.add_output(Output( - "cmsDbAddress", - Value=GetAtt(cms_db_instance, "Endpoint.Address") -)) - -template.add_output(Output( - "cmsDbPort", - Value=GetAtt(cms_db_instance, "Endpoint.Port") -)) - -template.add_output(Output( - "cmsDbJdbcConnectionString", - Description="JDBC connection string for cms database", - Value=Join("", [ - "jdbc:mysql://", - GetAtt(cms_db_instance, "Endpoint.Address"), - ":", - GetAtt(cms_db_instance, "Endpoint.Port"), - "/", - Ref(cms_db_name_param), - "?useUnicode=true&characterEncoding=utf8&useLegacyDatetimeCode=false&serverTimezone=UTC" - ]) -)) - -template.add_output(Output( - "vpcHostedZoneId", - Description="The VPC hosted zone ID", - Value=Ref(vpc_hosted_zone) -)) - -for zone_identifier, subnet in vpc_subnet_resources_by_zone_identifier.items(): - template.add_output(Output( - "vpcSubnetIdForAz{0}".format(zone_identifier), - Value=Ref(subnet) - )) - -template.add_output(Output( - "cmsKmsPolicyId", - Description="The policy id for CMS managing KMS keys", - Value=Ref(cms_kms_policy) -)) - -cerberus_network.add_outputs(template) - -### -# -# Print! -# -### - -cf_file = open(CF_FILE, 'w') -cf_file.truncate() -cf_file.write(template.to_json(indent=0)) -cf_file.close() - -print("CloudFormation template written to: {0}".format(CF_FILE)) diff --git a/src/main/resources/cloudformation/.gitkeep b/src/main/resources/cloudformation/.gitkeep deleted file mode 100644 index 010921d6..00000000 --- a/src/main/resources/cloudformation/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# Generated CF templates go here during builds. \ No newline at end of file diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/base.yaml new file mode 100644 index 00000000..c8bbe9b9 --- /dev/null +++ b/src/main/resources/cloudformation/base.yaml @@ -0,0 +1,161 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Creates the necessary IAM roles, S3 buckets, and KMS keys for Cerberus +Outputs: + configBucketDomainName: + Value: !GetAtt 'CerberusConfigBucket.DomainName' + configBucketName: + Value: !Ref 'CerberusConfigBucket' + configFileKeyId: + Value: !Ref 'ConfigFileKey' + cmsIamRoleName: + Value: !Ref 'CmsIamRole' + Export: + Name: !Sub "${AWS::StackName}-cmsIamRoleName" + cmsKmsPolicyId: + Description: The policy id for CMS managing KMS keys + Value: !Ref 'CmsKmsPolicy' +Parameters: + accountAdminArn: + Description: The ARN for a IAM user, group or role that can create this stack. + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped + under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project. + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Environment name, e.g. "cerberus-{environment}"' + Type: String +Resources: + CerberusConfigBucket: + Properties: + AccessControl: Private + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + - Key: email + Value: !Ref 'tagEmail' + VersioningConfiguration: + Status: Enabled + Type: AWS::S3::Bucket + CerberusConfigBucketAccessPolicy: + Properties: + Bucket: !Ref 'CerberusConfigBucket' + PolicyDocument: + Statement: + - Action: + - s3:ListBucket + Effect: Allow + Principal: + AWS: + - !GetAtt 'CmsIamRole.Arn' + Resource: + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket']] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /*]] + Sid: Allow-ListBucket-Access + - Action: + - s3:* + Effect: Allow + Principal: + AWS: + - !GetAtt 'CmsIamRole.Arn' + Resource: + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /data/cms/*]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /data/cms]] + Sid: Allow-Bucket-Access-For-CMS + Version: '2012-10-17' + Type: AWS::S3::BucketPolicy + CmsIamRole: + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: + - sts:AssumeRole + Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Version: '2012-10-17' + Path: / + Policies: + - PolicyDocument: + Statement: + - Action: + - EC2:Describe* + - cloudformation:SignalResource + Effect: Allow + Resource: '*' + PolicyName: cmsPolicy + Type: AWS::IAM::Role + CmsKmsPolicy: + Properties: + PolicyDocument: + Statement: + - Action: + - kms:CreateAlias + - kms:CreateKey + - kms:DeleteAlias + - kms:DescribeKey + - kms:DisableKey + - kms:DisableKeyRotation + - kms:EnableKey + - kms:EnableKeyRotation + - kms:GetKeyPolicy + - kms:GetKeyRotationStatus + - kms:ListAliases + - kms:ListKeyPolicies + - kms:ListKeys + - kms:PutKeyPolicy + - kms:UpdateAlias + - kms:UpdateKeyDescription + Effect: Allow + Resource: + - '*' + Version: '2012-10-17' + PolicyName: CmsKmsPolicy + Roles: + - Ref: 'CmsIamRole' + Type: AWS::IAM::Policy + ConfigFileKey: + Properties: + Description: Cerberus encryption key for storing config files in an encrypted + state. + Enabled: 'true' + KeyPolicy: + Statement: + - Action: + - kms:* + Effect: Allow + Principal: + AWS: + - !Join ['', ['arn:aws:iam::', !Ref 'AWS::AccountId', ':root']] + Resource: '*' + Sid: Allow-Root-User + - Action: + - kms:Decrypt + Effect: Allow + Principal: + AWS: + - !GetAtt 'CmsIamRole.Arn' + Resource: '*' + Sid: Allow-Decrypt-From-Instances + - Action: + - kms:* + Effect: Allow + Principal: + AWS: + Ref: 'accountAdminArn' + Resource: '*' + Sid: Allow-Account-Admin + Version: '2012-10-17' + Type: AWS::KMS::Key \ No newline at end of file diff --git a/src/main/resources/cloudformation/cms-cluster.yaml b/src/main/resources/cloudformation/cms-cluster.yaml new file mode 100644 index 00000000..8c05fb82 --- /dev/null +++ b/src/main/resources/cloudformation/cms-cluster.yaml @@ -0,0 +1,131 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Launches the CMS cluster in the Cerberus VPC +Outputs: + autoscalingGroupLogicalId: + Value: !Ref 'CmsAutoScalingGroup' + launchConfigurationLogicalId: + Value: !Ref 'CmsLaunchConfiguration' + cmsInstanceProfileName: + Value: !Ref 'CmsInstanceProfile' +Parameters: + albStackName: + Description: The name of the Cerberus ALB CloudFormation stack + Type: String + baseStackName: + Description: The name of the Cerberus base CloudFormation stack + Type: String + cmsAmiId: + Description: The AMI ID for the CMS instances + Type: String + cmsStackName: + Description: The name of the Cerberus CMS CloudFormation stack + Type: String + desiredInstances: + Default: '3' + Description: Desired Number of Auto Scaling Instances + Type: Number + instanceSize: + Description: The instance size for the CMS instances + Type: String + keyPairName: + Description: The key pair to be associated with the EC2 instances + Type: String + maximumInstances: + Default: '4' + Description: Maximum Number of Auto Scaling Instances (must be larger than min) + Type: Number + minimumInstances: + Default: '3' + Description: Minimum Number of Auto Scaling Instances + Type: Number + pauseTime: + Default: PT15M + Description: Pause time for AutoScalingRollingUpdate e.g PT15M + Type: String + sgStackName: + Description: The name of the Cerberus Security Groups CloudFormation stack + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project. + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Name assigned to the stack. Format: "cerberus-{environment}"' + Type: String + userData: + Description: CMS user data + Type: String + vpcSubnetIdForAz1: + Description: The subnet for the first availability zone + Type: String + vpcSubnetIdForAz2: + Description: The subnet for the second availability zone + Type: String + vpcSubnetIdForAz3: + Description: The subnet for the third availability zone + Type: String + waitOnResourceSignals: + Default: 'True' + Description: Enabling WaitOnResourceSignals allows CloudFormation to wait until + you have received a success signal before performing the next scaling action. + Type: String +Resources: + CmsAutoScalingGroup: + Properties: + DesiredCapacity: !Ref 'desiredInstances' + HealthCheckGracePeriod: 900 + HealthCheckType: ELB + LaunchConfigurationName: !Ref 'CmsLaunchConfiguration' + TargetGroupARNs: + - Fn::ImportValue: !Sub "${albStackName}-cmsTargetGroupArn" + MaxSize: !Ref 'maximumInstances' + MinSize: !Ref 'minimumInstances' + Tags: + - Key: Name + PropagateAtLaunch: true + Value: !Ref 'tagName' + - Key: email + PropagateAtLaunch: true + Value: !Ref 'tagEmail' + - Key: classification + PropagateAtLaunch: true + Value: !Ref 'tagClassification' + - Key: costcenter + PropagateAtLaunch: true + Value: !Ref 'tagCostcenter' + VPCZoneIdentifier: + - Ref: 'vpcSubnetIdForAz1' + - Ref: 'vpcSubnetIdForAz2' + - Ref: 'vpcSubnetIdForAz3' + Type: AWS::AutoScaling::AutoScalingGroup + UpdatePolicy: + AutoScalingRollingUpdate: + MaxBatchSize: 1 + MinInstancesInService: !Ref 'minimumInstances' + PauseTime: !Ref 'pauseTime' + WaitOnResourceSignals: !Ref 'waitOnResourceSignals' + CmsInstanceProfile: + Properties: + Path: / + Roles: + - Fn::ImportValue: !Sub "${baseStackName}-cmsIamRoleName" + Type: AWS::IAM::InstanceProfile + CmsLaunchConfiguration: + Properties: + AssociatePublicIpAddress: 'true' + IamInstanceProfile: !Ref 'CmsInstanceProfile' + ImageId: !Ref 'cmsAmiId' + InstanceMonitoring: 'true' + InstanceType: !Ref 'instanceSize' + KeyName: !Ref 'keyPairName' + SecurityGroups: + - Fn::ImportValue: !Sub "${sgStackName}-whitelistIngressSgId" + - Fn::ImportValue: !Sub "${sgStackName}-cmsSgId" + UserData: !Ref 'userData' + Type: AWS::AutoScaling::LaunchConfiguration diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml new file mode 100644 index 00000000..906bfd8f --- /dev/null +++ b/src/main/resources/cloudformation/database.yaml @@ -0,0 +1,108 @@ +AWSTemplateFormatVersion: '2010-09-09' +Conditions: + RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] +Description: Creates the database for use by the Cerberus Management Service (CMS) +Outputs: + cmsDbAddress: + Value: !GetAtt 'CmsDatabase.Endpoint.Address' + cmsDbId: + Value: !Ref 'CmsDatabase' + cmsDbJdbcConnectionString: + Description: JDBC connection string for cms database + Value: !Join ['', ['jdbc:mysql://', !GetAtt 'CmsDatabase.Endpoint.Address', ':', + !GetAtt 'CmsDatabase.Endpoint.Port', /, !Ref 'cmsDbName', '?useUnicode=true&characterEncoding=utf8&useLegacyDatetimeCode=false&serverTimezone=UTC']] +Parameters: + cmsDbAllocatedStorage: + Default: '100' + Description: The allocated storage for the RDS instance. + Type: String + cmsDbInstanceSize: + Default: db.r3.large + Description: MySQL DB instance class + Type: String + cmsDbMasterPassword: + Description: Master password for the cms RDS instance + Type: String + cmsDbMasterUsername: + Description: Master username for the cms RDS instance + Type: String + cmsDbName: + Description: The name of the database initially create on the RDS instance + Type: String + sgStackName: + Description: The name of the Cerberus Security Groups CloudFormation stack + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped + under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project.aaa + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Environment name, e.g. "cerberus-{environment}"' + Type: String + vpcSubnetIdForAz1: + Description: The subnet for the first availability zone + Type: String + vpcSubnetIdForAz2: + Description: The subnet for the second availability zone + Type: String + vpcSubnetIdForAz3: + Description: The subnet for the third availability zone + Type: String +Resources: + CmsDatabase: + Properties: + AllocatedStorage: !Ref 'cmsDbAllocatedStorage' + AllowMajorVersionUpgrade: 'true' + AutoMinorVersionUpgrade: 'true' + BackupRetentionPeriod: 14 + DBInstanceClass: !Ref 'cmsDbInstanceSize' + DBName: !Ref 'cmsDbName' + DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' + DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' + Engine: MySQL + MasterUserPassword: !Ref 'cmsDbMasterPassword' + MasterUsername: !Ref 'cmsDbMasterUsername' + MultiAZ: 'true' + Port: + Fn::ImportValue: !Sub "${sgStackName}-cmsDbPort" + PreferredBackupWindow: 13:14-13:44 + PreferredMaintenanceWindow: tue:06:48-tue:07:18 + PubliclyAccessible: 'false' + StorageEncrypted: 'true' + StorageType: gp2 + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VPCSecurityGroups: + - Fn::ImportValue: !Sub "${sgStackName}-cmsDbSgId" + Type: AWS::RDS::DBInstance + CmsDatabaseParamGroup: + Properties: + Description: Default parameters for the cms DB + Family: mysql5.6 + Parameters: + log_output: TABLE + slow_query_log: 1 + Type: AWS::RDS::DBParameterGroup + CmsDatabaseSubnetGroup: + Properties: + DBSubnetGroupDescription: DB Subnet Group for management DB + SubnetIds: + - Ref: 'vpcSubnetIdForAz1' + - Ref: 'vpcSubnetIdForAz2' + - Ref: 'vpcSubnetIdForAz3' + Type: AWS::RDS::DBSubnetGroup + \ No newline at end of file diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml new file mode 100644 index 00000000..10a1ee25 --- /dev/null +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -0,0 +1,147 @@ +AWSTemplateFormatVersion: '2010-09-09' +Conditions: + RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] +Description: Creates the application Load Balancer for the Cerberus environment +Outputs: + albAccessLogBucket: + Value: !Ref 'ALBLogBucket' + appLoadBalancerLogicalId: + Value: !Ref 'ApplicationLoadBalancer' + Export: + Name: !Sub "${AWS::StackName}-appLoadBalancerLogicalId" + albDnsName: + Value: !GetAtt 'ApplicationLoadBalancer.DNSName' + Export: + Name: !Sub "${AWS::StackName}-appLoadBalancerDnsName" + cmsTargetGroup: + Value: !Ref 'CmsTargetGroup' + Export: + Name: !Sub "${AWS::StackName}-cmsTargetGroupArn" +Parameters: + sgStackName: + Description: The name of the stack containing Cerberus IAM roles and SGs stack + Type: String + sslCertificateArn: + Description: TLS certificate ARN for CMS + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped + under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project. + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Environment name, e.g. "cerberus-{environment}"' + Type: String + vpcId: + Description: The VPC in which the EC2 instances will be run + Type: String + vpcSubnetIdForAz1: + Description: The subnet for the first availability zone + Type: String + vpcSubnetIdForAz2: + Description: The subnet for the second availability zone + Type: String + vpcSubnetIdForAz3: + Description: The subnet for the third availability zone + Type: String +Resources: + ALBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + Certificates: + - CertificateArn: !Ref sslCertificateArn + DefaultActions: + - Type: forward + TargetGroupArn: !Ref 'CmsTargetGroup' + LoadBalancerArn: !Ref 'ApplicationLoadBalancer' + Port: 443 + Protocol: HTTPS + SslPolicy: ELBSecurityPolicy-TLS-1-2-2017-01 + ALBLogBucket: + Properties: + AccessControl: BucketOwnerFullControl + + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + - Key: email + Value: !Ref 'tagEmail' + Type: AWS::S3::Bucket + ALBLogBucketPolicy: + Properties: + Bucket: !Ref 'ALBLogBucket' + PolicyDocument: + Statement: + - Action: + - s3:* + Effect: Allow + Principal: + AWS: + - 797873946194 # the AWS-owned ElasticLoadBalancing account for us-west-2 (required) + Resource: + - !Join ['', ['arn:aws:s3:::', !Ref 'ALBLogBucket', /*]] + Sid: Allow-ALB-Log-Access + Version: '2012-10-17' + Type: AWS::S3::BucketPolicy + ApplicationLoadBalancer: + Properties: + LoadBalancerAttributes: + - Key: access_logs.s3.enabled + Value: true + - Key: access_logs.s3.bucket + Value: !Ref 'ALBLogBucket' + - Key: access_logs.s3.prefix + Value: environment + Scheme: internet-facing + SecurityGroups: + - Fn::ImportValue: !Sub "${sgStackName}-appLoadBalancerSgId" + Subnets: + - Ref: 'vpcSubnetIdForAz1' + - Ref: 'vpcSubnetIdForAz2' + - Ref: 'vpcSubnetIdForAz3' + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + CmsTargetGroup: + Type: "AWS::ElasticLoadBalancingV2::TargetGroup" + Properties: + HealthCheckIntervalSeconds: 5 + HealthCheckPath: /healthcheck + HealthCheckPort: 8443 + HealthCheckProtocol: HTTPS + HealthCheckTimeoutSeconds: 2 + HealthyThresholdCount: 2 + Port: 8443 + Protocol: HTTPS + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + TargetGroupAttributes: + - Key: deregistration_delay.timeout_seconds + Value: 10 + UnhealthyThresholdCount: 2 + VpcId: + Ref: 'vpcId' diff --git a/src/main/resources/cloudformation/route53.yaml b/src/main/resources/cloudformation/route53.yaml new file mode 100644 index 00000000..426bd325 --- /dev/null +++ b/src/main/resources/cloudformation/route53.yaml @@ -0,0 +1,28 @@ +AWSTemplateFormatVersion: '2010-09-09' +Conditions: + RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] +Description: Creates the Route53 record for Cerberus +Outputs: + recordSet: + Value: !Ref 'CerberusPublicRecordSet' +Parameters: + appLoadBalancerStackName: + Description: The name of the Cerberus load balancer CloudFormation stack + Type: String + hostname: + Description: The base hostname for the public facing ALB + Type: String + hostedZoneId: + Description: The base hosted zone to add the ALB CNAME to + Type: String +Resources: + CerberusPublicRecordSet: + Properties: + HostedZoneId: + Ref: hostedZoneId + Name: !Join [., [!Ref 'hostname', '']] + ResourceRecords: + - Fn::ImportValue: !Sub "${appLoadBalancerStackName}-appLoadBalancerDnsName" + TTL: 30 + Type: CNAME + Type: AWS::Route53::RecordSet \ No newline at end of file diff --git a/src/main/resources/cloudformation/security-groups.yaml b/src/main/resources/cloudformation/security-groups.yaml new file mode 100644 index 00000000..83ffbf63 --- /dev/null +++ b/src/main/resources/cloudformation/security-groups.yaml @@ -0,0 +1,145 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Launches the Security Groups required for Cerberus +Outputs: + appLoadBalancerSgId: + Value: !GetAtt 'AlbSg.GroupId' + Export: + Name: !Sub "${AWS::StackName}-appLoadBalancerSgId" + cmsDbPort: + Value: !Ref 'cmsDbPort' + Export: + Name: !Sub "${AWS::StackName}-cmsDbPort" + cmsSgId: + Value: !GetAtt 'CmsSg.GroupId' + Export: + Name: !Sub "${AWS::StackName}-cmsSgId" + cmsDbSgId: + Value: !GetAtt 'CmsDbSg.GroupId' + Export: + Name: !Sub "${AWS::StackName}-cmsDbSgId" + whitelistIngressSgId: + Value: !GetAtt 'WhitelistIngressSg.GroupId' + Export: + Name: !Sub "${AWS::StackName}-whitelistIngressSgId" +Parameters: + appLoadBalancerCidrBlock: + AllowedPattern: '[0-9./]*' + Default: 0.0.0.0/0 + Description: The application load balancer CIDR block for where traffic is allowed from + MaxLength: '20' + MinLength: '9' + Type: String + cmsDbPort: + Default: '3306' + Description: Port for the cms DB instance + Type: Number + vpcId: + Description: ID of the VPC to associate with the security groups + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped + under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project. + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Environment name, e.g. "cerberus-{environment}"' + Type: String +Resources: + AlbSg: + Properties: + GroupDescription: ALB Security Group + Tags: + - Key: AutoUpdate + Value: 'true' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: + Ref: 'vpcId' + Type: AWS::EC2::SecurityGroup + AlbIngressFromInternetSg443: + Properties: + CidrIp: !Ref 'appLoadBalancerCidrBlock' + FromPort: 443 + GroupId: !Ref 'AlbSg' + IpProtocol: tcp + ToPort: 443 + Type: AWS::EC2::SecurityGroupIngress + CmsDbFromCms: + Properties: + FromPort: !Ref 'cmsDbPort' + GroupId: !Ref 'CmsDbSg' + IpProtocol: tcp + SourceSecurityGroupId: !Ref 'CmsSg' + ToPort: !Ref 'cmsDbPort' + Type: AWS::EC2::SecurityGroupIngress + CmsDbSg: + Properties: + GroupDescription: Management Server Database Security Group + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: + Ref: 'vpcId' + Type: AWS::EC2::SecurityGroup + CmsIngressFromAlb8077: + Properties: + FromPort: 8077 + GroupId: !Ref 'CmsSg' + IpProtocol: tcp + SourceSecurityGroupId: !Ref 'AlbSg' + ToPort: 8077 + Type: AWS::EC2::SecurityGroupIngress + CmsIngressFromCmsElb8443: + Properties: + FromPort: 8443 + GroupId: !Ref 'CmsSg' + IpProtocol: tcp + SourceSecurityGroupId: !Ref 'AlbSg' + ToPort: 8443 + Type: AWS::EC2::SecurityGroupIngress + CmsSg: + Properties: + GroupDescription: Management Server Instance Security Group + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: + Ref: 'vpcId' + Type: AWS::EC2::SecurityGroup + WhitelistIngressSg: + Properties: + GroupDescription: Administration ingress from tools NAT boxes + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: + Ref: 'vpcId' + Type: AWS::EC2::SecurityGroup \ No newline at end of file diff --git a/src/main/resources/cloudformation/vpc.yaml b/src/main/resources/cloudformation/vpc.yaml new file mode 100644 index 00000000..814a04a2 --- /dev/null +++ b/src/main/resources/cloudformation/vpc.yaml @@ -0,0 +1,212 @@ +AWSTemplateFormatVersion: '2010-09-09' +Conditions: + RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] +Description: Creates the Cerberus VPC and foundational resources +Outputs: + subnetCidrBlockForAz1: + Description: Cidr block for subnet in AZ '1' + Value: 172.20.0.0/24 + subnetCidrBlockForAz2: + Description: Cidr block for subnet in AZ '2' + Value: 172.20.4.0/24 + subnetCidrBlockForAz3: + Description: Cidr block for subnet in AZ '3' + Value: 172.20.8.0/24 +Parameters: + internetGatewayCidrBlock: + AllowedPattern: '[0-9./]*' + Default: 0.0.0.0/0 + Description: The application load balancer CIDR block for where traffic is allowed from + MaxLength: '20' + MinLength: '9' + Type: String + az1: + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: An availability zone identifier for zone '1' + Type: String + az2: + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: An availability zone identifier for zone '2' + Type: String + az3: + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: An availability zone identifier for zone '3' + Type: String + subnetCidrBlockForAz1: + AllowedPattern: '[0-9./]*' + Default: 172.20.0.0/24 + Description: Cidr block for subnet in AZ '1' + MaxLength: '20' + MinLength: '9' + Type: String + subnetCidrBlockForAz2: + AllowedPattern: '[0-9./]*' + Default: 172.20.4.0/24 + Description: Cidr block for subnet in AZ '2' + MaxLength: '20' + MinLength: '9' + Type: String + subnetCidrBlockForAz3: + AllowedPattern: '[0-9./]*' + Default: 172.20.8.0/24 + Description: Cidr block for subnet in AZ '3' + MaxLength: '20' + MinLength: '9' + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped + under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project. + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Environment name, e.g. "cerberus-{environment}"' + Type: String + vpcCidrBlock: + AllowedPattern: '[0-9./]*' + Default: 172.20.0.0/20 + Description: The VPC's CIDR block for internal IPv4 addressing + MaxLength: '20' + MinLength: '9' + Type: String +Resources: + CerberusDhcpOptions: + Properties: + DomainName: !If [RegionEqualsEastOne, ec2.internal, !Join [., [!Ref 'AWS::Region', + compute.internal]]] + DomainNameServers: + - AmazonProvidedDNS + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + Type: AWS::EC2::DHCPOptions + CerberusInternetGateway: + Properties: + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + Type: AWS::EC2::InternetGateway + CerberusRouteInternetGateway: + Properties: + DestinationCidrBlock: !Ref 'internetGatewayCidrBlock' + GatewayId: !Ref 'CerberusInternetGateway' + RouteTableId: !Ref 'CerberusRouteTable' + Type: AWS::EC2::Route + CerberusRouteTable: + Properties: + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: !Ref 'CerberusVpc' + Type: AWS::EC2::RouteTable + CerberusSubnetForAz1: + Properties: + AvailabilityZone: !Ref 'az1' + CidrBlock: !Ref 'subnetCidrBlockForAz1' + MapPublicIpOnLaunch: 'true' + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: !Ref 'CerberusVpc' + Type: AWS::EC2::Subnet + CerberusSubnetForAz2: + Properties: + AvailabilityZone: !Ref 'az2' + CidrBlock: !Ref 'subnetCidrBlockForAz2' + MapPublicIpOnLaunch: 'true' + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: !Ref 'CerberusVpc' + Type: AWS::EC2::Subnet + CerberusSubnetForAz3: + Properties: + AvailabilityZone: !Ref 'az3' + CidrBlock: !Ref 'subnetCidrBlockForAz3' + MapPublicIpOnLaunch: 'true' + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + VpcId: !Ref 'CerberusVpc' + Type: AWS::EC2::Subnet + CerberusSubnetRouteTableAssociationForAz1: + Properties: + RouteTableId: !Ref 'CerberusRouteTable' + SubnetId: !Ref 'CerberusSubnetForAz1' + Type: AWS::EC2::SubnetRouteTableAssociation + CerberusSubnetRouteTableAssociationForAz2: + Properties: + RouteTableId: !Ref 'CerberusRouteTable' + SubnetId: !Ref 'CerberusSubnetForAz2' + Type: AWS::EC2::SubnetRouteTableAssociation + CerberusSubnetRouteTableAssociationForAz3: + Properties: + RouteTableId: !Ref 'CerberusRouteTable' + SubnetId: !Ref 'CerberusSubnetForAz3' + Type: AWS::EC2::SubnetRouteTableAssociation + CerberusVpc: + Properties: + CidrBlock: !Ref 'vpcCidrBlock' + EnableDnsHostnames: 'true' + EnableDnsSupport: 'true' + Tags: + - Key: Name + Value: !Ref 'tagName' + - Key: email + Value: !Ref 'tagEmail' + - Key: classification + Value: !Ref 'tagClassification' + - Key: costcenter + Value: !Ref 'tagCostcenter' + Type: AWS::EC2::VPC + CerberusVpcDhcpOptionsAssociation: + Properties: + DhcpOptionsId: !Ref 'CerberusDhcpOptions' + VpcId: !Ref 'CerberusVpc' + Type: AWS::EC2::VPCDHCPOptionsAssociation + CerberusVpcGatewayAttachment: + Properties: + InternetGatewayId: !Ref 'CerberusInternetGateway' + VpcId: !Ref 'CerberusVpc' + Type: AWS::EC2::VPCGatewayAttachment diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml new file mode 100644 index 00000000..e3fca6c7 --- /dev/null +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -0,0 +1,179 @@ +AWSTemplateFormatVersion: '2010-09-09' +Conditions: + RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] +Description: Creates the Web Application Firewall (WAF) for the Cerberus ALB +Outputs: + autoBlockIPSetID: + Value: !Ref 'WAFAutoBlockSet' + manualBlockIPSetID: + Value: !Ref 'WAFManualBlockSet' + whiteListIPSetID: + Value: !Ref 'WAFWhiteListSet' +Parameters: + appLoadBalancerStackName: + Description: The name of the Cerberus load balancer CloudFormation stack + Type: String + tagClassification: + Default: Gold + Description: Denotes which category of Data Classification the instance is grouped + under. + Type: String + tagCostcenter: + Description: Represents the Cost Center associated with the team/project. + Type: String + tagEmail: + Description: E-mail address for group or person responsible for the stack. + Type: String + tagName: + Description: 'Environment name, e.g. "cerberus-{environment}"' + Type: String +Resources: + CerberusWAFWebAcl: + DependsOn: + - WAFManualBlockRule + - WAFAutoBlockRule + Properties: + DefaultAction: + Type: ALLOW + MetricName: CerberusWAF + Name: !Join [-, [waf, !Ref 'tagName']] + Rules: + - Action: + Type: BLOCK + Priority: 1 + RuleId: !Ref 'CerberusWafSizeConstraintRule' + - Action: + Type: BLOCK + Priority: 2 + RuleId: !Ref 'CerberusWafSqlInjectionRule' + - Action: + Type: BLOCK + Priority: 3 + RuleId: !Ref 'CerberusWafXssRule' + - Action: + Type: ALLOW + Priority: 4 + RuleId: !Ref 'WAFWhiteListRule' + - Action: + Type: BLOCK + Priority: 5 + RuleId: !Ref 'WAFManualBlockRule' + - Action: + Type: BLOCK + Priority: 6 + RuleId: !Ref 'WAFAutoBlockRule' + Type: AWS::WAFRegional::WebACL + CerberusWebACLAssociation: + Type: "AWS::WAFRegional::WebACLAssociation" + Properties: + ResourceArn: + Fn::ImportValue: !Sub "${appLoadBalancerStackName}-appLoadBalancerLogicalId" + WebACLId: + !Ref 'CerberusWAFWebAcl' + CerberusWafSizeConstraintRule: + Properties: + MetricName: CerberusWafSizeConstraint + Name: CerberusWafSizeConstraintRule + Predicates: + - DataId: !Ref 'CerberusWafSizeConstraintSet' + Negated: 'false' + Type: SizeConstraint + Type: AWS::WAFRegional::Rule + CerberusWafSizeConstraintSet: + Properties: + Name: CerberusWafSizeConstraintSet + SizeConstraints: + - ComparisonOperator: GE + FieldToMatch: + Type: BODY + Size: 256000 + TextTransformation: NONE + Type: AWS::WAFRegional::SizeConstraintSet + CerberusWafSqlInjectionMatchSet: + Properties: + Name: CerberusWafSqlInjectionMatchSet + SqlInjectionMatchTuples: + - FieldToMatch: + Type: URI + TextTransformation: NONE + - FieldToMatch: + Type: QUERY_STRING + TextTransformation: NONE + - FieldToMatch: + Type: BODY + TextTransformation: NONE + Type: AWS::WAFRegional::SqlInjectionMatchSet + CerberusWafSqlInjectionRule: + Properties: + MetricName: CerberusWafSqlInjection + Name: CerberusWafSqlInjectionRule + Predicates: + - DataId: !Ref 'CerberusWafSqlInjectionMatchSet' + Negated: 'false' + Type: SqlInjectionMatch + Type: AWS::WAFRegional::Rule + CerberusWafXssMatchSet: + Properties: + Name: CerberusWafXssMatchSet + XssMatchTuples: + - FieldToMatch: + Type: URI + TextTransformation: NONE + - FieldToMatch: + Type: QUERY_STRING + TextTransformation: NONE + - FieldToMatch: + Type: BODY + TextTransformation: NONE + Type: AWS::WAFRegional::XssMatchSet + CerberusWafXssRule: + Properties: + MetricName: CerberusWafXss + Name: CerberusWafXssRule + Predicates: + - DataId: !Ref 'CerberusWafXssMatchSet' + Negated: 'false' + Type: XssMatch + Type: AWS::WAFRegional::Rule + WAFAutoBlockRule: + DependsOn: WAFAutoBlockSet + Properties: + MetricName: AutoBlockRule + Name: Auto Block Rule + Predicates: + - DataId: !Ref 'WAFAutoBlockSet' + Negated: 'false' + Type: IPMatch + Type: AWS::WAFRegional::Rule + WAFAutoBlockSet: + Properties: + Name: Auto Block Set + Type: AWS::WAFRegional::IPSet + WAFManualBlockRule: + DependsOn: WAFManualBlockSet + Properties: + MetricName: ManualBlockRule + Name: Manual Block Rule + Predicates: + - DataId: !Ref 'WAFManualBlockSet' + Negated: 'false' + Type: IPMatch + Type: AWS::WAFRegional::Rule + WAFManualBlockSet: + Properties: + Name: Manual Block Set + Type: AWS::WAFRegional::IPSet + WAFWhiteListRule: + DependsOn: WAFWhiteListSet + Properties: + MetricName: WhiteListRule + Name: White List Rule + Predicates: + - DataId: !Ref 'WAFWhiteListSet' + Negated: 'false' + Type: IPMatch + Type: AWS::WAFRegional::Rule + WAFWhiteListSet: + Properties: + Name: White List Set + Type: AWS::WAFRegional::IPSet \ No newline at end of file From d800ae13cbcc893bfceb999802af744e42e2a0ef Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Thu, 2 Nov 2017 10:44:14 -0700 Subject: [PATCH 06/69] Use Amazon Aurora instead of MySQL (#68) * Remove whitelist security group from database * Use Aurora instead of MySQL + cleanup --- .../resources/cloudformation/cms-cluster.yaml | 8 +-- .../resources/cloudformation/database.yaml | 62 ++++++++++++------- .../cloudformation/load-balancer.yaml | 4 +- .../resources/cloudformation/route53.yaml | 4 +- src/main/resources/cloudformation/vpc.yaml | 15 ++++- .../cloudformation/web-app-firewall.yaml | 4 +- 6 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/main/resources/cloudformation/cms-cluster.yaml b/src/main/resources/cloudformation/cms-cluster.yaml index 8c05fb82..bf55b8db 100644 --- a/src/main/resources/cloudformation/cms-cluster.yaml +++ b/src/main/resources/cloudformation/cms-cluster.yaml @@ -8,7 +8,7 @@ Outputs: cmsInstanceProfileName: Value: !Ref 'CmsInstanceProfile' Parameters: - albStackName: + loadBalancerStackName: Description: The name of the Cerberus ALB CloudFormation stack Type: String baseStackName: @@ -17,14 +17,12 @@ Parameters: cmsAmiId: Description: The AMI ID for the CMS instances Type: String - cmsStackName: - Description: The name of the Cerberus CMS CloudFormation stack - Type: String desiredInstances: Default: '3' Description: Desired Number of Auto Scaling Instances Type: Number instanceSize: + Default: 'm3.medium' Description: The instance size for the CMS instances Type: String keyPairName: @@ -83,7 +81,7 @@ Resources: HealthCheckType: ELB LaunchConfigurationName: !Ref 'CmsLaunchConfiguration' TargetGroupARNs: - - Fn::ImportValue: !Sub "${albStackName}-cmsTargetGroupArn" + - Fn::ImportValue: !Sub "${loadBalancerStackName}-cmsTargetGroupArn" MaxSize: !Ref 'maximumInstances' MinSize: !Ref 'minimumInstances' Tags: diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml index 906bfd8f..5797e644 100644 --- a/src/main/resources/cloudformation/database.yaml +++ b/src/main/resources/cloudformation/database.yaml @@ -4,18 +4,14 @@ Conditions: Description: Creates the database for use by the Cerberus Management Service (CMS) Outputs: cmsDbAddress: - Value: !GetAtt 'CmsDatabase.Endpoint.Address' + Value: !GetAtt 'CmsDatabaseCluster.Endpoint.Address' cmsDbId: - Value: !Ref 'CmsDatabase' + Value: !Ref 'CmsDbInstance' cmsDbJdbcConnectionString: Description: JDBC connection string for cms database - Value: !Join ['', ['jdbc:mysql://', !GetAtt 'CmsDatabase.Endpoint.Address', ':', - !GetAtt 'CmsDatabase.Endpoint.Port', /, !Ref 'cmsDbName', '?useUnicode=true&characterEncoding=utf8&useLegacyDatetimeCode=false&serverTimezone=UTC']] + Value: !Join ['', ['jdbc:mysql://', !GetAtt 'CmsDbInstance.Endpoint.Address', ':', + !GetAtt 'CmsDatabaseCluster.Endpoint.Port', /, !Ref 'cmsDbName', '?useUnicode=true&characterEncoding=utf8&useLegacyDatetimeCode=false&serverTimezone=UTC']] Parameters: - cmsDbAllocatedStorage: - Default: '100' - Description: The allocated storage for the RDS instance. - Type: String cmsDbInstanceSize: Default: db.r3.large Description: MySQL DB instance class @@ -23,12 +19,21 @@ Parameters: cmsDbMasterPassword: Description: Master password for the cms RDS instance Type: String + NoEcho: true cmsDbMasterUsername: Description: Master username for the cms RDS instance Type: String cmsDbName: Description: The name of the database initially create on the RDS instance Type: String + cmsDbPrimaryAz: + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: The availability zone for the primary CMS DB Instance + Type: String + cmsDbReplicaAz: + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: The availability zone for the standby read replica CMS DB Instance + Type: String sgStackName: Description: The name of the Cerberus Security Groups CloudFormation stack Type: String @@ -56,27 +61,22 @@ Parameters: Description: The subnet for the third availability zone Type: String Resources: - CmsDatabase: + CmsDatabaseCluster: Properties: - AllocatedStorage: !Ref 'cmsDbAllocatedStorage' - AllowMajorVersionUpgrade: 'true' - AutoMinorVersionUpgrade: 'true' + AvailabilityZones: + - Ref: 'cmsDbPrimaryAz' + - Ref: 'cmsDbReplicaAz' BackupRetentionPeriod: 14 - DBInstanceClass: !Ref 'cmsDbInstanceSize' - DBName: !Ref 'cmsDbName' - DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' + DatabaseName: !Ref 'cmsDbName' DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' - Engine: MySQL + Engine: aurora MasterUserPassword: !Ref 'cmsDbMasterPassword' MasterUsername: !Ref 'cmsDbMasterUsername' - MultiAZ: 'true' Port: Fn::ImportValue: !Sub "${sgStackName}-cmsDbPort" PreferredBackupWindow: 13:14-13:44 PreferredMaintenanceWindow: tue:06:48-tue:07:18 - PubliclyAccessible: 'false' StorageEncrypted: 'true' - StorageType: gp2 Tags: - Key: Name Value: !Ref 'tagName' @@ -86,13 +86,33 @@ Resources: Value: !Ref 'tagClassification' - Key: costcenter Value: !Ref 'tagCostcenter' - VPCSecurityGroups: + VpcSecurityGroupIds: - Fn::ImportValue: !Sub "${sgStackName}-cmsDbSgId" + Type: AWS::RDS::DBCluster + CmsDbInstance: + Properties: + DBClusterIdentifier: !Ref CmsDatabaseCluster + DBInstanceClass: !Ref 'cmsDbInstanceSize' + DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' + DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' + Engine: aurora + PubliclyAccessible: 'false' + Type: AWS::RDS::DBInstance + # There is no guarantee that this instance will be the read replica, since + # RDS decides automatically + CmsReadReplicaDbInstance: + Properties: + DBClusterIdentifier: !Ref CmsDatabaseCluster + DBInstanceClass: !Ref 'cmsDbInstanceSize' + DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' + DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' + Engine: aurora + PubliclyAccessible: 'false' Type: AWS::RDS::DBInstance CmsDatabaseParamGroup: Properties: Description: Default parameters for the cms DB - Family: mysql5.6 + Family: aurora5.6 Parameters: log_output: TABLE slow_query_log: 1 diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 10a1ee25..3ae5bd5e 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -100,11 +100,9 @@ Resources: Value: true - Key: access_logs.s3.bucket Value: !Ref 'ALBLogBucket' - - Key: access_logs.s3.prefix - Value: environment Scheme: internet-facing SecurityGroups: - - Fn::ImportValue: !Sub "${sgStackName}-appLoadBalancerSgId" + - Fn::ImportValue: !Sub "${sgStackName}-loadBalancerSgId" Subnets: - Ref: 'vpcSubnetIdForAz1' - Ref: 'vpcSubnetIdForAz2' diff --git a/src/main/resources/cloudformation/route53.yaml b/src/main/resources/cloudformation/route53.yaml index 426bd325..69354c4a 100644 --- a/src/main/resources/cloudformation/route53.yaml +++ b/src/main/resources/cloudformation/route53.yaml @@ -6,7 +6,7 @@ Outputs: recordSet: Value: !Ref 'CerberusPublicRecordSet' Parameters: - appLoadBalancerStackName: + loadBalancerStackName: Description: The name of the Cerberus load balancer CloudFormation stack Type: String hostname: @@ -22,7 +22,7 @@ Resources: Ref: hostedZoneId Name: !Join [., [!Ref 'hostname', '']] ResourceRecords: - - Fn::ImportValue: !Sub "${appLoadBalancerStackName}-appLoadBalancerDnsName" + - Fn::ImportValue: !Sub "${loadBalancerStackName}-appLoadBalancerDnsName" TTL: 30 Type: CNAME Type: AWS::Route53::RecordSet \ No newline at end of file diff --git a/src/main/resources/cloudformation/vpc.yaml b/src/main/resources/cloudformation/vpc.yaml index 814a04a2..7dd56fab 100644 --- a/src/main/resources/cloudformation/vpc.yaml +++ b/src/main/resources/cloudformation/vpc.yaml @@ -5,13 +5,22 @@ Description: Creates the Cerberus VPC and foundational resources Outputs: subnetCidrBlockForAz1: Description: Cidr block for subnet in AZ '1' - Value: 172.20.0.0/24 + Value: !Ref 'subnetCidrBlockForAz1' subnetCidrBlockForAz2: Description: Cidr block for subnet in AZ '2' - Value: 172.20.4.0/24 + Value: !Ref 'subnetCidrBlockForAz2' subnetCidrBlockForAz3: Description: Cidr block for subnet in AZ '3' - Value: 172.20.8.0/24 + Value: !Ref 'subnetCidrBlockForAz3' + vpcSubnetForAz1: + Description: The VPC subnet in AZ '1' + Value: !Ref 'CerberusSubnetForAz1' + vpcSubnetForAz2: + Description: The VPC subnet in AZ '2' + Value: !Ref 'CerberusSubnetForAz2' + vpcSubnetForAz3: + Description: The VPC subnet in AZ '3' + Value: !Ref 'CerberusSubnetForAz3' Parameters: internetGatewayCidrBlock: AllowedPattern: '[0-9./]*' diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml index e3fca6c7..29b66680 100644 --- a/src/main/resources/cloudformation/web-app-firewall.yaml +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -10,7 +10,7 @@ Outputs: whiteListIPSetID: Value: !Ref 'WAFWhiteListSet' Parameters: - appLoadBalancerStackName: + loadBalancerStackName: Description: The name of the Cerberus load balancer CloudFormation stack Type: String tagClassification: @@ -67,7 +67,7 @@ Resources: Type: "AWS::WAFRegional::WebACLAssociation" Properties: ResourceArn: - Fn::ImportValue: !Sub "${appLoadBalancerStackName}-appLoadBalancerLogicalId" + Fn::ImportValue: !Sub "${loadBalancerStackName}-appLoadBalancerLogicalId" WebACLId: !Ref 'CerberusWAFWebAcl' CerberusWafSizeConstraintRule: From c6d388f2e7d1f09632c03aba9fb3d58fd737dc41 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 3 Nov 2017 10:33:01 -0700 Subject: [PATCH 07/69] Remove vault, consul, gateway, and dashoard (#69) * Remove gateway and lambda code * Remove vault and consul code * Remove dashboard code * Remove unneeded config constants * Keep gateway bits needed for backup --- smaas-cf/smaas/gateway-cluster.py | 817 ------------------ .../com/nike/cerberus/ConfigConstants.java | 40 - .../com/nike/cerberus/cli/CerberusRunner.java | 38 - .../cli/EnvironmentConfigToArgsMapper.java | 153 ---- .../consul/CreateConsulClusterCommand.java | 63 -- .../consul/CreateConsulConfigCommand.java | 44 - .../command/consul/CreateVaultAclCommand.java | 43 - .../consul/UpdateConsulConfigCommand.java | 41 - .../dashboard/PublishDashboardCommand.java | 62 -- ...FrontLogProcessingLambdaConfigCommand.java | 84 -- ...rontSecurityGroupUpdaterLambdaCommand.java | 44 - .../gateway/CreateGatewayClusterCommand.java | 83 -- .../gateway/CreateGatewayConfigCommand.java | 53 -- .../command/gateway/PublishLambdaCommand.java | 63 -- .../UploadCertFilesStackNameValidator.java | 3 +- .../vault/CreateCmsVaultTokenCommand.java | 57 -- .../vault/CreateVaultClusterCommand.java | 63 -- .../vault/CreateVaultConfigCommand.java | 43 - .../vault/DisableAuditBackendCommand.java | 52 -- .../vault/EnableAuditBackendCommand.java | 63 -- .../vault/InitVaultClusterCommand.java | 43 - .../LoadDefaultVaultPoliciesCommand.java | 43 - .../vault/UnsealVaultClusterCommand.java | 43 - .../vault/VaultHealthCheckCommand.java | 52 -- .../CloudFrontIpSynchronizerOutputs.java | 31 - .../CloudFrontIpSynchronizerParameters.java | 42 - .../domain/cloudformation/ConsulOutputs.java | 45 - .../cloudformation/ConsulParameters.java | 147 ---- .../domain/cloudformation/GatewayOutputs.java | 177 ---- .../cloudformation/LambdaInputParameters.java | 121 --- .../cloudformation/VaultParameters.java | 207 ----- .../configuration/ConsulConfiguration.java | 58 -- .../configuration/GatewayConfiguration.java | 45 - .../configuration/VaultConfiguration.java | 33 - .../CloudFrontLogProcessingLambdaConfig.java | 100 --- .../domain/environment/ConsulSecrets.java | 56 -- .../domain/environment/Environment.java | 3 - .../domain/environment/LambdaName.java | 52 -- .../cerberus/domain/environment/Secrets.java | 11 - .../domain/environment/StackName.java | 6 +- .../nike/cerberus/domain/input/Consul.java | 23 - .../nike/cerberus/domain/input/Dashboard.java | 42 - .../cerberus/domain/input/EdgeSecurity.java | 88 -- .../domain/input/EnvironmentConfig.java | 27 - .../nike/cerberus/domain/input/Gateway.java | 16 - .../template/ConsulConfigurationInput.java | 56 -- .../template/GatewayConfigurationInput.java | 67 -- .../domain/template/VaultAclInput.java | 34 - .../template/VaultConfigurationInput.java | 45 - .../cerberus/domain/vault/AuditBackend.java | 35 - .../generator/ConfigGenerationException.java | 26 - .../generator/ConsulConfigGenerator.java | 92 -- .../generator/GatewayConfigGenerator.java | 65 -- .../cerberus/generator/VaultAclGenerator.java | 66 -- .../generator/VaultConfigGenerator.java | 57 -- .../consul/CreateConsulClusterOperation.java | 167 ---- .../consul/CreateConsulConfigOperation.java | 72 -- .../consul/CreateVaultAclOperation.java | 69 -- .../consul/UpdateConsulConfigOperation.java | 85 -- ...RollingRebootWithHealthCheckOperation.java | 6 +- .../operation/core/UpdateStackOperation.java | 10 - .../dashboard/DashboardMetaDataProvider.java | 40 - .../dashboard/PublishDashboardOperation.java | 160 ---- ...ontLogProcessingLambdaConfigOperation.java | 84 -- ...ntSecurityGroupUpdaterLambdaOperation.java | 190 ---- .../CreateGatewayClusterOperation.java | 184 ---- .../gateway/CreateGatewayConfigOperation.java | 95 -- .../gateway/PublishLambdaOperation.java | 126 --- .../vault/CreateCmsVaultTokenOperation.java | 106 --- .../vault/CreateVaultClusterOperation.java | 182 ---- .../vault/CreateVaultConfigOperation.java | 76 -- .../vault/DisableAuditBackendOperation.java | 61 -- .../vault/EnableAuditBackendOperation.java | 102 --- .../vault/InitVaultClusterOperation.java | 74 -- .../LoadDefaultVaultPoliciesOperation.java | 83 -- .../vault/UnsealVaultClusterOperation.java | 68 -- .../vault/VaultHealthCheckOperation.java | 102 --- .../cerberus/service/AmiTagCheckService.java | 4 - .../cerberus/service/Ec2UserDataService.java | 51 -- .../com/nike/cerberus/store/ConfigStore.java | 230 ----- src/main/resources/example-standup.yaml | 53 +- .../templates/consul-client.json.mustache | 33 - .../templates/consul-server.json.mustache | 39 - .../resources/templates/nginx.conf.mustache | 69 -- .../templates/site-gateway.conf.mustache | 76 -- .../templates/vault-acl.json.mustache | 6 - .../resources/templates/vault.json.mustache | 24 - .../resources/vault/default-policies.json | 5 - .../EnvironmentConfigToArgsMapperTest.java | 173 +--- .../generator/VaultAclGeneratorTest.java | 61 -- src/test/resources/environment.yaml | 50 +- 91 files changed, 13 insertions(+), 6966 deletions(-) delete mode 100644 smaas-cf/smaas/gateway-cluster.py delete mode 100644 src/main/java/com/nike/cerberus/command/consul/CreateConsulClusterCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/consul/CreateConsulConfigCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/consul/CreateVaultAclCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/consul/UpdateConsulConfigCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/dashboard/PublishDashboardCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontLogProcessingLambdaConfigCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/gateway/CreateGatewayClusterCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/gateway/CreateGatewayConfigCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/gateway/PublishLambdaCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/CreateCmsVaultTokenCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/CreateVaultClusterCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/CreateVaultConfigCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/DisableAuditBackendCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/EnableAuditBackendCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/InitVaultClusterCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/LoadDefaultVaultPoliciesCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/UnsealVaultClusterCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/vault/VaultHealthCheckCommand.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerOutputs.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerParameters.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/ConsulOutputs.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/ConsulParameters.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/GatewayOutputs.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/LambdaInputParameters.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/VaultParameters.java delete mode 100644 src/main/java/com/nike/cerberus/domain/configuration/ConsulConfiguration.java delete mode 100644 src/main/java/com/nike/cerberus/domain/configuration/GatewayConfiguration.java delete mode 100644 src/main/java/com/nike/cerberus/domain/configuration/VaultConfiguration.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/CloudFrontLogProcessingLambdaConfig.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/ConsulSecrets.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/LambdaName.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/Consul.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/Dashboard.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/EdgeSecurity.java delete mode 100644 src/main/java/com/nike/cerberus/domain/template/ConsulConfigurationInput.java delete mode 100644 src/main/java/com/nike/cerberus/domain/template/GatewayConfigurationInput.java delete mode 100644 src/main/java/com/nike/cerberus/domain/template/VaultAclInput.java delete mode 100644 src/main/java/com/nike/cerberus/domain/template/VaultConfigurationInput.java delete mode 100644 src/main/java/com/nike/cerberus/domain/vault/AuditBackend.java delete mode 100644 src/main/java/com/nike/cerberus/generator/ConfigGenerationException.java delete mode 100644 src/main/java/com/nike/cerberus/generator/ConsulConfigGenerator.java delete mode 100644 src/main/java/com/nike/cerberus/generator/GatewayConfigGenerator.java delete mode 100644 src/main/java/com/nike/cerberus/generator/VaultAclGenerator.java delete mode 100644 src/main/java/com/nike/cerberus/generator/VaultConfigGenerator.java delete mode 100644 src/main/java/com/nike/cerberus/operation/consul/CreateConsulClusterOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/consul/CreateConsulConfigOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/consul/CreateVaultAclOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/consul/UpdateConsulConfigOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/dashboard/DashboardMetaDataProvider.java delete mode 100644 src/main/java/com/nike/cerberus/operation/dashboard/PublishDashboardOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontLogProcessingLambdaConfigOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayClusterOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayConfigOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/gateway/PublishLambdaOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/CreateCmsVaultTokenOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/CreateVaultClusterOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/CreateVaultConfigOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/DisableAuditBackendOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/EnableAuditBackendOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/InitVaultClusterOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/LoadDefaultVaultPoliciesOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/UnsealVaultClusterOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/vault/VaultHealthCheckOperation.java delete mode 100644 src/main/resources/templates/consul-client.json.mustache delete mode 100644 src/main/resources/templates/consul-server.json.mustache delete mode 100644 src/main/resources/templates/nginx.conf.mustache delete mode 100644 src/main/resources/templates/site-gateway.conf.mustache delete mode 100644 src/main/resources/templates/vault-acl.json.mustache delete mode 100644 src/main/resources/templates/vault.json.mustache delete mode 100644 src/main/resources/vault/default-policies.json delete mode 100644 src/test/java/com/nike/cerberus/generator/VaultAclGeneratorTest.java diff --git a/smaas-cf/smaas/gateway-cluster.py b/smaas-cf/smaas/gateway-cluster.py deleted file mode 100644 index 7080a02b..00000000 --- a/smaas-cf/smaas/gateway-cluster.py +++ /dev/null @@ -1,817 +0,0 @@ -### -# Copyright (c) 2017 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. -### - -from io import open -from troposphere import Ref, Template, Parameter, Output, GetAtt, Join, AWS_ACCOUNT_ID -from troposphere.autoscaling import AutoScalingGroup, LaunchConfiguration -from troposphere.cloudfront import Distribution, DefaultCacheBehavior, ForwardedValues, DistributionConfig, Origin, \ - CustomOrigin, ViewerCertificate, Logging -from troposphere.elasticloadbalancing import LoadBalancer, ConnectionDrainingPolicy, ConnectionSettings, HealthCheck, \ - Listener, Policy -from troposphere.policies import UpdatePolicy, AutoScalingRollingUpdate -from troposphere.route53 import RecordSetType -from troposphere.waf import WebACL, Action, Rule, SizeConstraintSet, SizeConstraint, FieldToMatch, Predicates, Rules, \ - IPSet, SqlInjectionMatchSet, SqlInjectionMatchTuples, XssMatchSet, XssMatchTuple -from troposphere.s3 import Bucket, BucketOwnerFullControl, NotificationConfiguration, LambdaConfigurations, Filter, \ - S3Key, Rules as S3Rules, BucketPolicy -from troposphere.awslambda import Function, Code, Permission - -from smaas.support.tags import CerberusTags - -CF_FILE = '../../src/main/resources/cloudformation/gateway-cluster.json' -CLOUD_FRONT_PREFIX = 'cf-logs/' - -# Create Template -template = Template() -template.description = "Launches the gateway cluster in the Cerberus VPC" -template.version = '2010-09-09' - -### -# -# Inputs -# -### - -cerberus_tags = CerberusTags() -cerberus_tags.add_tag_parameters(template) - -cert_public_key_param = template.add_parameter(Parameter( - "certPublicKey", - Description="TLS certificate public key to be used for backend authentication", - Type="String" -)) - -ssl_certificate_arn_param = template.add_parameter(Parameter( - "sslCertificateArn", - Description="TLS certificate ARN for the ELB", - Type="String" -)) - -ssl_certificate_id_param = template.add_parameter(Parameter( - "sslCertificateId", - Description="TLS certificate ID for the CloudFront distribution and ELB", - Type="String" -)) - -vpc_id_param = template.add_parameter(Parameter( - "vpcId", - Description="The ID of the VPC", - Type="AWS::EC2::VPC::Id" -)) - -instance_profile_name_param = template.add_parameter(Parameter( - "instanceProfileName", - Description="The name for the instance profile", - Type="String" -)) - -ami_id_param = template.add_parameter(Parameter( - "amiId", - Description="The AMI ID for the instances", - Type="String" -)) - -instance_size_param = template.add_parameter(Parameter( - "instanceSize", - Description="The instance size", - Type="String" -)) - -gateway_elb_sg_id_param = template.add_parameter(Parameter( - "gatewayElbSgId", - Description="Gateway Server ELB Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -gateway_server_sg_id_param = template.add_parameter(Parameter( - "gatewayServerSgId", - Description="Gateway Server Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -tools_ingress_sg_id_param = template.add_parameter(Parameter( - "toolsIngressSgId", - Description="Tools Ingress Security Group ID", - Type="AWS::EC2::SecurityGroup::Id" -)) - -cloud_front_log_processor_lambda_iam_role_param = template.add_parameter(Parameter( - "cloudFrontLogProcessorLambdaIamRoleArn", - Description="The IAM Role Arn for the lambda", - Type="String" -)) - -subnet_id_refs = [] -for zone_identifier in range(1, 4): - vpc_subnet_id = template.add_parameter(Parameter( - "vpcSubnetIdForAz{0}".format(zone_identifier), - Description="VPC Subnet ID for Zone {0}".format(zone_identifier), - Type="String" - )) - - subnet_id_refs.append(Ref(vpc_subnet_id)) - -key_pair_name_param = template.add_parameter(Parameter( - "keyPairName", - Description="The key pair to be associated with the EC2 instances", - Type="String", - Default="cpe-cerberus" -)) - -gateway_user_data_param = template.add_parameter(Parameter( - "userData", - Description="Gateway User Data", - Type="String" -)) - -hosted_zone_id_param = template.add_parameter(Parameter( - "hostedZoneId", - Description="The hosted zone id to associate the CNAME record with", - Type="String" -)) - -hostname_param = template.add_parameter(Parameter( - "hostname", - Description="The base hostname for the public facing gateway / router", - Type="String" -)) - -waf_lambda_bucket = template.add_parameter(Parameter( - "wafLambdaBucket", - Type="String", - Description="S3 Bucket for waf lambda function artifact" -)) - -waf_lambda_key = template.add_parameter(Parameter( - "wafLambdaKey", - Type="String", - Description="Key for waf lambda function artifact" -)) - -desired_instances_param = template.add_parameter(Parameter( - "desiredInstances", - Description="Desired Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -maximum_instances_param = template.add_parameter(Parameter( - "maximumInstances", - Description="Maximum Number of Auto Scaling Instances (must be larger than min)", - Type="Number", - Default="4" -)) - -minimum_instances_param = template.add_parameter(Parameter( - "minimumInstances", - Description="Minimum Number of Auto Scaling Instances", - Type="Number", - Default="3" -)) - -pause_time_param = template.add_parameter(Parameter( - "pauseTime", - Description="Pause time for AutoScalingRollingUpdate e.g PT15M", - Type="String", - Default="PT15M" -)) - -wait_on_resource_signals_param = template.add_parameter(Parameter( - "waitOnResourceSignals", - Description="Enabling WaitOnResourceSignals allows CloudFormation to wait until you have received a success signal before performing the next scaling action.", - Type="String", - Default="True" -)) - -### -# -# Elastic Load Balancer -# -### - -gateway_load_balancer = template.add_resource(LoadBalancer( - "GatewayElasticLoadBalancer", - ConnectionDrainingPolicy=ConnectionDrainingPolicy( - Enabled=True, - Timeout=10 - ), - ConnectionSettings=ConnectionSettings( - IdleTimeout=10 - ), - CrossZone=True, - HealthCheck=HealthCheck( - HealthyThreshold=2, - Interval=5, - Target="HTTPS:443/sys/health", - Timeout=2, - UnhealthyThreshold=2 - ), - Listeners=[ - Listener( - InstancePort=443, - InstanceProtocol="HTTPS", - LoadBalancerPort=443, - PolicyNames=[ - "GatewayTLSNegotiationPolicy" - ], - Protocol="HTTPS", - SSLCertificateId=Ref(ssl_certificate_arn_param) - ) - ], - Policies=[ - Policy( - PolicyName="GatewayTLSNegotiationPolicy", - PolicyType="SSLNegotiationPolicyType", - Attributes=[ - { - "Name": "Reference-Security-Policy", - "Value": "ELBSecurityPolicy-TLS-1-2-2017-01" - } - ] - ), - Policy( - PolicyName="GatewayPublicKeyPolicy", - PolicyType="PublicKeyPolicyType", - Attributes=[ - { - "Name": "PublicKey", - "Value": Ref(cert_public_key_param) - } - ] - ), - Policy( - PolicyName="GatewayBackendServerAuthenticationPolicy", - PolicyType="BackendServerAuthenticationPolicyType", - Attributes=[ - { - "Name": "PublicKeyPolicyName", - "Value": "GatewayPublicKeyPolicy" - } - ], - InstancePorts=[ - "443" - ] - ) - ], - Scheme="internet-facing", - SecurityGroups=[ - Ref(gateway_elb_sg_id_param) - ], - Subnets=subnet_id_refs, - Tags=cerberus_tags.get_tags_as_list() -)) - -### -# -# Launch Configuration and Auto Scaling Group for Gateway -# -### - -gateway_launch_config = template.add_resource(LaunchConfiguration( - "GatewayLaunchConfiguration", - AssociatePublicIpAddress=True, - IamInstanceProfile=Ref(instance_profile_name_param), - ImageId=Ref(ami_id_param), - InstanceMonitoring=True, - InstanceType=Ref(instance_size_param), - KeyName=Ref(key_pair_name_param), - SecurityGroups=[ - Ref(tools_ingress_sg_id_param), - Ref(gateway_server_sg_id_param), - ], - UserData=Ref(gateway_user_data_param) -)) - -gateway_autoscaling_group = template.add_resource(AutoScalingGroup( - "GatewayAutoScalingGroup", - DesiredCapacity=Ref(desired_instances_param), - HealthCheckGracePeriod=300, - HealthCheckType="ELB", - LaunchConfigurationName=Ref(gateway_launch_config), - LoadBalancerNames=[ - Ref(gateway_load_balancer) - ], - MaxSize=Ref(maximum_instances_param), - MinSize=Ref(minimum_instances_param), - UpdatePolicy=UpdatePolicy( - AutoScalingRollingUpdate=AutoScalingRollingUpdate( - MaxBatchSize=1, - MinInstancesInService=Ref(minimum_instances_param), - PauseTime=Ref(pause_time_param), - WaitOnResourceSignals=Ref(wait_on_resource_signals_param) - ) - ), - VPCZoneIdentifier=subnet_id_refs, - Tags=cerberus_tags.get_autoscaling_tags_as_list() -)) - -### -# -# Record Set for public Cerberus CNAME -# -### - -gateway_record_set = template.add_resource(RecordSetType( - "OriginCerberusPublicRecordSet", - HostedZoneId=Ref(hosted_zone_id_param), - Name=Join(".", ["origin", Ref(hostname_param), ""]), - TTL=30, - Type="CNAME", - ResourceRecords=[GetAtt(gateway_load_balancer, "CanonicalHostedZoneName")] -)) - -### -# -# WAF Web ACL -# -### - -xss_match_set = template.add_resource(XssMatchSet( - "CerberusWafXssMatchSet", - Name="CerberusWafXssMatchSet", - XssMatchTuples=[ - XssMatchTuple( - "CerberusWafXssUriMatch", - FieldToMatch=FieldToMatch( - Type="URI" - ), - TextTransformation="NONE" - ), - XssMatchTuple( - "CerberusWafXssQueryStringMatch", - FieldToMatch=FieldToMatch( - Type="QUERY_STRING" - ), - TextTransformation="NONE" - ), - XssMatchTuple( - "CerberusWafXssBodyMatch", - FieldToMatch=FieldToMatch( - Type="BODY" - ), - TextTransformation="NONE" - ) - ] -)) - -xss_rule = template.add_resource(Rule( - "CerberusWafXssRule", - Name="CerberusWafXssRule", - MetricName="CerberusWafXss", - Predicates=[ - Predicates( - DataId=Ref(xss_match_set), - Negated=False, - Type="XssMatch" - ) - ] -)) - -sql_injection_match_set = template.add_resource(SqlInjectionMatchSet( - "CerberusWafSqlInjectionMatchSet", - Name="CerberusWafSqlInjectionMatchSet", - SqlInjectionMatchTuples=[ - SqlInjectionMatchTuples( - "CerberusWafSqlInjectionUriMatch", - FieldToMatch=FieldToMatch( - Type="URI" - ), - TextTransformation="NONE" - ), - SqlInjectionMatchTuples( - "CerberusWafSqlInjectionQueryStringMatch", - FieldToMatch=FieldToMatch( - Type="QUERY_STRING" - ), - TextTransformation="NONE" - ), - SqlInjectionMatchTuples( - "CerberusWafSqlInjectionBodyMatch", - FieldToMatch=FieldToMatch( - Type="BODY" - ), - TextTransformation="NONE" - ) - ] -)) - -sql_injection_rule = template.add_resource(Rule( - "CerberusWafSqlInjectionRule", - Name="CerberusWafSqlInjectionRule", - MetricName="CerberusWafSqlInjection", - Predicates=[ - Predicates( - DataId=Ref(sql_injection_match_set), - Negated=False, - Type="SqlInjectionMatch" - ) - ] -)) - -size_constraint_set = template.add_resource(SizeConstraintSet( - "CerberusWafSizeConstraintSet", - Name="CerberusWafSizeConstraintSet", - SizeConstraints=[ - SizeConstraint( - "CerberusWafSizeConstraint", - ComparisonOperator="GE", - FieldToMatch=FieldToMatch( - Type="BODY" - ), - Size=256000, - TextTransformation="NONE" - ) - ] -)) - -size_constraint_rule = template.add_resource(Rule( - "CerberusWafSizeConstraintRule", - Name="CerberusWafSizeConstraintRule", - MetricName="CerberusWafSizeConstraint", - Predicates=[ - Predicates( - DataId=Ref(size_constraint_set), - Negated=False, - Type="SizeConstraint" - ) - ] -)) - -waf_white_list_set = template.add_resource(IPSet( - "WAFWhiteListSet", - Name="White List Set" -)) - -waf_manual_block_set = template.add_resource(IPSet( - "WAFManualBlockSet", - Name="Manual Block Set" -)) - -waf_auto_block_set = template.add_resource(IPSet( - "WAFAutoBlockSet", - Name="Auto Block Set" -)) - -waf_white_list_rule = template.add_resource(Rule( - "WAFWhiteListRule", - DependsOn="WAFWhiteListSet", - Name="White List Rule", - MetricName="WhiteListRule", - Predicates=[ - Predicates( - DataId=Ref(waf_white_list_set), - Negated=False, - Type="IPMatch" - ) - ] -)) - -waf_manual_block_rule = template.add_resource(Rule( - "WAFManualBlockRule", - DependsOn="WAFManualBlockSet", - Name="Manual Block Rule", - MetricName="ManualBlockRule", - Predicates=[ - Predicates( - DataId=Ref(waf_manual_block_set), - Negated=False, - Type="IPMatch" - ) - ] -)) - -waf_auto_block_rule = template.add_resource(Rule( - "WAFAutoBlockRule", - DependsOn="WAFAutoBlockSet", - Name="Auto Block Rule", - MetricName="AutoBlockRule", - Predicates=[ - Predicates( - DataId=Ref(waf_auto_block_set), - Negated=False, - Type="IPMatch" - ) - ] -)) - -web_acl = template.add_resource(WebACL( - "CerberusWAFWebAcl", - DependsOn=["WAFManualBlockRule", "WAFAutoBlockRule"], - MetricName="CerberusWAF", - DefaultAction=Action( - Type="ALLOW" - ), - Name=Join(".", ["waf", Ref(hostname_param)]), - Rules=[ - Rules( - Action=Action( - Type="BLOCK" - ), - Priority=1, - RuleId=Ref(size_constraint_rule) - ), - Rules( - Action=Action( - Type="BLOCK" - ), - Priority=2, - RuleId=Ref(sql_injection_rule) - ), - Rules( - Action=Action( - Type="BLOCK" - ), - Priority=3, - RuleId=Ref(xss_rule) - ), - Rules( - Action=Action( - Type="ALLOW" - ), - Priority=4, - RuleId=Ref(waf_white_list_rule) - ), - Rules( - Action=Action( - Type="BLOCK" - ), - Priority=5, - RuleId=Ref(waf_manual_block_rule) - ), - Rules( - Action=Action( - Type="BLOCK" - ), - Priority=6, - RuleId=Ref(waf_auto_block_rule) - ) - ] -)) - -### -# -# WAF Lambda Function -# -### - -lambda_waf_blacklisting_function = template.add_resource(Function( - "LambdaWAFBlacklistingFunction", - Description="Function for auto black listing ips that are misbehaving", - Handler="com.nike.cerberus.lambda.waf.handler.CloudFrontLogEventHandler::handleNewS3Event", - Role=Ref(cloud_front_log_processor_lambda_iam_role_param), - Code=Code( - S3Bucket=Ref(waf_lambda_bucket), - S3Key=Ref(waf_lambda_key) - ), - Runtime="java8", - MemorySize="512", - Timeout="60" -)) - -lambda_invoke_permission = template.add_resource(Permission( - "LambdaInvokePermission", - DependsOn="LambdaWAFBlacklistingFunction", - FunctionName=GetAtt(lambda_waf_blacklisting_function, "Arn"), - Action="lambda:*", - Principal="s3.amazonaws.com", - SourceAccount=Ref(AWS_ACCOUNT_ID) -)) - -### -# -# Bucket for Cloud Front Logs -# -### - -cloud_front_logs_bucket = template.add_resource(Bucket( - "CloudFrontBucket", - AccessControl=BucketOwnerFullControl, - Tags=cerberus_tags.get_tags(), - NotificationConfiguration=NotificationConfiguration( - LambdaConfigurations=[LambdaConfigurations( - Event="s3:ObjectCreated:*", - Filter=Filter( - S3Key=S3Key( - Rules=[S3Rules( - Name="suffix", - Value="gz" - )] - ) - ), - Function=GetAtt(lambda_waf_blacklisting_function, "Arn") - )] - ) -)) - -cloud_front_logs_bucket_policy = template.add_resource(BucketPolicy( - "CloudFrontLogsBucketPolicy", - Bucket=Ref(cloud_front_logs_bucket), - PolicyDocument={ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "Allow-CloudFront-Log-Access", - "Effect": "Allow", - "Principal": { - "AWS": [ - Ref(cloud_front_log_processor_lambda_iam_role_param) - ] - }, - "Action": [ - "s3:*" - ], - "Resource": [ - { - "Fn::Join": [ - "", - [ - "arn:aws:s3:::", - Ref(cloud_front_logs_bucket), - "/*" - ] - ] - } - ] - } - ] - } - -)) - -### -# -# Cloud Front for Web Application Firewall -# -### - -gateway_distribution = template.add_resource(Distribution( - "CerberusDistribution", - DistributionConfig=DistributionConfig( - Aliases=[ - Ref(hostname_param) - ], - DefaultCacheBehavior=DefaultCacheBehavior( - AllowedMethods=[ - "GET", - "PUT", - "POST", - "DELETE", - "PATCH", - "HEAD", - "OPTIONS" - ], - CachedMethods=[ - "GET", - "HEAD", - "OPTIONS" - ], - ForwardedValues=ForwardedValues( - Headers=[ - "*" - ], - QueryString=True - ), - MaxTTL=0, - MinTTL=0, - DefaultTTL=0, - TargetOriginId="CerberusGatewayOrigin", - ViewerProtocolPolicy="https-only" - ), - Enabled=True, - Origins=[ - Origin( - Id="CerberusGatewayOrigin", - DomainName=Join(".", ["origin", Ref(hostname_param)]), - CustomOriginConfig=CustomOrigin( - HTTPSPort="443", - OriginProtocolPolicy="https-only", - OriginSSLProtocols=[ - "TLSv1.2" - ] - ) - ) - ], - PriceClass="PriceClass_100", - ViewerCertificate=ViewerCertificate( - IamCertificateId=Ref(ssl_certificate_id_param), - MinimumProtocolVersion="TLSv1", - SslSupportMethod="sni-only" - ), - WebACLId=Ref(web_acl), - Logging=Logging( - Bucket=GetAtt(cloud_front_logs_bucket, "DomainName") - ) - ) -)) - -### -# -# Record Set for public Cerberus Cloud Front CNAME -# -### - -cf_gateway_record_set = template.add_resource(RecordSetType( - "CerberusPublicRecordSet", - HostedZoneId=Ref(hosted_zone_id_param), - Name=Join(".", [Ref(hostname_param), ""]), - TTL=30, - Type="CNAME", - ResourceRecords=[GetAtt(gateway_distribution, "DomainName")] -)) - -### -# -# Outputs -# -### - -template.add_output(Output( - "autoscalingGroupLogicalId", - Value=Ref(gateway_autoscaling_group) -)) - -template.add_output(Output( - "launchConfigurationLogicalId", - Value=Ref(gateway_launch_config) -)) - -template.add_output(Output( - "elbLogicalId", - Value=Ref(gateway_load_balancer) -)) - -template.add_output(Output( - "elbCanonicalHostedZoneNameId", - Value=GetAtt(gateway_load_balancer, "CanonicalHostedZoneNameID") -)) - -template.add_output(Output( - "elbDnsName", - Value=GetAtt(gateway_load_balancer, "DNSName") -)) - -template.add_output(Output( - "elbSourceSecurityGroupName", - Value=GetAtt(gateway_load_balancer, "SourceSecurityGroup.GroupName") -)) - -template.add_output(Output( - "elbSourceSecurityGroupOwnerAlias", - Value=GetAtt(gateway_load_balancer, "SourceSecurityGroup.OwnerAlias") -)) - -template.add_output(Output( - "cloudFrontDistributionId", - Value=Ref(gateway_distribution), -)) - -template.add_output(Output( - "cloudFrontDistributionDomainName", - Value=GetAtt(gateway_distribution, "DomainName") -)) - -template.add_output(Output( - "cloudFrontAccessLogBucket", - Value=Ref(cloud_front_logs_bucket) -)) - -template.add_output(Output( - "whiteListIPSetID", - Value=Ref(waf_white_list_set) -)) - -template.add_output(Output( - "manualBlockIPSetID", - Value=Ref(waf_manual_block_set) -)) - -template.add_output(Output( - "autoBlockIPSetID", - Value=Ref(waf_auto_block_set) -)) - -### -# -# Print! -# -### - -cf_file = open(CF_FILE, 'w') -cf_file.truncate() -cf_file.write(template.to_json(indent=0)) -cf_file.close() - -print("CloudFormation template written to: {0}".format(CF_FILE)) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 415365e7..aa818a19 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -32,8 +32,6 @@ public class ConfigConstants { public static final String ENV_DATA_FILE = "config/environment.json"; - public static final String CF_LOG_PROCESSOR_LAMBDA_CONFIG_FILE = "data/cloud-front-log-processor/lambda-config.json"; - public static final String SECRETS_DATA_FILE = "config/secrets.json"; public static final String CERT_PART_CA = "ca.pem"; @@ -50,44 +48,14 @@ public class ConfigConstants { public static final String BASE_STACK_TEMPLATE_PATH = "/cloudformation/vpc-and-base.json"; - public static final String CONSUL_DATACENTER = "cerberus"; - - public static final String CONSUL_SERVER_CONFIG_FILE = "data/consul/consul-server-config.json"; - - public static final String CONSUL_CLIENT_CONFIG_FILE = "data/consul/consul-client-config.json"; - - public static final String CONSUL_STACK_TEMPLATE_PATH = "/cloudformation/consul-cluster.json"; - - public static final String VAULT_ACL_ENTRY_FILE = "data/consul/vault-acl.json"; - - public static final String VAULT_CONFIG_FILE = "data/vault/vault-config.json"; - - public static final int VAULT_SECRET_SHARES = 5; - - public static final int VAULT_SECRET_THRESHOLD = 3; - - public static final String VAULT_STACK_TEMPLATE_PATH = "/cloudformation/vault-cluster.json"; - public static final String CMS_STACK_TEMPLATE_PATH = "/cloudformation/cms-cluster.json"; - public static final String GATEWAY_STACK_TEMPLATE_PATH = "/cloudformation/gateway-cluster.json"; - - public static final String GATEWAY_SITE_CONFIG_FILE = "data/gateway/gateway.conf"; - - public static final String GATEWAY_GLOBAL_CONFIG_FILE = "data/gateway/nginx.conf"; - public static final String CMS_ENV_CONFIG_PATH = "data/cms/environment.properties"; - public static final String CF_ELB_IP_SYNC_STACK_TEMPLATE_PATH = "/cloudformation/cloudfront-elb-security-group-updater-lambda.json"; - public static final String VERSION_PROPERTY = "cli.version"; public static final String CMS_ADMIN_GROUP_KEY = "cms.admin.group"; - public static final String VAULT_ADDR_KEY = "vault.addr"; - - public static final String VAULT_TOKEN_KEY = "vault.token"; - public static final String ROOT_USER_ARN_KEY = "root.user.arn"; public static final String ADMIN_ROLE_ARN_KEY = "admin.role.arn"; @@ -105,8 +73,6 @@ public class ConfigConstants { public static final String HASH_SALT = "auth.token.hashSalt"; public static final ImmutableSet SYSTEM_CONFIGURED_CMS_PROPERTIES = ImmutableSet.of( - VAULT_ADDR_KEY, - VAULT_TOKEN_KEY, ROOT_USER_ARN_KEY, ADMIN_ROLE_ARN_KEY, CMS_ROLE_ARN_KEY, @@ -120,12 +86,6 @@ public class ConfigConstants { public static final String CMS_AMI_TAG_VALUE = "cms"; - public static final String GATEWAY_AMI_TAG_VALUE = "gateway"; - - public static final String CONSUL_AMI_TAG_VALUE = "consul"; - - public static final String VAULT_AMI_TAG_VALUE = "vault"; - public static final String SKIP_AMI_TAG_CHECK_ARG = "--skip-ami-tag-check"; public static final String SKIP_AMI_TAG_CHECK_DESCRIPTION = "Skip validation of 'cerberus_component' tag on AMI"; diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 075a0e6f..7974d5ef 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -34,10 +34,6 @@ import com.nike.cerberus.command.core.CreateCerberusBackupCommand; import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; import com.nike.cerberus.command.core.ViewConfigCommand; -import com.nike.cerberus.command.consul.CreateConsulClusterCommand; -import com.nike.cerberus.command.consul.CreateConsulConfigCommand; -import com.nike.cerberus.command.consul.CreateVaultAclCommand; -import com.nike.cerberus.command.consul.UpdateConsulConfigCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; import com.nike.cerberus.command.core.RestoreCerberusBackupCommand; @@ -45,21 +41,6 @@ import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.command.dashboard.PublishDashboardCommand; -import com.nike.cerberus.command.gateway.CreateCloudFrontLogProcessingLambdaConfigCommand; -import com.nike.cerberus.command.gateway.CreateCloudFrontSecurityGroupUpdaterLambdaCommand; -import com.nike.cerberus.command.gateway.CreateGatewayClusterCommand; -import com.nike.cerberus.command.gateway.CreateGatewayConfigCommand; -import com.nike.cerberus.command.gateway.PublishLambdaCommand; -import com.nike.cerberus.command.vault.CreateCmsVaultTokenCommand; -import com.nike.cerberus.command.vault.CreateVaultClusterCommand; -import com.nike.cerberus.command.vault.CreateVaultConfigCommand; -import com.nike.cerberus.command.vault.DisableAuditBackendCommand; -import com.nike.cerberus.command.vault.EnableAuditBackendCommand; -import com.nike.cerberus.command.vault.InitVaultClusterCommand; -import com.nike.cerberus.command.vault.LoadDefaultVaultPoliciesCommand; -import com.nike.cerberus.command.vault.UnsealVaultClusterCommand; -import com.nike.cerberus.command.vault.VaultHealthCheckCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.logging.LoggingConfigurer; import com.nike.cerberus.module.CerberusModule; @@ -177,30 +158,11 @@ private void printCliVersion() { private void registerAllCommands() { registerCommand(new CreateBaseCommand()); registerCommand(new UploadCertFilesCommand()); - registerCommand(new CreateConsulConfigCommand()); - registerCommand(new UpdateConsulConfigCommand()); - registerCommand(new CreateVaultAclCommand()); - registerCommand(new CreateConsulClusterCommand()); - registerCommand(new CreateVaultConfigCommand()); - registerCommand(new CreateVaultClusterCommand()); - registerCommand(new PublishDashboardCommand()); - registerCommand(new InitVaultClusterCommand()); - registerCommand(new UnsealVaultClusterCommand()); - registerCommand(new EnableAuditBackendCommand()); - registerCommand(new DisableAuditBackendCommand()); - registerCommand(new LoadDefaultVaultPoliciesCommand()); - registerCommand(new CreateCmsVaultTokenCommand()); registerCommand(new CreateCmsConfigCommand()); registerCommand(new CreateCmsClusterCommand()); registerCommand(new CreateCmsCmkCommand()); - registerCommand(new CreateGatewayConfigCommand()); - registerCommand(new CreateGatewayClusterCommand()); registerCommand(new UpdateStackCommand()); registerCommand(new PrintStackInfoCommand()); - registerCommand(new VaultHealthCheckCommand()); - registerCommand(new PublishLambdaCommand()); - registerCommand(new CreateCloudFrontLogProcessingLambdaConfigCommand()); - registerCommand(new CreateCloudFrontSecurityGroupUpdaterLambdaCommand()); registerCommand(new WhitelistCidrForVpcAccessCommand()); registerCommand(new RestoreCerberusBackupCommand()); registerCommand(new ViewConfigCommand()); diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 125a3d7a..5edecdef 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -20,25 +20,13 @@ import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; -import com.nike.cerberus.command.consul.CreateConsulClusterCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.command.dashboard.PublishDashboardCommand; -import com.nike.cerberus.command.gateway.CreateCloudFrontLogProcessingLambdaConfigCommand; -import com.nike.cerberus.command.gateway.CreateGatewayClusterCommand; -import com.nike.cerberus.command.gateway.CreateGatewayConfigCommand; -import com.nike.cerberus.command.gateway.PublishLambdaCommand; -import com.nike.cerberus.command.vault.CreateVaultClusterCommand; import com.nike.cerberus.domain.input.CerberusStack; -import com.nike.cerberus.domain.input.Consul; -import com.nike.cerberus.domain.input.Dashboard; -import com.nike.cerberus.domain.input.EdgeSecurity; import com.nike.cerberus.domain.input.EnvironmentConfig; -import com.nike.cerberus.domain.input.Gateway; import com.nike.cerberus.domain.input.ManagementService; -import com.nike.cerberus.domain.input.Vault; import com.nike.cerberus.domain.input.VpcAccessWhitelist; import org.apache.commons.lang3.StringUtils; @@ -90,28 +78,14 @@ private static List getArgsForCommand(EnvironmentConfig environmentConfi return getCreateBaseCommandArgs(environmentConfig); case UploadCertFilesCommand.COMMAND_NAME: return getUploadCertFilesCommandArgs(environmentConfig, passedArgs); - case CreateConsulClusterCommand.COMMAND_NAME: - return getCreateConsulClusterCommandArgs(environmentConfig); - case CreateVaultClusterCommand.COMMAND_NAME: - return getCreateVaultClusterCommandArgs(environmentConfig); case CreateCmsClusterCommand.COMMAND_NAME: return getCreateCmsClusterCommandArgs(environmentConfig); - case CreateGatewayClusterCommand.COMMAND_NAME: - return getCreateGatewayClusterCommandArgs(environmentConfig); - case PublishDashboardCommand.COMMAND_NAME: - return getPublishDashboardCommandArgs(environmentConfig); case WhitelistCidrForVpcAccessCommand.COMMAND_NAME: return getWhitelistCidrForVpcAccessCommandArgs(environmentConfig); case CreateCmsConfigCommand.COMMAND_NAME: return getCreateCmsConfigCommandArgs(environmentConfig); - case CreateGatewayConfigCommand.COMMAND_NAME: - return getCreateGatewayClusterConfigCommandArgs(environmentConfig); case UpdateStackCommand.COMMAND_NAME: return getUpdateStackCommandArgs(environmentConfig, passedArgs); - case PublishLambdaCommand.COMMAND_NAME: - return getPublishLambdaCommandArgs(environmentConfig, passedArgs); - case CreateCloudFrontLogProcessingLambdaConfigCommand.COMMAND_NAME: - return getCreateCloudFrontLogProcessingLambdaConfigCommandArgs(environmentConfig); case UpdateCmsConfigCommand.COMMAND_NAME: return getCreateCmsConfigCommandArgs(environmentConfig); default: @@ -119,65 +93,6 @@ private static List getArgsForCommand(EnvironmentConfig environmentConfi } } - private static List getCreateCloudFrontLogProcessingLambdaConfigCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - EdgeSecurity edgeSecurity = environmentConfig.getEdgeSecurity(); - - args.add(CreateCloudFrontLogProcessingLambdaConfigCommand.RATE_LIMIT_PER_MINUTE_LONG_ARG); - args.add(edgeSecurity.getRateLimitPerMinute()); - args.add(CreateCloudFrontLogProcessingLambdaConfigCommand.RATE_LIMIT_VIOLATION_BLOCK_PERIOD_IN_MINUTES_LONG_ARG); - args.add(edgeSecurity.getRateLimitViolationBlockPeriodInMinutes()); - - if (StringUtils.isNotBlank(edgeSecurity.getGoogleAnalyticsTrackingId())) { - args.add(CreateCloudFrontLogProcessingLambdaConfigCommand.GOOGLE_ANALYTICS_TRACKING_ID_LONG_ARG); - args.add(edgeSecurity.getGoogleAnalyticsTrackingId()); - } - - if (StringUtils.isNotBlank(edgeSecurity.getSlackWebHookUrl())) { - args.add(CreateCloudFrontLogProcessingLambdaConfigCommand.SLACK_WEB_HOOK_URL_LONG_ARG); - args.add(edgeSecurity.getSlackWebHookUrl()); - } - - if (StringUtils.isNotBlank(edgeSecurity.getSlackIcon())) { - args.add(CreateCloudFrontLogProcessingLambdaConfigCommand.SLACK_ICON_LONG_ARG); - args.add(edgeSecurity.getSlackIcon()); - } - - return args; - } - - private static List getPublishLambdaCommandArgs(EnvironmentConfig environmentConfig, String[] passedArgs) { - List args = new LinkedList<>(); - String lambdaName = null; - for (int i = 0; i < passedArgs.length; i++) { - if (StringUtils.equals(passedArgs[i], PublishLambdaCommand.LAMBDA_NAME_LONG_ARG) && i < passedArgs.length - 1) { - lambdaName = passedArgs[i+1]; - } - } - - if (StringUtils.isBlank(lambdaName)) { - return args; - } - - args.add(PublishLambdaCommand.LAMBDA_NAME_LONG_ARG); - args.add(lambdaName); - args.add(PublishLambdaCommand.ARTIFACT_URL_LONG_ARG); - - switch (lambdaName.toUpperCase()) { - case "CLOUD_FRONT_SG_GROUP_IP_SYNC": - args.add(environmentConfig.getEdgeSecurity().getCloudfrontSecurityGroupIpSyncLambdaArtifactUrl()); - break; - case "WAF": - args.add(environmentConfig.getEdgeSecurity().getCloudfrontLambdaArtifactUrl()); - break; - default: - args.add(""); - } - - return args; - } - private static List getCreateCmsConfigCommandArgs(EnvironmentConfig environmentConfig) { List args = new LinkedList<>(); @@ -194,15 +109,6 @@ private static List getCreateCmsConfigCommandArgs(EnvironmentConfig envi return args; } - private static List getCreateGatewayClusterConfigCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - args.add(CreateGatewayClusterCommand.HOSTNAME_LONG_ARG); - args.add(environmentConfig.getHostname()); - - return args; - } - private static List getWhitelistCidrForVpcAccessCommandArgs(EnvironmentConfig environmentConfig) { List args = new LinkedList<>(); @@ -221,40 +127,6 @@ private static List getWhitelistCidrForVpcAccessCommandArgs(EnvironmentC return args; } - private static List getPublishDashboardCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - Dashboard dashboard = environmentConfig.getDashboard(); - - args.add(PublishDashboardCommand.ARTIFACT_URL_LONG_ARG); - args.add(dashboard.getArtifactUrl()); - - if ( StringUtils.isNotBlank(dashboard.getOverrideArtifactUrl()) ) { - args.add(PublishDashboardCommand.OVERRIDE_ARTIFACT_URL_LONG_ARG); - args.add(dashboard.getOverrideArtifactUrl()); - } - - return args; - } - - private static List getCreateConsulClusterCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - Consul component = environmentConfig.getConsul(); - addCommonStackArgs(environmentConfig, args, component); - - return args; - } - - private static List getCreateVaultClusterCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - Vault component = environmentConfig.getVault(); - addCommonStackArgs(environmentConfig, args, component); - - return args; - } - private static List getCreateCmsClusterCommandArgs(EnvironmentConfig environmentConfig) { List args = new LinkedList<>(); @@ -264,19 +136,6 @@ private static List getCreateCmsClusterCommandArgs(EnvironmentConfig env return args; } - private static List getCreateGatewayClusterCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - Gateway component = environmentConfig.getGateway(); - addCommonStackArgs(environmentConfig, args, component); - args.add(CreateGatewayClusterCommand.HOSTNAME_LONG_ARG); - args.add(environmentConfig.getHostname()); - args.add(CreateGatewayClusterCommand.HOSTED_ZONE_ID_LONG_ARG); - args.add(environmentConfig.getHostedZoneId()); - - return args; - } - private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List args, CerberusStack stack) { args.add(StackDelegate.AMI_ID_LONG_ARG); args.add(stack.getAmiId()); @@ -319,18 +178,12 @@ private static List getUploadCertFilesCommandArgs(EnvironmentConfig envi args.add(UploadCertFilesCommand.CERT_PATH_LONG_ARG); switch (stackName) { - case "consul": - args.add(environmentConfig.getConsul().getCertPath()); - break; case "vault": args.add(environmentConfig.getVault().getCertPath()); break; case "cms": args.add(environmentConfig.getManagementService().getCertPath()); break; - case "gateway": - args.add(environmentConfig.getGateway().getCertPath()); - break; default: args.add(""); } @@ -372,18 +225,12 @@ private static List getUpdateStackCommandArgs(EnvironmentConfig environm CerberusStack cerberusStack; switch (stackName) { - case "consul": - cerberusStack = environmentConfig.getConsul(); - break; case "vault": cerberusStack = environmentConfig.getVault(); break; case "cms": cerberusStack = environmentConfig.getManagementService(); break; - case "gateway": - cerberusStack = environmentConfig.getGateway(); - break; default: cerberusStack = null; } diff --git a/src/main/java/com/nike/cerberus/command/consul/CreateConsulClusterCommand.java b/src/main/java/com/nike/cerberus/command/consul/CreateConsulClusterCommand.java deleted file mode 100644 index 5fd06339..00000000 --- a/src/main/java/com/nike/cerberus/command/consul/CreateConsulClusterCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 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.consul; - -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.command.StackDelegate; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.consul.CreateConsulClusterOperation; - -import static com.nike.cerberus.command.consul.CreateConsulClusterCommand.COMMAND_NAME; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_ARG; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_DESCRIPTION; - -/** - * Command to create the consul cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the consul cluster.") -public class CreateConsulClusterCommand implements Command { - - public static final String COMMAND_NAME = "create-consul-cluster"; - - @ParametersDelegate - private StackDelegate stackDelegate = new StackDelegate(); - - public StackDelegate getStackDelegate() { - return stackDelegate; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Parameter(names = SKIP_AMI_TAG_CHECK_ARG, - description = SKIP_AMI_TAG_CHECK_DESCRIPTION) - private boolean skipAmiTagCheck; - - public boolean isSkipAmiTagCheck() { - return skipAmiTagCheck; - } - - @Override - public Class> getOperationClass() { - return CreateConsulClusterOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/consul/CreateConsulConfigCommand.java b/src/main/java/com/nike/cerberus/command/consul/CreateConsulConfigCommand.java deleted file mode 100644 index 5f34540f..00000000 --- a/src/main/java/com/nike/cerberus/command/consul/CreateConsulConfigCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2016 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.consul; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.consul.CreateConsulConfigOperation; - -import static com.nike.cerberus.command.consul.CreateConsulConfigCommand.COMMAND_NAME; - -/** - * Command to create the consul configuration. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the consul configuration for the cluster.") -public class CreateConsulConfigCommand implements Command { - - public static final String COMMAND_NAME = "create-consul-config"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateConsulConfigOperation.class; - } -} - diff --git a/src/main/java/com/nike/cerberus/command/consul/CreateVaultAclCommand.java b/src/main/java/com/nike/cerberus/command/consul/CreateVaultAclCommand.java deleted file mode 100644 index c9272254..00000000 --- a/src/main/java/com/nike/cerberus/command/consul/CreateVaultAclCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2016 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.consul; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.consul.CreateVaultAclOperation; - -import static com.nike.cerberus.command.consul.CreateVaultAclCommand.COMMAND_NAME; - -/** - * Command to create the Vault ACL with Consul. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the Vault ACL with Consul.") -public class CreateVaultAclCommand implements Command { - - public static final String COMMAND_NAME = "create-vault-acl"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateVaultAclOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/consul/UpdateConsulConfigCommand.java b/src/main/java/com/nike/cerberus/command/consul/UpdateConsulConfigCommand.java deleted file mode 100644 index f24324f5..00000000 --- a/src/main/java/com/nike/cerberus/command/consul/UpdateConsulConfigCommand.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.nike.cerberus.command.consul; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.consul.UpdateConsulConfigOperation; - -import static com.nike.cerberus.command.consul.CreateConsulConfigCommand.COMMAND_NAME; - -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Updates the consul configuration for the cluster while maintaining the secrets in the current config.") -public class UpdateConsulConfigCommand implements Command { - - public static final String COMMAND_NAME = "update-consul-config"; - - @Parameter(names = {"--acl-master-token"}, description = "Overwrites the existing ACL Master Token with the value supplied." - + " If not supplied, the existing token in secrets.json will be maintained (safer and generally preferred).") - private String aclMasterToken = null; - - @Parameter(names = {"--gossip-encryption-token"}, description = "Overwrites the existing Gossip Encryption Token with the value supplied." - + " If not supplied, the existing token in secrets.json will be maintained (safer and generally preferred).") - private String gossipEncryptionToken = null; - - public String getAclMasterToken() { - return aclMasterToken; - } - - public String getGossipEncryptionToken() { - return gossipEncryptionToken; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return UpdateConsulConfigOperation.class; - } -} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/command/dashboard/PublishDashboardCommand.java b/src/main/java/com/nike/cerberus/command/dashboard/PublishDashboardCommand.java deleted file mode 100644 index 21b69091..00000000 --- a/src/main/java/com/nike/cerberus/command/dashboard/PublishDashboardCommand.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 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.dashboard; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.dashboard.PublishDashboardOperation; - -import java.net.URL; - -import static com.nike.cerberus.command.dashboard.PublishDashboardCommand.COMMAND_NAME; - -/** - * Command for uploading a new dashboard. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Publishes the dashboard artifact to Cerberus.") -public class PublishDashboardCommand implements Command { - - public static final String COMMAND_NAME = "publish-dashboard"; - public static final String ARTIFACT_URL_LONG_ARG = "--artifact-url"; - public static final String OVERRIDE_ARTIFACT_URL_LONG_ARG = "--override-artifact-url"; - - @Parameter(names = ARTIFACT_URL_LONG_ARG, description = "URL to the dashboard artifact.", required = true) - private URL artifactUrl; - - @Parameter(names = OVERRIDE_ARTIFACT_URL_LONG_ARG, description = "URL to an artifact that will be extracted and merged into the main artifact before upload to s3.") - private URL overrideArtifactUrl; - - public URL getArtifactUrl() { - return artifactUrl; - } - - public URL getOverrideArtifactUrl() { - return overrideArtifactUrl; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return PublishDashboardOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontLogProcessingLambdaConfigCommand.java b/src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontLogProcessingLambdaConfigCommand.java deleted file mode 100644 index 87b38838..00000000 --- a/src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontLogProcessingLambdaConfigCommand.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.gateway.CreateCloudFrontLogProcessingLambdaConfigOperation; - -import static com.nike.cerberus.command.gateway.CreateCloudFrontLogProcessingLambdaConfigCommand.COMMAND_NAME; - -/** - * Command for creating config file for Cloud Front Log Processing Lambda - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates Config file needed to drive CloudFront log processing lambda.") -public class CreateCloudFrontLogProcessingLambdaConfigCommand implements Command { - - public static final String COMMAND_NAME = "create-cloud-front-log-processor-lambda-config"; - public static final String RATE_LIMIT_PER_MINUTE_LONG_ARG = "--rate-limit-per-minute"; - public static final String RATE_LIMIT_VIOLATION_BLOCK_PERIOD_IN_MINUTES_LONG_ARG = "--rate-limit-violation-block-period-in-minutes"; - public static final String SLACK_WEB_HOOK_URL_LONG_ARG = "--slack-web-hook-url"; - public static final String SLACK_ICON_LONG_ARG = "--slack-icon"; - public static final String GOOGLE_ANALYTICS_TRACKING_ID_LONG_ARG = "--google-analytics-tracking-id"; - - @Parameter(names = RATE_LIMIT_PER_MINUTE_LONG_ARG, description = "The maximum number of requests from an IP per minute that can flow through the gateway before being auto blocked.", required = true) - private Integer requestPerMinuteLimit; - - @Parameter(names = RATE_LIMIT_VIOLATION_BLOCK_PERIOD_IN_MINUTES_LONG_ARG, description = "Time in minutes to block an ip that violates the rate limit.", required = true) - private Integer rateLimitViolationBlacklistPeriodInMinutes; - - @Parameter(names = SLACK_WEB_HOOK_URL_LONG_ARG, description = "If you provide a web hook url for slack the lambda will send messages on errors and summary info.") - private String slackWebHookUrl; - - @Parameter(names = SLACK_ICON_LONG_ARG, description = "If you provide an emoji or an icon url the lambda will use it when sending messages.") - private String slackIcon; - - @Parameter(names = GOOGLE_ANALYTICS_TRACKING_ID_LONG_ARG, description = "If you provide a Google Analytics tracking id, the KPI processer will send events to that GA Account.") - private String googleAnalyticsId; - - public Integer getRequestPerMinuteLimit() { - return requestPerMinuteLimit; - } - - public Integer getRateLimitViolationBlacklistPeriodInMinutes() { - return rateLimitViolationBlacklistPeriodInMinutes; - } - - public String getSlackWebHookUrl() { - return slackWebHookUrl; - } - - public String getSlackIcon() { - return slackIcon; - } - - public String getGoogleAnalyticsId() { - return googleAnalyticsId; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateCloudFrontLogProcessingLambdaConfigOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaCommand.java b/src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaCommand.java deleted file mode 100644 index 4758c1ff..00000000 --- a/src/main/java/com/nike/cerberus/command/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.gateway.CreateCloudFrontSecurityGroupUpdaterLambdaOperation; - -import static com.nike.cerberus.command.gateway.CreateCloudFrontLogProcessingLambdaConfigCommand.COMMAND_NAME; - -/** - * This command creates the Lambda needed to update the Security Groups that limit traffic ingress to only IPs coming - * from AWS Cloud Front. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "creates the Lambda needed to update the Security Groups that limit traffic ingress to only IPs coming from AWS Cloud Front.") -public class CreateCloudFrontSecurityGroupUpdaterLambdaCommand implements Command { - - public static final String COMMAND_NAME = "create-cloud-front-security-group-updater-lambda"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateCloudFrontSecurityGroupUpdaterLambdaOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/gateway/CreateGatewayClusterCommand.java b/src/main/java/com/nike/cerberus/command/gateway/CreateGatewayClusterCommand.java deleted file mode 100644 index b4a755e9..00000000 --- a/src/main/java/com/nike/cerberus/command/gateway/CreateGatewayClusterCommand.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -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.command.StackDelegate; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.gateway.CreateGatewayClusterOperation; - -import static com.nike.cerberus.command.gateway.CreateGatewayClusterCommand.COMMAND_NAME; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_ARG; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_DESCRIPTION; - -/** - * Command to create the Gateway cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the CMS cluster.") -public class CreateGatewayClusterCommand implements Command { - - public static final String COMMAND_NAME = "create-gateway-cluster"; - public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; - public static final String HOSTNAME_LONG_ARG = "--hostname"; - - @Parameter(names = HOSTED_ZONE_ID_LONG_ARG, - description = "The Route 53 hosted zone ID that will be used to create the CNAME record for Cerberus.", - required = true) - private String hostedZoneId; - - @Parameter(names = HOSTNAME_LONG_ARG, - description = "The hostname that will be used to expose this Cerberus environment.", - required = true) - private String hostname; - - @Parameter(names = SKIP_AMI_TAG_CHECK_ARG, - description = SKIP_AMI_TAG_CHECK_DESCRIPTION) - private boolean skipAmiTagCheck; - - @ParametersDelegate - private StackDelegate stackDelegate = new StackDelegate(); - - public String getHostedZoneId() { - return hostedZoneId; - } - - public String getHostname() { - return hostname; - } - - public boolean isSkipAmiTagCheck() { - return skipAmiTagCheck; - } - - public StackDelegate getStackDelegate() { - return stackDelegate; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateGatewayClusterOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/gateway/CreateGatewayConfigCommand.java b/src/main/java/com/nike/cerberus/command/gateway/CreateGatewayConfigCommand.java deleted file mode 100644 index 6c3e1093..00000000 --- a/src/main/java/com/nike/cerberus/command/gateway/CreateGatewayConfigCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.gateway.CreateGatewayConfigOperation; - -import static com.nike.cerberus.command.gateway.CreateGatewayConfigCommand.COMMAND_NAME; - -/** - * Command to create the gateway configuration. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the gateway configuration for the cluster.") -public class CreateGatewayConfigCommand implements Command { - - public static final String COMMAND_NAME = "create-gateway-config"; - - @Parameter(names = CreateGatewayClusterCommand.HOSTNAME_LONG_ARG, - description = "The hostname that will be used to expose this Cerberus environment.", - required = true) - private String hostname; - - public String getHostname() { - return hostname; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateGatewayConfigOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/gateway/PublishLambdaCommand.java b/src/main/java/com/nike/cerberus/command/gateway/PublishLambdaCommand.java deleted file mode 100644 index 5bcb0975..00000000 --- a/src/main/java/com/nike/cerberus/command/gateway/PublishLambdaCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.environment.LambdaName; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.gateway.PublishLambdaOperation; - -import java.net.URL; - -import static com.nike.cerberus.command.gateway.PublishLambdaCommand.COMMAND_NAME; - -/** - * Command for uploading a new lambda. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Publishes the lambda artifact to Cerberus.") -public class PublishLambdaCommand implements Command { - - public static final String COMMAND_NAME = "publish-lambda"; - public static final String LAMBDA_NAME_LONG_ARG = "--lambda-name"; - public static final String ARTIFACT_URL_LONG_ARG = "--artifact-url"; - - @Parameter(names = LAMBDA_NAME_LONG_ARG, description = "Which lambda is being uploaded.", required = true) - private LambdaName lambdaName; - - @Parameter(names = ARTIFACT_URL_LONG_ARG, description = "URL to the lambda artifact.", required = true) - private URL artifactUrl; - - public LambdaName getLambdaName() { - return lambdaName; - } - - public URL getArtifactUrl() { - return artifactUrl; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return PublishLambdaOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java index 68d20ae1..86b7752c 100644 --- a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java +++ b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java @@ -28,8 +28,7 @@ */ public class UploadCertFilesStackNameValidator implements IValueValidator { - private final Set stackNamesWithCerts = Sets.newHashSet( - StackName.VAULT, StackName.CONSUL, StackName.CMS, StackName.GATEWAY); + private final Set stackNamesWithCerts = Sets.newHashSet(StackName.CMS); @Override public void validate(final String name, final StackName stackName) throws ParameterException { diff --git a/src/main/java/com/nike/cerberus/command/vault/CreateCmsVaultTokenCommand.java b/src/main/java/com/nike/cerberus/command/vault/CreateCmsVaultTokenCommand.java deleted file mode 100644 index 1495bd24..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/CreateCmsVaultTokenCommand.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.CreateCmsVaultTokenOperation; - -import static com.nike.cerberus.command.vault.CreateCmsVaultTokenCommand.COMMAND_NAME; - -/** - * Command to create the Vault token for CMS. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the Vault token for CMS.") -public class CreateCmsVaultTokenCommand implements Command { - - public static final String COMMAND_NAME = "create-cms-vault-token"; - - public static final String FORCE_OVERWRITE_LONG_ARG = "--force-overwrite"; - - // This option is mainly useful if: - // 1) you want to go through some manual steps to rotate the CMS vault token - // 2) you've partially hosed a development system - @Parameter(names = FORCE_OVERWRITE_LONG_ARG, - description = "Force overwriting existing CMS vault token in secrets.json. It is important to manually revoke the old CMS token when using this option.") - private boolean forceOverwrite = false; - - public boolean isForceOverwrite() { - return forceOverwrite; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateCmsVaultTokenOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/CreateVaultClusterCommand.java b/src/main/java/com/nike/cerberus/command/vault/CreateVaultClusterCommand.java deleted file mode 100644 index 6791beef..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/CreateVaultClusterCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -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.command.StackDelegate; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.CreateVaultClusterOperation; - -import static com.nike.cerberus.command.vault.CreateVaultClusterCommand.COMMAND_NAME; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_ARG; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_DESCRIPTION; - -/** - * Command to create the Vault cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the Vault cluster.") -public class CreateVaultClusterCommand implements Command { - - public static final String COMMAND_NAME = "create-vault-cluster"; - - @ParametersDelegate - private StackDelegate stackDelegate = new StackDelegate(); - - public StackDelegate getStackDelegate() { - return stackDelegate; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Parameter(names = SKIP_AMI_TAG_CHECK_ARG, - description = SKIP_AMI_TAG_CHECK_DESCRIPTION) - private boolean skipAmiTagCheck; - - public boolean isSkipAmiTagCheck() { - return skipAmiTagCheck; - } - - @Override - public Class> getOperationClass() { - return CreateVaultClusterOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/CreateVaultConfigCommand.java b/src/main/java/com/nike/cerberus/command/vault/CreateVaultConfigCommand.java deleted file mode 100644 index bc889df7..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/CreateVaultConfigCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.CreateVaultConfigOperation; - -import static com.nike.cerberus.command.vault.CreateVaultConfigCommand.COMMAND_NAME; - -/** - * Command to create the vault configuration. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the vault configuration for the cluster.") -public class CreateVaultConfigCommand implements Command { - - public static final String COMMAND_NAME = "create-vault-config"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateVaultConfigOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/DisableAuditBackendCommand.java b/src/main/java/com/nike/cerberus/command/vault/DisableAuditBackendCommand.java deleted file mode 100644 index 70ebe27e..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/DisableAuditBackendCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.vault.AuditBackend; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.DisableAuditBackendOperation; - -import static com.nike.cerberus.command.vault.DisableAuditBackendCommand.COMMAND_NAME; - -/** - * Command for disabling a specific audit backend. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Disables the specified audit backend.") -public class DisableAuditBackendCommand implements Command { - - public static final String COMMAND_NAME = "disable-audit-backend"; - - @Parameter(names = {"--backend"}, required = true, description = "Audit backend.") - private AuditBackend auditBackend; - - public AuditBackend getAuditBackend() { - return auditBackend; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return DisableAuditBackendOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/EnableAuditBackendCommand.java b/src/main/java/com/nike/cerberus/command/vault/EnableAuditBackendCommand.java deleted file mode 100644 index c9476fed..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/EnableAuditBackendCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.vault.AuditBackend; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.EnableAuditBackendOperation; - -import java.nio.file.Path; - -import static com.nike.cerberus.command.vault.EnableAuditBackendCommand.COMMAND_NAME; - -/** - * Command to enable the audit backend for Vault. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Enables the audit backend for Vault.") -public class EnableAuditBackendCommand implements Command { - - public static final String COMMAND_NAME = "enable-audit-backend"; - - @Parameter(names = {"--backend"}, required = true, description = "Audit backend.") - private AuditBackend auditBackend; - - @Parameter(names = {"--file-path"}, - description = "File path for where the audit log will be written on the Vault instance. " + - "Only required for the file backend type.") - private Path filePath; - - public AuditBackend getAuditBackend() { - return auditBackend; - } - - public Path getFilePath() { - return filePath; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return EnableAuditBackendOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/InitVaultClusterCommand.java b/src/main/java/com/nike/cerberus/command/vault/InitVaultClusterCommand.java deleted file mode 100644 index d1ca10eb..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/InitVaultClusterCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.InitVaultClusterOperation; - -import static com.nike.cerberus.command.vault.InitVaultClusterCommand.COMMAND_NAME; - -/** - * Command to initialize the Vault cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Initializes the Vault cluster.") -public class InitVaultClusterCommand implements Command { - - public static final String COMMAND_NAME = "init-vault-cluster"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return InitVaultClusterOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/LoadDefaultVaultPoliciesCommand.java b/src/main/java/com/nike/cerberus/command/vault/LoadDefaultVaultPoliciesCommand.java deleted file mode 100644 index 906735b3..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/LoadDefaultVaultPoliciesCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.LoadDefaultVaultPoliciesOperation; - -import static com.nike.cerberus.command.vault.LoadDefaultVaultPoliciesCommand.COMMAND_NAME; - -/** - * Command to initialize the Vault cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Loads the default policies into Vault.") -public class LoadDefaultVaultPoliciesCommand implements Command { - - public static final String COMMAND_NAME = "load-default-policies"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return LoadDefaultVaultPoliciesOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/UnsealVaultClusterCommand.java b/src/main/java/com/nike/cerberus/command/vault/UnsealVaultClusterCommand.java deleted file mode 100644 index 4882e477..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/UnsealVaultClusterCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.UnsealVaultClusterOperation; - -import static com.nike.cerberus.command.vault.UnsealVaultClusterCommand.COMMAND_NAME; - -/** - * Command to unseal the Vault cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Unseals the Vault cluster.") -public class UnsealVaultClusterCommand implements Command { - - public static final String COMMAND_NAME = "unseal-vault-cluster"; - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return UnsealVaultClusterOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/vault/VaultHealthCheckCommand.java b/src/main/java/com/nike/cerberus/command/vault/VaultHealthCheckCommand.java deleted file mode 100644 index a4f205e5..00000000 --- a/src/main/java/com/nike/cerberus/command/vault/VaultHealthCheckCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.vault.VaultHealthCheckOperation; - -import static com.nike.cerberus.command.vault.UnsealVaultClusterCommand.COMMAND_NAME; - -/** - * Polls health statuses for vault cluster - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Reports the health of each Vault instance.") -public class VaultHealthCheckCommand implements Command { - - public static final String COMMAND_NAME = "vault-health"; - - @Parameter(names = "--poll", - description = "Flag for polling health check vs running once") - private boolean poll; - - public boolean isPoll() { - return poll; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return VaultHealthCheckOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerOutputs.java deleted file mode 100644 index 15eb5a65..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerOutputs.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2016 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; - -public class CloudFrontIpSynchronizerOutputs { - - private String cloudFrontOriginElbSgIpSyncFunctionArn; - - public String getCloudFrontOriginElbSgIpSyncFunctionArn() { - return cloudFrontOriginElbSgIpSyncFunctionArn; - } - - public CloudFrontIpSynchronizerOutputs setCloudFrontOriginElbSgIpSyncFunctionArn(String cloudFrontOriginElbSgIpSyncFunctionArn) { - this.cloudFrontOriginElbSgIpSyncFunctionArn = cloudFrontOriginElbSgIpSyncFunctionArn; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerParameters.java deleted file mode 100644 index 2be59ec9..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/CloudFrontIpSynchronizerParameters.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2016 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; - -public class CloudFrontIpSynchronizerParameters { - - private String lambdaBucket; - - private String lambdaKey; - - public String getLambdaBucket() { - return lambdaBucket; - } - - public CloudFrontIpSynchronizerParameters setLambdaBucket(String lambdaBucket) { - this.lambdaBucket = lambdaBucket; - return this; - } - - public String getLambdaKey() { - return lambdaKey; - } - - public CloudFrontIpSynchronizerParameters setLambdaKey(String lambdaKey) { - this.lambdaKey = lambdaKey; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/ConsulOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/ConsulOutputs.java deleted file mode 100644 index ff70c334..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/ConsulOutputs.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016 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; - -/** - * Consul CloudFormation stack outputs - */ -public class ConsulOutputs { - - private String autoscalingGroupLogicalId; - - private String launchConfigurationLogicalId; - - public String getAutoscalingGroupLogicalId() { - return autoscalingGroupLogicalId; - } - - public ConsulOutputs setAutoscalingGroupLogicalId(String autoscalingGroupLogicalId) { - this.autoscalingGroupLogicalId = autoscalingGroupLogicalId; - return this; - } - - public String getLaunchConfigurationLogicalId() { - return launchConfigurationLogicalId; - } - - public ConsulOutputs setLaunchConfigurationLogicalId(String launchConfigurationLogicalId) { - this.launchConfigurationLogicalId = launchConfigurationLogicalId; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/ConsulParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/ConsulParameters.java deleted file mode 100644 index 515e38aa..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/ConsulParameters.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2016 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; - -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -/** - * Consul CloudFormation stack inputs - */ -public class ConsulParameters implements LaunchConfigParameters { - - private String instanceProfileName; - - private String consulClientSgId; - - private String consulServerSgId; - - private String toolsIngressSgId; - - private String vpcId; - - private String vpcSubnetIdForAz1; - - private String vpcSubnetIdForAz2; - - private String vpcSubnetIdForAz3; - - @JsonUnwrapped - private SslConfigParametersDelegate sslConfigParameters = new SslConfigParametersDelegate(); - - @JsonUnwrapped - private LaunchConfigParametersDelegate launchConfigParameters = new LaunchConfigParametersDelegate(); - - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - - public String getInstanceProfileName() { - return instanceProfileName; - } - - public ConsulParameters setInstanceProfileName(String instanceProfileName) { - this.instanceProfileName = instanceProfileName; - return this; - } - - public String getConsulClientSgId() { - return consulClientSgId; - } - - public ConsulParameters setConsulClientSgId(String consulClientSgId) { - this.consulClientSgId = consulClientSgId; - return this; - } - - public String getConsulServerSgId() { - return consulServerSgId; - } - - public ConsulParameters setConsulServerSgId(String consulServerSgId) { - this.consulServerSgId = consulServerSgId; - return this; - } - - public String getToolsIngressSgId() { - return toolsIngressSgId; - } - - public ConsulParameters setToolsIngressSgId(String toolsIngressSgId) { - this.toolsIngressSgId = toolsIngressSgId; - return this; - } - - public String getVpcId() { - return vpcId; - } - - public ConsulParameters setVpcId(String vpcId) { - this.vpcId = vpcId; - return this; - } - - public String getVpcSubnetIdForAz1() { - return vpcSubnetIdForAz1; - } - - public ConsulParameters setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { - this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; - return this; - } - - public String getVpcSubnetIdForAz2() { - return vpcSubnetIdForAz2; - } - - public ConsulParameters setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { - this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; - return this; - } - - public String getVpcSubnetIdForAz3() { - return vpcSubnetIdForAz3; - } - - public ConsulParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { - this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; - return this; - } - - @Override - public SslConfigParametersDelegate getSslConfigParameters() { - return sslConfigParameters; - } - - @Override - public LaunchConfigParametersDelegate getLaunchConfigParameters() { - return launchConfigParameters; - } - - public ConsulParameters setLaunchConfigParameters(LaunchConfigParametersDelegate launchConfigParameters) { - this.launchConfigParameters = launchConfigParameters; - return this; - } - - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public ConsulParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayOutputs.java deleted file mode 100644 index 6fe4be4e..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayOutputs.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2016 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; - -/** - * Gateway CloudFormation stack outputs - */ -public class GatewayOutputs { - - private String autoscalingGroupLogicalId; - - private String launchConfigurationLogicalId; - - private String elbLogicalId; - - private String elbCanonicalHostedZoneName; - - private String elbCanonicalHostedZoneNameId; - - private String elbDnsName; - - private String elbSourceSecurityGroupName; - - private String elbSourceSecurityGroupOwnerAlias; - - private String cloudFrontDistributionId; - - private String cloudFrontDistributionDomainName; - - private String cloudFrontAccessLogBucket; - - private String whiteListIPSetID; - - private String manualBlockIPSetID; - - private String autoBlockIPSetID; - - public String getAutoscalingGroupLogicalId() { - return autoscalingGroupLogicalId; - } - - public GatewayOutputs setAutoscalingGroupLogicalId(String autoscalingGroupLogicalId) { - this.autoscalingGroupLogicalId = autoscalingGroupLogicalId; - return this; - } - - public String getLaunchConfigurationLogicalId() { - return launchConfigurationLogicalId; - } - - public GatewayOutputs setLaunchConfigurationLogicalId(String launchConfigurationLogicalId) { - this.launchConfigurationLogicalId = launchConfigurationLogicalId; - return this; - } - - public String getElbLogicalId() { - return elbLogicalId; - } - - public GatewayOutputs setElbLogicalId(String elbLogicalId) { - this.elbLogicalId = elbLogicalId; - return this; - } - - public String getElbCanonicalHostedZoneName() { - return elbCanonicalHostedZoneName; - } - - public GatewayOutputs setElbCanonicalHostedZoneName(String elbCanonicalHostedZoneName) { - this.elbCanonicalHostedZoneName = elbCanonicalHostedZoneName; - return this; - } - - public String getElbCanonicalHostedZoneNameId() { - return elbCanonicalHostedZoneNameId; - } - - public GatewayOutputs setElbCanonicalHostedZoneNameId(String elbCanonicalHostedZoneNameId) { - this.elbCanonicalHostedZoneNameId = elbCanonicalHostedZoneNameId; - return this; - } - - public String getElbDnsName() { - return elbDnsName; - } - - public GatewayOutputs setElbDnsName(String elbDnsName) { - this.elbDnsName = elbDnsName; - return this; - } - - public String getElbSourceSecurityGroupName() { - return elbSourceSecurityGroupName; - } - - public GatewayOutputs setElbSourceSecurityGroupName(String elbSourceSecurityGroupName) { - this.elbSourceSecurityGroupName = elbSourceSecurityGroupName; - return this; - } - - public String getElbSourceSecurityGroupOwnerAlias() { - return elbSourceSecurityGroupOwnerAlias; - } - - public GatewayOutputs setElbSourceSecurityGroupOwnerAlias(String elbSourceSecurityGroupOwnerAlias) { - this.elbSourceSecurityGroupOwnerAlias = elbSourceSecurityGroupOwnerAlias; - return this; - } - - public String getCloudFrontDistributionId() { - return cloudFrontDistributionId; - } - - public GatewayOutputs setCloudFrontDistributionId(String cloudFrontDistributionId) { - this.cloudFrontDistributionId = cloudFrontDistributionId; - return this; - } - - public String getCloudFrontDistributionDomainName() { - return cloudFrontDistributionDomainName; - } - - public GatewayOutputs setCloudFrontDistributionDomainName(String cloudFrontDistributionDomainName) { - this.cloudFrontDistributionDomainName = cloudFrontDistributionDomainName; - return this; - } - - public String getCloudFrontAccessLogBucket() { - return cloudFrontAccessLogBucket; - } - - public GatewayOutputs setCloudFrontAccessLogBucket(String cloudFrontAccessLogBucket) { - this.cloudFrontAccessLogBucket = cloudFrontAccessLogBucket; - return this; - } - - public String getWhiteListIPSetID() { - return whiteListIPSetID; - } - - public GatewayOutputs setWhiteListIPSetID(String whiteListIPSetID) { - this.whiteListIPSetID = whiteListIPSetID; - return this; - } - - public String getManualBlockIPSetID() { - return manualBlockIPSetID; - } - - public GatewayOutputs setManualBlockIPSetID(String manualBlockIPSetID) { - this.manualBlockIPSetID = manualBlockIPSetID; - return this; - } - - public String getAutoBlockIPSetID() { - return autoBlockIPSetID; - } - - public GatewayOutputs setAutoBlockIPSetID(String autoBlockIPSetID) { - this.autoBlockIPSetID = autoBlockIPSetID; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LambdaInputParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LambdaInputParameters.java deleted file mode 100644 index 3ce83da4..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LambdaInputParameters.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2016 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; - -public class LambdaInputParameters { - - private String lambdaJarS3Bucket; - private String lambdaJarS3Key; - private String instanceProfileArn; - private String vaultIngressSecurityGroup; - private String vpcSubnetIdForAz1; - private String vpcSubnetIdForAz2; - private String vpcSubnetIdForAz3; - private String tagName; - private String tagEmail; - private String tagCostcenter; - - public String getLambdaJarS3Bucket() { - return lambdaJarS3Bucket; - } - - public LambdaInputParameters setLambdaJarS3Bucket(String lambdaJarS3Bucket) { - this.lambdaJarS3Bucket = lambdaJarS3Bucket; - return this; - } - - public String getLambdaJarS3Key() { - return lambdaJarS3Key; - } - - public LambdaInputParameters setLambdaJarS3Key(String lambdaJarS3Key) { - this.lambdaJarS3Key = lambdaJarS3Key; - return this; - } - - public String getInstanceProfileArn() { - return instanceProfileArn; - } - - public LambdaInputParameters setInstanceProfileArn(String instanceProfileArn) { - this.instanceProfileArn = instanceProfileArn; - return this; - } - - public String getVaultIngressSecurityGroup() { - return vaultIngressSecurityGroup; - } - - public LambdaInputParameters setVaultIngressSecurityGroup(String vaultIngressSecurityGroup) { - this.vaultIngressSecurityGroup = vaultIngressSecurityGroup; - return this; - } - - public String getVpcSubnetIdForAz1() { - return vpcSubnetIdForAz1; - } - - public LambdaInputParameters setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { - this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; - return this; - } - - public String getVpcSubnetIdForAz2() { - return vpcSubnetIdForAz2; - } - - public LambdaInputParameters setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { - this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; - return this; - } - - public String getVpcSubnetIdForAz3() { - return vpcSubnetIdForAz3; - } - - public LambdaInputParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { - this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; - return this; - } - - public String getTagName() { - return tagName; - } - - public LambdaInputParameters setTagName(String tagName) { - this.tagName = tagName; - return this; - } - - public String getTagEmail() { - return tagEmail; - } - - public LambdaInputParameters setTagEmail(String tagEmail) { - this.tagEmail = tagEmail; - return this; - } - - public String getTagCostcenter() { - return tagCostcenter; - } - - public LambdaInputParameters setTagCostcenter(String tagCostcenter) { - this.tagCostcenter = tagCostcenter; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VaultParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VaultParameters.java deleted file mode 100644 index 4c8e6d64..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/VaultParameters.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2016 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; - -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -/** - * Represents the input parameters for the Vault CloudFormation script. - */ -public class VaultParameters implements LaunchConfigParameters { - - private String vpcId; - - private String instanceProfileName; - - private String toolsIngressSgId; - - private String vaultServerElbSgId; - - private String vaultClientSgId; - - private String vaultServerSgId; - - private String consulClientSgId; - - private String consulServerSgId; - - private String vpcSubnetIdForAz1; - - private String vpcSubnetIdForAz2; - - private String vpcSubnetIdForAz3; - - private String hostedZoneId; - - private String cname; - - @JsonUnwrapped - private SslConfigParametersDelegate sslConfigParameters = new SslConfigParametersDelegate(); - - @JsonUnwrapped - private LaunchConfigParametersDelegate launchConfigParameters = new LaunchConfigParametersDelegate(); - - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - - public String getVpcId() { - return vpcId; - } - - public VaultParameters setVpcId(String vpcId) { - this.vpcId = vpcId; - return this; - } - - public String getInstanceProfileName() { - return instanceProfileName; - } - - public VaultParameters setInstanceProfileName(String instanceProfileName) { - this.instanceProfileName = instanceProfileName; - return this; - } - - public String getToolsIngressSgId() { - return toolsIngressSgId; - } - - public VaultParameters setToolsIngressSgId(String toolsIngressSgId) { - this.toolsIngressSgId = toolsIngressSgId; - return this; - } - - public String getVaultServerElbSgId() { - return vaultServerElbSgId; - } - - public VaultParameters setVaultServerElbSgId(String vaultServerElbSgId) { - this.vaultServerElbSgId = vaultServerElbSgId; - return this; - } - - public String getVaultClientSgId() { - return vaultClientSgId; - } - - public VaultParameters setVaultClientSgId(String vaultClientSgId) { - this.vaultClientSgId = vaultClientSgId; - return this; - } - - public String getVaultServerSgId() { - return vaultServerSgId; - } - - public VaultParameters setVaultServerSgId(String vaultServerSgId) { - this.vaultServerSgId = vaultServerSgId; - return this; - } - - public String getConsulClientSgId() { - return consulClientSgId; - } - - public VaultParameters setConsulClientSgId(String consulClientSgId) { - this.consulClientSgId = consulClientSgId; - return this; - } - - public String getConsulServerSgId() { - return consulServerSgId; - } - - public VaultParameters setConsulServerSgId(String consulServerSgId) { - this.consulServerSgId = consulServerSgId; - return this; - } - - public String getVpcSubnetIdForAz1() { - return vpcSubnetIdForAz1; - } - - public VaultParameters setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { - this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; - return this; - } - - public String getVpcSubnetIdForAz2() { - return vpcSubnetIdForAz2; - } - - public VaultParameters setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { - this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; - return this; - } - - public String getVpcSubnetIdForAz3() { - return vpcSubnetIdForAz3; - } - - public VaultParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { - this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; - return this; - } - - public String getHostedZoneId() { - return hostedZoneId; - } - - public VaultParameters setHostedZoneId(String hostedZoneId) { - this.hostedZoneId = hostedZoneId; - return this; - } - - public String getCname() { - return cname; - } - - public VaultParameters setCname(String cname) { - this.cname = cname; - return this; - } - - @Override - public SslConfigParametersDelegate getSslConfigParameters() { - return sslConfigParameters; - } - - public VaultParameters setSslConfigParameters(SslConfigParametersDelegate sslConfigParameters) { - this.sslConfigParameters = sslConfigParameters; - return this; - } - - @Override - public LaunchConfigParametersDelegate getLaunchConfigParameters() { - return launchConfigParameters; - } - - public VaultParameters setLaunchConfigParameters(LaunchConfigParametersDelegate launchConfigParameters) { - this.launchConfigParameters = launchConfigParameters; - return this; - } - - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public VaultParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/configuration/ConsulConfiguration.java b/src/main/java/com/nike/cerberus/domain/configuration/ConsulConfiguration.java deleted file mode 100644 index 8ca7a77d..00000000 --- a/src/main/java/com/nike/cerberus/domain/configuration/ConsulConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2016 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.configuration; - -import com.nike.cerberus.domain.template.ConsulConfigurationInput; - -/** - * Consul configuration POJO. - */ -public class ConsulConfiguration { - - private ConsulConfigurationInput input; - - private String clientConfiguration; - - private String serverConfiguration; - - public ConsulConfigurationInput getInput() { - return input; - } - - public ConsulConfiguration setInput(ConsulConfigurationInput input) { - this.input = input; - return this; - } - - public String getClientConfiguration() { - return clientConfiguration; - } - - public ConsulConfiguration setClientConfiguration(String clientConfiguration) { - this.clientConfiguration = clientConfiguration; - return this; - } - - public String getServerConfiguration() { - return serverConfiguration; - } - - public ConsulConfiguration setServerConfiguration(String serverConfiguration) { - this.serverConfiguration = serverConfiguration; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/configuration/GatewayConfiguration.java b/src/main/java/com/nike/cerberus/domain/configuration/GatewayConfiguration.java deleted file mode 100644 index c20f4e3e..00000000 --- a/src/main/java/com/nike/cerberus/domain/configuration/GatewayConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016 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.configuration; - -/** - * Gateway configuration POJO. - */ -public class GatewayConfiguration { - - private String siteConfig; - - private String globalConfig; - - public String getSiteConfig() { - return siteConfig; - } - - public GatewayConfiguration setSiteConfig(String siteConfig) { - this.siteConfig = siteConfig; - return this; - } - - public String getGlobalConfig() { - return globalConfig; - } - - public GatewayConfiguration setGlobalConfig(String globalConfig) { - this.globalConfig = globalConfig; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/configuration/VaultConfiguration.java b/src/main/java/com/nike/cerberus/domain/configuration/VaultConfiguration.java deleted file mode 100644 index a2387918..00000000 --- a/src/main/java/com/nike/cerberus/domain/configuration/VaultConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2016 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.configuration; - -/** - * Represents the generated Vault configuration. - */ -public class VaultConfiguration { - private String config; - - public VaultConfiguration setConfig(String config) { - this.config = config; - return this; - } - - public String getConfig() { - return config; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/CloudFrontLogProcessingLambdaConfig.java b/src/main/java/com/nike/cerberus/domain/environment/CloudFrontLogProcessingLambdaConfig.java deleted file mode 100644 index 9b85127c..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/CloudFrontLogProcessingLambdaConfig.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -public class CloudFrontLogProcessingLambdaConfig { - - private String manualWhiteListIpSetId; - - private String manualBlackListIpSetId; - - private String rateLimitAutoBlackListIpSetId; - - private Integer rateLimitViolationBlacklistPeriodInMinutes; - - private Integer requestPerMinuteLimit; - - private String slackWebHookUrl; - - private String slackIcon; - - private String googleAnalyticsId; - - public String getManualWhiteListIpSetId() { - return manualWhiteListIpSetId; - } - - public void setManualWhiteListIpSetId(String manualWhiteListIpSetId) { - this.manualWhiteListIpSetId = manualWhiteListIpSetId; - } - - public String getManualBlackListIpSetId() { - return manualBlackListIpSetId; - } - - public void setManualBlackListIpSetId(String manualBlackListIpSetId) { - this.manualBlackListIpSetId = manualBlackListIpSetId; - } - - public String getRateLimitAutoBlackListIpSetId() { - return rateLimitAutoBlackListIpSetId; - } - - public void setRateLimitAutoBlackListIpSetId(String rateLimitAutoBlackListIpSetId) { - this.rateLimitAutoBlackListIpSetId = rateLimitAutoBlackListIpSetId; - } - - public Integer getRateLimitViolationBlacklistPeriodInMinutes() { - return rateLimitViolationBlacklistPeriodInMinutes; - } - - public void setRateLimitViolationBlacklistPeriodInMinutes(Integer rateLimitViolationBlacklistPeriodInMinutes) { - this.rateLimitViolationBlacklistPeriodInMinutes = rateLimitViolationBlacklistPeriodInMinutes; - } - - public Integer getRequestPerMinuteLimit() { - return requestPerMinuteLimit; - } - - public void setRequestPerMinuteLimit(Integer requestPerMinuteLimit) { - this.requestPerMinuteLimit = requestPerMinuteLimit; - } - - public String getSlackWebHookUrl() { - return slackWebHookUrl; - } - - public void setSlackWebHookUrl(String slackWebHookUrl) { - this.slackWebHookUrl = slackWebHookUrl; - } - - public String getSlackIcon() { - return slackIcon; - } - - public void setSlackIcon(String slackIcon) { - this.slackIcon = slackIcon; - } - - public String getGoogleAnalyticsId() { - return googleAnalyticsId; - } - - public void setGoogleAnalyticsId(String googleAnalyticsId) { - this.googleAnalyticsId = googleAnalyticsId; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/ConsulSecrets.java b/src/main/java/com/nike/cerberus/domain/environment/ConsulSecrets.java deleted file mode 100644 index d7fff020..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/ConsulSecrets.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -/** - * Represents sensitive information used by Consul. - */ -public class ConsulSecrets { - - private String gossipEncryptionToken; - - private String aclMasterToken; - - private String vaultAclToken; - - public String getGossipEncryptionToken() { - return gossipEncryptionToken; - } - - public ConsulSecrets setGossipEncryptionToken(String gossipEncryptionToken) { - this.gossipEncryptionToken = gossipEncryptionToken; - return this; - } - - public String getAclMasterToken() { - return aclMasterToken; - } - - public ConsulSecrets setAclMasterToken(String aclMasterToken) { - this.aclMasterToken = aclMasterToken; - return this; - } - - public String getVaultAclToken() { - return vaultAclToken; - } - - public ConsulSecrets setVaultAclToken(String vaultAclToken) { - this.vaultAclToken = vaultAclToken; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java index 4257f4d3..dc1d5926 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Environment.java @@ -56,10 +56,7 @@ public Environment() { } serverCertificateIdMap = new HashMap<>(); - serverCertificateIdMap.put(StackName.GATEWAY, ""); serverCertificateIdMap.put(StackName.CMS, ""); - serverCertificateIdMap.put(StackName.VAULT, ""); - serverCertificateIdMap.put(StackName.CONSUL, ""); } public String getAz1() { diff --git a/src/main/java/com/nike/cerberus/domain/environment/LambdaName.java b/src/main/java/com/nike/cerberus/domain/environment/LambdaName.java deleted file mode 100644 index f5e25b12..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/LambdaName.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -/** - * Describes the lambdas that are part of Cerberus. - */ -public enum LambdaName { - WAF("waf", "lambda/waf.jar"), - CLOUD_FRONT_SG_GROUP_IP_SYNC("cf-sg-ip-sync", "lambda/cf-sg-ip-sync.zip"); - - private final String name; - - private final String bucketKey; - - LambdaName(final String name, final String bucketKey) { - this.name = name; - this.bucketKey = bucketKey; - } - - public String getName() { - return name; - } - - public String getBucketKey() { - return bucketKey; - } - - public static LambdaName fromName(final String name) { - for (LambdaName lambdaName : LambdaName.values()) { - if (lambdaName.getName().equals(name)) { - return lambdaName; - } - } - - throw new IllegalArgumentException("Unknown lambda name: " + name); - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/Secrets.java b/src/main/java/com/nike/cerberus/domain/environment/Secrets.java index 80994aa7..b4bc65d6 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Secrets.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Secrets.java @@ -21,21 +21,10 @@ */ public class Secrets { - private ConsulSecrets consul = new ConsulSecrets(); - private CmsSecrets cms = new CmsSecrets(); private VaultSecrets vault = new VaultSecrets(); - public ConsulSecrets getConsul() { - return consul; - } - - public Secrets setConsul(ConsulSecrets consul) { - this.consul = consul; - return this; - } - public CmsSecrets getCms() { return cms; } diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackName.java b/src/main/java/com/nike/cerberus/domain/environment/StackName.java index 1182227e..cc2fa12a 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/StackName.java +++ b/src/main/java/com/nike/cerberus/domain/environment/StackName.java @@ -21,15 +21,11 @@ */ public enum StackName { BASE("base"), - CONSUL("consul"), VAULT("vault"), CMS("cms"), GATEWAY("gateway"), @Deprecated - LAMBDA("lambda"), - @Deprecated - RDSBACKUP("rdsbackup"), // TODO: need to remove but casually deleting will cause JSON parse error - CLOUD_FRONT_IP_SYNCHRONIZER("cloud-front-ip-synchronizer"); + RDSBACKUP("rdsbackup"); // TODO: need to remove but casually deleting will cause JSON parse error private final String name; diff --git a/src/main/java/com/nike/cerberus/domain/input/Consul.java b/src/main/java/com/nike/cerberus/domain/input/Consul.java deleted file mode 100644 index d648d28f..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/Consul.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2016 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.input; - -/** - * Stores Consul-specific parameters parsed from YAML - */ -public class Consul extends CerberusStack { -} diff --git a/src/main/java/com/nike/cerberus/domain/input/Dashboard.java b/src/main/java/com/nike/cerberus/domain/input/Dashboard.java deleted file mode 100644 index 980082a2..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/Dashboard.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2016 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.input; - -/** - * Stores Dashboard-specific parameters parsed from YAML - */ -public class Dashboard { - - private String artifactUrl; - private String overrideArtifactUrl; - - public String getArtifactUrl() { - return artifactUrl; - } - - public void setArtifactUrl(String artifactUrl) { - this.artifactUrl = artifactUrl; - } - - public String getOverrideArtifactUrl() { - return overrideArtifactUrl; - } - - public void setOverrideArtifactUrl(String overrideArtifactUrl) { - this.overrideArtifactUrl = overrideArtifactUrl; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/input/EdgeSecurity.java b/src/main/java/com/nike/cerberus/domain/input/EdgeSecurity.java deleted file mode 100644 index 17a876d4..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/EdgeSecurity.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2016 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.input; - - -/** - * Stores CloudFront-specific parameters parsed from YAML - */ -public class EdgeSecurity { - - private String cloudfrontLambdaArtifactUrl; - private String cloudfrontSecurityGroupIpSyncLambdaArtifactUrl; - private String rateLimitPerMinute; - private String rateLimitViolationBlockPeriodInMinutes; - private String googleAnalyticsTrackingId; - private String slackWebHookUrl; - private String slackIcon; - - public String getCloudfrontLambdaArtifactUrl() { - return cloudfrontLambdaArtifactUrl; - } - - public void setCloudfrontLambdaArtifactUrl(String cloudfrontLambdaArtifactUrl) { - this.cloudfrontLambdaArtifactUrl = cloudfrontLambdaArtifactUrl; - } - - public String getCloudfrontSecurityGroupIpSyncLambdaArtifactUrl() { - return cloudfrontSecurityGroupIpSyncLambdaArtifactUrl; - } - - public void setCloudfrontSecurityGroupIpSyncLambdaArtifactUrl(String cloudfrontSecurityGroupIpSyncLambdaArtifactUrl) { - this.cloudfrontSecurityGroupIpSyncLambdaArtifactUrl = cloudfrontSecurityGroupIpSyncLambdaArtifactUrl; - } - - public String getRateLimitPerMinute() { - return rateLimitPerMinute; - } - - public void setRateLimitPerMinute(String rateLimitPerMinute) { - this.rateLimitPerMinute = rateLimitPerMinute; - } - - public String getRateLimitViolationBlockPeriodInMinutes() { - return rateLimitViolationBlockPeriodInMinutes; - } - - public void setRateLimitViolationBlockPeriodInMinutes(String rateLimitViolationBlockPeriodInMinutes) { - this.rateLimitViolationBlockPeriodInMinutes = rateLimitViolationBlockPeriodInMinutes; - } - - public String getGoogleAnalyticsTrackingId() { - return googleAnalyticsTrackingId; - } - - public void setGoogleAnalyticsTrackingId(String googleAnalyticsTrackingId) { - this.googleAnalyticsTrackingId = googleAnalyticsTrackingId; - } - - public String getSlackWebHookUrl() { - return slackWebHookUrl; - } - - public void setSlackWebHookUrl(String slackWebHookUrl) { - this.slackWebHookUrl = slackWebHookUrl; - } - - public String getSlackIcon() { - return slackIcon; - } - - public void setSlackIcon(String slackIcon) { - this.slackIcon = slackIcon; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java index b4eb2911..d4e7e270 100644 --- a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java +++ b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java @@ -34,12 +34,9 @@ public class EnvironmentConfig { private String hostname; private String hostedZoneId; private VpcAccessWhitelist vpcAccessWhitelist; - private Consul consul; private Vault vault; private ManagementService managementService; private Gateway gateway; - private Dashboard dashboard; - private EdgeSecurity edgeSecurity; public String getVersion() { return version; @@ -137,14 +134,6 @@ public void setVpcAccessWhitelist(VpcAccessWhitelist vpcAccessWhitelist) { this.vpcAccessWhitelist = vpcAccessWhitelist; } - public Consul getConsul() { - return consul; - } - - public void setConsul(Consul consul) { - this.consul = consul; - } - public Vault getVault() { return vault; } @@ -168,20 +157,4 @@ public Gateway getGateway() { public void setGateway(Gateway gateway) { this.gateway = gateway; } - - public Dashboard getDashboard() { - return dashboard; - } - - public void setDashboard(Dashboard dashboard) { - this.dashboard = dashboard; - } - - public EdgeSecurity getEdgeSecurity() { - return edgeSecurity; - } - - public void setEdgeSecurity(EdgeSecurity edgeSecurity) { - this.edgeSecurity = edgeSecurity; - } } diff --git a/src/main/java/com/nike/cerberus/domain/input/Gateway.java b/src/main/java/com/nike/cerberus/domain/input/Gateway.java index 6672af5a..b8af726b 100644 --- a/src/main/java/com/nike/cerberus/domain/input/Gateway.java +++ b/src/main/java/com/nike/cerberus/domain/input/Gateway.java @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2016 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.input; public class Gateway extends CerberusStack { diff --git a/src/main/java/com/nike/cerberus/domain/template/ConsulConfigurationInput.java b/src/main/java/com/nike/cerberus/domain/template/ConsulConfigurationInput.java deleted file mode 100644 index 85f24aa1..00000000 --- a/src/main/java/com/nike/cerberus/domain/template/ConsulConfigurationInput.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016 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.template; - -/** - * Consul configuration input. - */ -public class ConsulConfigurationInput { - - private String aclMasterToken; - - private String gossipEncryptionToken; - - private String datacenter; - - public String getAclMasterToken() { - return aclMasterToken; - } - - public ConsulConfigurationInput setAclMasterToken(String aclMasterToken) { - this.aclMasterToken = aclMasterToken; - return this; - } - - public String getGossipEncryptionToken() { - return gossipEncryptionToken; - } - - public ConsulConfigurationInput setGossipEncryptionToken(String gossipEncryptionToken) { - this.gossipEncryptionToken = gossipEncryptionToken; - return this; - } - - public String getDatacenter() { - return datacenter; - } - - public ConsulConfigurationInput setDatacenter(String datacenter) { - this.datacenter = datacenter; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/template/GatewayConfigurationInput.java b/src/main/java/com/nike/cerberus/domain/template/GatewayConfigurationInput.java deleted file mode 100644 index fafe3b66..00000000 --- a/src/main/java/com/nike/cerberus/domain/template/GatewayConfigurationInput.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2016 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.template; - -/** - * POJO for gateway configuration inputs. - */ -public class GatewayConfigurationInput { - - private String gatewayHost; - - private String dashboardHost; - - private String vaultHost; - - private String cmsHost; - - public String getGatewayHost() { - return gatewayHost; - } - - public GatewayConfigurationInput setGatewayHost(String gatewayHost) { - this.gatewayHost = gatewayHost; - return this; - } - - public String getDashboardHost() { - return dashboardHost; - } - - public GatewayConfigurationInput setDashboardHost(String dashboardHost) { - this.dashboardHost = dashboardHost; - return this; - } - - public String getVaultHost() { - return vaultHost; - } - - public GatewayConfigurationInput setVaultHost(String vaultHost) { - this.vaultHost = vaultHost; - return this; - } - - public String getCmsHost() { - return cmsHost; - } - - public GatewayConfigurationInput setCmsHost(String cmsHost) { - this.cmsHost = cmsHost; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/template/VaultAclInput.java b/src/main/java/com/nike/cerberus/domain/template/VaultAclInput.java deleted file mode 100644 index e32f1929..00000000 --- a/src/main/java/com/nike/cerberus/domain/template/VaultAclInput.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016 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.template; - -/** - * Input for the template generation of the Vault ACL entry. - */ -public class VaultAclInput { - - private String aclToken; - - public String getAclToken() { - return aclToken; - } - - public VaultAclInput setAclToken(String aclToken) { - this.aclToken = aclToken; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/template/VaultConfigurationInput.java b/src/main/java/com/nike/cerberus/domain/template/VaultConfigurationInput.java deleted file mode 100644 index 41d098f4..00000000 --- a/src/main/java/com/nike/cerberus/domain/template/VaultConfigurationInput.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016 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.template; - -/** - * Represents the inputs for the Vault configuration template. - */ -public class VaultConfigurationInput { - - private String datacenter; - - private String aclToken; - - public String getDatacenter() { - return datacenter; - } - - public VaultConfigurationInput setDatacenter(String datacenter) { - this.datacenter = datacenter; - return this; - } - - public String getAclToken() { - return aclToken; - } - - public VaultConfigurationInput setAclToken(String aclToken) { - this.aclToken = aclToken; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/vault/AuditBackend.java b/src/main/java/com/nike/cerberus/domain/vault/AuditBackend.java deleted file mode 100644 index d108e263..00000000 --- a/src/main/java/com/nike/cerberus/domain/vault/AuditBackend.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -/** - * Supported backends for audit in Vault. - */ -public enum AuditBackend { - FILE("file"), - SYSLOG("syslog"); - - private final String type; - - AuditBackend(final String type) { - this.type = type; - } - - public String getType() { - return type; - } -} diff --git a/src/main/java/com/nike/cerberus/generator/ConfigGenerationException.java b/src/main/java/com/nike/cerberus/generator/ConfigGenerationException.java deleted file mode 100644 index b6216362..00000000 --- a/src/main/java/com/nike/cerberus/generator/ConfigGenerationException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2016 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.generator; - -/** - * Error thrown when config generation fails. - */ -public class ConfigGenerationException extends RuntimeException { - public ConfigGenerationException(String message, Throwable throwable) { - super(message, throwable); - } -} diff --git a/src/main/java/com/nike/cerberus/generator/ConsulConfigGenerator.java b/src/main/java/com/nike/cerberus/generator/ConsulConfigGenerator.java deleted file mode 100644 index 191ae156..00000000 --- a/src/main/java/com/nike/cerberus/generator/ConsulConfigGenerator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2016 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.generator; - -import com.github.mustachejava.Mustache; -import com.github.mustachejava.MustacheFactory; -import com.google.inject.Inject; -import com.nike.cerberus.domain.configuration.ConsulConfiguration; -import com.nike.cerberus.domain.template.ConsulConfigurationInput; -import com.nike.cerberus.util.TokenSupplier; -import com.nike.cerberus.util.UuidSupplier; - -import java.io.IOException; -import java.io.StringWriter; - -/** - * Handles generating the Consul configuration files. - */ -public class ConsulConfigGenerator { - - private static final String serverTemplatePath = "templates/consul-server.json.mustache"; - - private static final String clientTemplatePath = "templates/consul-client.json.mustache"; - - private final UuidSupplier uuidSupplier; - - private final TokenSupplier tokenSupplier; - - private final MustacheFactory mustacheFactory; - - @Inject - public ConsulConfigGenerator(final UuidSupplier uuidSupplier, - final TokenSupplier tokenSupplier, - final MustacheFactory mustacheFactory) { - this.uuidSupplier = uuidSupplier; - this.tokenSupplier = tokenSupplier; - this.mustacheFactory = mustacheFactory; - } - - /** - * Generates a configuration object that contains the newly generated gossip encryption token, - * acl master token, and string representations of the full JSON configuration for Consul. - */ - public ConsulConfiguration generate(final String datacenter) { - final String aclMasterToken = uuidSupplier.get(); - final String gossipEncryptionToken = tokenSupplier.get(); - return generate(datacenter, aclMasterToken, gossipEncryptionToken); - } - - /** - * Generates a configuration object that contains the supplied gossip encryption token, - * acl master token, and string representations of the full JSON configuration for Consul. - */ - public ConsulConfiguration generate(final String datacenter, - final String aclMasterToken, - final String gossipEncryptionToken) { - final ConsulConfigurationInput input = new ConsulConfigurationInput() - .setAclMasterToken(aclMasterToken) - .setGossipEncryptionToken(gossipEncryptionToken) - .setDatacenter(datacenter); - final Mustache serverTemplateCompiler = mustacheFactory.compile(serverTemplatePath); - final Mustache clientTemplateCompiler = mustacheFactory.compile(clientTemplatePath); - final StringWriter serverConfigWriter = new StringWriter(); - final StringWriter clientConfigWriter = new StringWriter(); - - try { - serverTemplateCompiler.execute(serverConfigWriter, input).flush(); - clientTemplateCompiler.execute(clientConfigWriter, input).flush(); - } catch (IOException ioe) { - throw new ConfigGenerationException("Failed to generate Consul configuration!", ioe); - } - - return new ConsulConfiguration() - .setInput(input) - .setServerConfiguration(serverConfigWriter.toString()) - .setClientConfiguration(clientConfigWriter.toString()); - } -} diff --git a/src/main/java/com/nike/cerberus/generator/GatewayConfigGenerator.java b/src/main/java/com/nike/cerberus/generator/GatewayConfigGenerator.java deleted file mode 100644 index b90d7897..00000000 --- a/src/main/java/com/nike/cerberus/generator/GatewayConfigGenerator.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2016 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.generator; - -import com.github.mustachejava.Mustache; -import com.github.mustachejava.MustacheFactory; -import com.google.inject.Inject; -import com.nike.cerberus.domain.configuration.GatewayConfiguration; -import com.nike.cerberus.domain.template.GatewayConfigurationInput; - -import java.io.IOException; -import java.io.StringWriter; - -/** - * Handles generating the Gateway configuration files. - */ -public class GatewayConfigGenerator { - - private static final String siteConfigTemplatePath = "templates/site-gateway.conf.mustache"; - - private static final String globalConfigTemplatePath = "templates/nginx.conf.mustache"; - - private final MustacheFactory mustacheFactory; - - @Inject - public GatewayConfigGenerator(final MustacheFactory mustacheFactory) { - this.mustacheFactory = mustacheFactory; - } - - /** - * Generates a configuration object that contains the newly generated gossip encryption token, - * acl master token and a string representation of the full JSON configuration for Consul. - */ - public GatewayConfiguration generate(final GatewayConfigurationInput input) { - final Mustache siteConfigTemplateCompiler = mustacheFactory.compile(siteConfigTemplatePath); - final Mustache globalConfigTemplateCompiler = mustacheFactory.compile(globalConfigTemplatePath); - final StringWriter siteConfigWriter = new StringWriter(); - final StringWriter globalConfigWriter = new StringWriter(); - - try { - siteConfigTemplateCompiler.execute(siteConfigWriter, input).flush(); - globalConfigTemplateCompiler.execute(globalConfigWriter, input).flush(); - } catch (IOException ioe) { - throw new ConfigGenerationException("Failed to generate Consul configuration!", ioe); - } - - return new GatewayConfiguration() - .setSiteConfig(siteConfigWriter.toString()) - .setGlobalConfig(globalConfigWriter.toString()); - } -} diff --git a/src/main/java/com/nike/cerberus/generator/VaultAclGenerator.java b/src/main/java/com/nike/cerberus/generator/VaultAclGenerator.java deleted file mode 100644 index a1542110..00000000 --- a/src/main/java/com/nike/cerberus/generator/VaultAclGenerator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016 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.generator; - -import com.github.mustachejava.Mustache; -import com.github.mustachejava.MustacheFactory; -import com.google.inject.Inject; -import com.nike.cerberus.domain.configuration.VaultAclEntry; -import com.nike.cerberus.domain.template.VaultAclInput; -import com.nike.cerberus.util.UuidSupplier; - -import java.io.IOException; -import java.io.StringWriter; - -/** - * Generates the Vault ACL entry and token. - */ -public class VaultAclGenerator { - - private static final String vaultAclTemplatePath = "templates/vault-acl.json.mustache"; - - private final UuidSupplier uuidSupplier; - - private final MustacheFactory mustacheFactory; - - @Inject - public VaultAclGenerator(final UuidSupplier uuidSupplier, - final MustacheFactory mustacheFactory) { - this.uuidSupplier = uuidSupplier; - this.mustacheFactory = mustacheFactory; - } - - /** - * Generates the Vault ACL token and entry JSON. - * - * @return POJO with the token and JSON - */ - public VaultAclEntry generate() { - final String aclToken = uuidSupplier.get(); - final VaultAclInput input = new VaultAclInput().setAclToken(aclToken); - final Mustache vaultAclEntryCompiler = mustacheFactory.compile(vaultAclTemplatePath); - final StringWriter vaulAclEntryStringWriter = new StringWriter(); - - try { - vaultAclEntryCompiler.execute(vaulAclEntryStringWriter, input).flush(); - } catch (final IOException ioe) { - throw new ConfigGenerationException("Failed to generate Vault ACL entry!", ioe); - } - - return new VaultAclEntry().setAclToken(aclToken).setEntry(vaulAclEntryStringWriter.toString()); - } -} diff --git a/src/main/java/com/nike/cerberus/generator/VaultConfigGenerator.java b/src/main/java/com/nike/cerberus/generator/VaultConfigGenerator.java deleted file mode 100644 index df7cccad..00000000 --- a/src/main/java/com/nike/cerberus/generator/VaultConfigGenerator.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016 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.generator; - -import com.github.mustachejava.Mustache; -import com.github.mustachejava.MustacheFactory; -import com.google.inject.Inject; -import com.nike.cerberus.domain.configuration.VaultConfiguration; -import com.nike.cerberus.domain.template.VaultConfigurationInput; - -import java.io.IOException; -import java.io.StringWriter; - -/** - * Generates the Vault configuration. - */ -public class VaultConfigGenerator { - - private static final String vaultTemplatePath = "templates/vault.json.mustache"; - - private final MustacheFactory mustacheFactory; - - @Inject - public VaultConfigGenerator(final MustacheFactory mustacheFactory) { - this.mustacheFactory = mustacheFactory; - } - - public VaultConfiguration generate(final String consulDatacenter, final String vaultAclToken) { - final VaultConfigurationInput input = new VaultConfigurationInput() - .setDatacenter(consulDatacenter) - .setAclToken(vaultAclToken); - final Mustache templateCompiler = mustacheFactory.compile(vaultTemplatePath); - final StringWriter configWriter = new StringWriter(); - - try { - templateCompiler.execute(configWriter, input).flush(); - } catch (IOException ioe) { - throw new ConfigGenerationException("Failed to generate Vault configuration!", ioe); - } - - return new VaultConfiguration().setConfig(configWriter.toString()); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/consul/CreateConsulClusterOperation.java b/src/main/java/com/nike/cerberus/operation/consul/CreateConsulClusterOperation.java deleted file mode 100644 index a0c07339..00000000 --- a/src/main/java/com/nike/cerberus/operation/consul/CreateConsulClusterOperation.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2017 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.consul; - -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.consul.CreateConsulClusterCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.domain.cloudformation.ConsulParameters; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; -import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.Ec2UserDataService; -import com.nike.cerberus.service.AmiTagCheckService; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.util.UuidSupplier; -import org.apache.commons.lang3.StringUtils; -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.CF_OBJECT_MAPPER; - -/** - * Operation to create the Consul cluster. - */ -public class CreateConsulClusterOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final EnvironmentMetadata environmentMetadata; - - private final CloudFormationService cloudFormationService; - - private final Ec2UserDataService ec2UserDataService; - - private final AmiTagCheckService amiTagCheckService; - - private final UuidSupplier uuidSupplier; - - private final ConfigStore configStore; - - private final ObjectMapper cloudformationObjectMapper; - - @Inject - public CreateConsulClusterOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final Ec2UserDataService ec2UserDataService, - final AmiTagCheckService amiTagCheckService, - final UuidSupplier uuidSupplier, - final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { - this.environmentMetadata = environmentMetadata; - this.cloudFormationService = cloudFormationService; - this.ec2UserDataService = ec2UserDataService; - this.amiTagCheckService = amiTagCheckService; - this.uuidSupplier = uuidSupplier; - this.configStore = configStore; - this.cloudformationObjectMapper = cloudformationObjectMapper; - } - - @Override - public void run(final CreateConsulClusterCommand command) { - final String uniqueStackName = String.format("%s-%s", StackName.CONSUL.getName(), uuidSupplier.get()); - final BaseOutputs baseOutputs = configStore.getBaseStackOutputs(); - - // Make sure the given AmiId is for Consul component. Check if it contains required tag - if ( !command.isSkipAmiTagCheck() ) { - amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), StackName.CONSUL); - } - - final ConsulParameters consulParameters = new ConsulParameters() - .setInstanceProfileName(baseOutputs.getConsulInstanceProfileName()) - .setConsulClientSgId(baseOutputs.getConsulClientSgId()) - .setConsulServerSgId(baseOutputs.getConsulServerSgId()) - .setToolsIngressSgId(baseOutputs.getToolsIngressSgId()) - .setVpcId(baseOutputs.getVpcId()) - .setVpcSubnetIdForAz1(baseOutputs.getVpcSubnetIdForAz1()) - .setVpcSubnetIdForAz2(baseOutputs.getVpcSubnetIdForAz2()) - .setVpcSubnetIdForAz3(baseOutputs.getVpcSubnetIdForAz3()); - - consulParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); - consulParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); - consulParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - consulParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - consulParameters.getLaunchConfigParameters().setUserData( - ec2UserDataService.getUserData(StackName.CONSUL, command.getStackDelegate().getOwnerGroup())); - consulParameters.getLaunchConfigParameters().setDesiredInstances( - command.getStackDelegate().getDesiredInstances()); - consulParameters.getLaunchConfigParameters().setMinimumInstances( - command.getStackDelegate().getMinimumInstances()); - consulParameters.getLaunchConfigParameters().setMaximumInstances( - command.getStackDelegate().getMaximumInstances()); - - consulParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - consulParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); - consulParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); - - final TypeReference> typeReference = new TypeReference>() {}; - final Map parameters = cloudformationObjectMapper.convertValue(consulParameters, typeReference); - - final String stackId = cloudFormationService.createStack(cloudFormationService.getEnvStackName(uniqueStackName), - parameters, ConfigConstants.CONSUL_STACK_TEMPLATE_PATH, true); - - logger.info("Uploading data to the configuration bucket."); - configStore.storeStackId(StackName.CONSUL, stackId); - logger.info("Uploading complete."); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } - } - - @Override - public boolean isRunnable(final CreateConsulClusterCommand command) { - boolean isRunnable = true; - final String baseStackId = configStore.getStackId(StackName.BASE); - final boolean hasConsulConfig = configStore.hasConsulConfig(); - final boolean hasVaultAcl = configStore.hasVaultAclEntry(); - - if (StringUtils.isBlank(baseStackId) || !cloudFormationService.isStackPresent(baseStackId)) { - logger.error("No base stack defined for this environment!"); - isRunnable = false; - } - - if (!hasConsulConfig) { - logger.error("No configuration for Consul exists for this environment!"); - isRunnable = false; - } - - if (!hasVaultAcl) { - logger.error("Vault ACL has not been generated for this environment!"); - isRunnable = false; - } - - return isRunnable; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/consul/CreateConsulConfigOperation.java b/src/main/java/com/nike/cerberus/operation/consul/CreateConsulConfigOperation.java deleted file mode 100644 index 45508947..00000000 --- a/src/main/java/com/nike/cerberus/operation/consul/CreateConsulConfigOperation.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2016 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.consul; - -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.consul.CreateConsulConfigCommand; -import com.nike.cerberus.domain.configuration.ConsulConfiguration; -import com.nike.cerberus.generator.ConsulConfigGenerator; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; - -/** - * Generates the configuration files for Consul and uploads them to the config store. - */ -public class CreateConsulConfigOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConsulConfigGenerator consulConfigGenerator; - - private final ConfigStore configStore; - - @Inject - public CreateConsulConfigOperation(final ConsulConfigGenerator consulConfigGenerator, - final ConfigStore configStore) { - this.consulConfigGenerator = consulConfigGenerator; - this.configStore = configStore; - } - - @Override - public void run(final CreateConsulConfigCommand command) { - logger.info("Generating the Consul configuration."); - final ConsulConfiguration consulConfiguration = - consulConfigGenerator.generate(ConfigConstants.CONSUL_DATACENTER); - - logger.info("Uploading Consul configuration to the configuration bucket."); - configStore.storeConsulConfig(consulConfiguration); - - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final CreateConsulConfigCommand command) { - final boolean hasConsulConfig = configStore.hasConsulConfig(); - - if (hasConsulConfig) { - // you don't want to just overwrite the config here because it contains - // tokens that need to be maintained for the cluster to keep working - logger.error("Consul configuration present for specified environment, use the update command."); - } - - return !hasConsulConfig; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/consul/CreateVaultAclOperation.java b/src/main/java/com/nike/cerberus/operation/consul/CreateVaultAclOperation.java deleted file mode 100644 index ad773b03..00000000 --- a/src/main/java/com/nike/cerberus/operation/consul/CreateVaultAclOperation.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2016 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.consul; - -import com.nike.cerberus.command.consul.CreateVaultAclCommand; -import com.nike.cerberus.domain.configuration.VaultAclEntry; -import com.nike.cerberus.generator.VaultAclGenerator; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; - -/** - * Operation that will generate the Vault ACL token for accessing Consul and upload it to the config store. - */ -public class CreateVaultAclOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final VaultAclGenerator vaultAclGenerator; - - private final ConfigStore configStore; - - @Inject - public CreateVaultAclOperation(final VaultAclGenerator vaultAclGenerator, - final ConfigStore configStore) { - this.vaultAclGenerator = vaultAclGenerator; - this.configStore = configStore; - } - - @Override - public void run(final CreateVaultAclCommand command) { - logger.info("Generating the Vault ACL entry."); - final VaultAclEntry vaultAclEntry = - vaultAclGenerator.generate(); - - logger.info("Uploading the Vault ACL entry to the configuration bucket."); - configStore.storeVaultAclEntry(vaultAclEntry); - - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final CreateVaultAclCommand command) { - final boolean hasVaultAclEntry = configStore.hasVaultAclEntry(); - - if (hasVaultAclEntry) { - logger.error("Vault ACL entry is present for specified environment, use the update command."); - } - - return !hasVaultAclEntry; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/consul/UpdateConsulConfigOperation.java b/src/main/java/com/nike/cerberus/operation/consul/UpdateConsulConfigOperation.java deleted file mode 100644 index b465ad06..00000000 --- a/src/main/java/com/nike/cerberus/operation/consul/UpdateConsulConfigOperation.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2016 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.consul; - -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.consul.UpdateConsulConfigCommand; -import com.nike.cerberus.domain.configuration.ConsulConfiguration; -import com.nike.cerberus.generator.ConsulConfigGenerator; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; - -/** - * Generates the configuration files for Consul and uploads them to the config store. - */ -public class UpdateConsulConfigOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConsulConfigGenerator consulConfigGenerator; - - private final ConfigStore configStore; - - @Inject - public UpdateConsulConfigOperation(final ConsulConfigGenerator consulConfigGenerator, - final ConfigStore configStore) { - this.consulConfigGenerator = consulConfigGenerator; - this.configStore = configStore; - } - - @Override - public void run(final UpdateConsulConfigCommand command) { - logger.info("Regenerating Consul configuration"); - - String aclMasterToken = command.getAclMasterToken(); - if (aclMasterToken == null) { - logger.info("Regenerating Consul configuration: maintaining existing ACL Master Token"); - aclMasterToken = configStore.getAclMasterToken(); - } - String gossipEncryptionToken = command.getGossipEncryptionToken(); - if (gossipEncryptionToken == null) { - logger.info("Regenerating Consul configuration: maintaining existing Gossip Encryption Token"); - gossipEncryptionToken = configStore.getGossipEncryptionToken(); - } - - final ConsulConfiguration consulConfiguration = consulConfigGenerator.generate( - ConfigConstants.CONSUL_DATACENTER, - aclMasterToken, - gossipEncryptionToken - ); - - logger.info("Uploading Consul configuration to the configuration bucket."); - configStore.storeConsulConfig(consulConfiguration); - - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final UpdateConsulConfigCommand command) { - final boolean hasConsulConfig = configStore.hasConsulConfig(); - - if (!hasConsulConfig) { - logger.error("Consul configuration is NOT present for specified environment, use the create command."); - } - - return hasConsulConfig; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java index 94904631..3fc93dbb 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java @@ -40,11 +40,7 @@ public class RollingRebootWithHealthCheckOperation implements Operation HEALTH_CHECK_MAP = ImmutableMap.of( - StackName.CMS.getName(), "http://%s:8080/healthcheck", - StackName.GATEWAY.getName(), "https://%s:443/sys/health", - StackName.VAULT.getName(), "https://%s:8200/v1/sys/health?standbyok" - // TODO: upgrade Consul and use new healthcheck -// StackName.CONSUL.getName(), "http://%s:8580/v1/status/peers" + StackName.CMS.getName(), "http://%s:8080/healthcheck" ); private final static int DEFAULT_HTTP_TIMEOUT = 15; diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index feb6d8fc..a1d55635 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -26,12 +26,9 @@ import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.cloudformation.CmsParameters; -import com.nike.cerberus.domain.cloudformation.ConsulParameters; -import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.LaunchConfigParameters; import com.nike.cerberus.domain.cloudformation.SslConfigParametersDelegate; import com.nike.cerberus.domain.cloudformation.TagParameters; -import com.nike.cerberus.domain.cloudformation.VaultParameters; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; @@ -46,7 +43,6 @@ import javax.inject.Inject; import javax.inject.Named; import java.util.HashMap; -import java.util.List; import java.util.Map; import static com.amazonaws.services.cloudformation.model.StackStatus.*; @@ -87,17 +83,11 @@ public UpdateStackOperation(final ConfigStore configStore, this.amiTagCheckService = amiTagCheckService; stackParameterMap = new HashMap<>(); - stackParameterMap.put(StackName.CONSUL, ConsulParameters.class); - stackParameterMap.put(StackName.VAULT, VaultParameters.class); stackParameterMap.put(StackName.CMS, CmsParameters.class); - stackParameterMap.put(StackName.GATEWAY, GatewayParameters.class); stackTemplatePathMap = new HashMap<>(); stackTemplatePathMap.put(StackName.BASE, ConfigConstants.BASE_STACK_TEMPLATE_PATH); - stackTemplatePathMap.put(StackName.CONSUL, ConfigConstants.CONSUL_STACK_TEMPLATE_PATH); - stackTemplatePathMap.put(StackName.VAULT, ConfigConstants.VAULT_STACK_TEMPLATE_PATH); stackTemplatePathMap.put(StackName.CMS, ConfigConstants.CMS_STACK_TEMPLATE_PATH); - stackTemplatePathMap.put(StackName.GATEWAY, ConfigConstants.GATEWAY_STACK_TEMPLATE_PATH); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/dashboard/DashboardMetaDataProvider.java b/src/main/java/com/nike/cerberus/operation/dashboard/DashboardMetaDataProvider.java deleted file mode 100644 index 5ad67b01..00000000 --- a/src/main/java/com/nike/cerberus/operation/dashboard/DashboardMetaDataProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016 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.dashboard; - -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.transfer.ObjectMetadataProvider; - -import java.io.File; - -/** - * Metadata provider for dashboard assets - * - * We use this to set some headers we want s3 to send with our assets - */ -public class DashboardMetaDataProvider implements ObjectMetadataProvider { - private static final String CACHE_CONTROL = "Cache-Control"; - private static final String CONTENT_TYPE = "Content-Type"; - - @Override - public void provideObjectMetadata(File file, ObjectMetadata metadata) { - metadata.setHeader(CACHE_CONTROL, "private, no-cache, no-store, proxy-revalidate, no-transform"); - if (file.getName().endsWith(".html")) { - metadata.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); - } - } -} diff --git a/src/main/java/com/nike/cerberus/operation/dashboard/PublishDashboardOperation.java b/src/main/java/com/nike/cerberus/operation/dashboard/PublishDashboardOperation.java deleted file mode 100644 index 91d1f556..00000000 --- a/src/main/java/com/nike/cerberus/operation/dashboard/PublishDashboardOperation.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2016 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.dashboard; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.transfer.MultipleFileUpload; -import com.amazonaws.services.s3.transfer.TransferManager; -import com.google.common.io.Files; -import com.nike.cerberus.command.dashboard.PublishDashboardCommand; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.operation.InvalidEnvironmentConfigException; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.compress.archivers.ArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; -import org.apache.commons.compress.utils.IOUtils; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; - -/** - * Publishes the dashboard artifact to the s3 bucket hosting the dashboard application. - */ -public class PublishDashboardOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConfigStore configStore; - - private final TransferManager transferManager; - - private final AmazonS3 amazonS3; - - @Inject - public PublishDashboardOperation(final ConfigStore configStore, final AmazonS3 amazonS3) { - this.configStore = configStore; - this.transferManager = new TransferManager(amazonS3); - this.amazonS3 = amazonS3; - } - - @Override - public void run(final PublishDashboardCommand command) { - final URL artifactUrl = command.getArtifactUrl(); - final URL overrideArtifactUrl = command.getOverrideArtifactUrl(); - - final BaseOutputs outputParameters = configStore.getBaseStackOutputs(); - final String dashboardBucketName = outputParameters.getDashboardBucketName(); - - if (StringUtils.isBlank(dashboardBucketName)) { - final String errorMessage = "The specified environment isn't configured properly!"; - logger.error(errorMessage); - throw new IllegalStateException(errorMessage); - } - - initClient(dashboardBucketName); - - final File extractedDirectory = extractArtifact(artifactUrl, overrideArtifactUrl); - - try { - final MultipleFileUpload multipleFileUpload = - transferManager.uploadDirectory(dashboardBucketName, "", extractedDirectory, true, new DashboardMetaDataProvider()); - logger.info("Uploading dashboard files."); - multipleFileUpload.waitForCompletion(); - logger.info("Uploading complete."); - } catch (InterruptedException e) { - logger.error("Interrupted while waiting for upload to complete!", e); - } finally { - transferManager.shutdownNow(false); - } - } - - @Override - public boolean isRunnable(final PublishDashboardCommand command) { - try { - this.configStore.getBaseStackOutputs(); - return true; - } catch (IllegalStateException ise) { - logger.error("The dashboard bucket doesn't exist, yet! Aborting..."); - return false; - } - } - - private File extractArtifact(final URL artifactUrl, final URL helpArtifactUrl) { - final File extractionDirectory = Files.createTempDir(); - logger.info("Extracting artifact contents to {}", extractionDirectory.getAbsolutePath()); - - downloadAndExtract(artifactUrl, extractionDirectory); - - if (helpArtifactUrl != null) { - downloadAndExtract(helpArtifactUrl, extractionDirectory); - } - - return extractionDirectory; - } - - private void downloadAndExtract(URL artifactUrl, File extractionDirectory) { - ArchiveEntry entry; - TarArchiveInputStream tarArchiveInputStream = null; - try { - tarArchiveInputStream = new TarArchiveInputStream(new GzipCompressorInputStream(artifactUrl.openStream())); - entry = tarArchiveInputStream.getNextEntry(); - while (entry != null) { - String entryName = entry.getName(); - if (entry.getName().startsWith("./")) { - entryName = entry.getName().substring(1); - } - final File destPath = new File(extractionDirectory, entryName); - if (!entry.isDirectory()) { - final File fileParentDir = new File(destPath.getParent()); - if (!fileParentDir.exists()) { - FileUtils.forceMkdir(fileParentDir); - } - FileOutputStream fileOutputStream = null; - try { - fileOutputStream = new FileOutputStream(destPath); - IOUtils.copy(tarArchiveInputStream, fileOutputStream); - } finally { - IOUtils.closeQuietly(fileOutputStream); - } - } - entry = tarArchiveInputStream.getNextEntry(); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - IOUtils.closeQuietly(tarArchiveInputStream); - } - } - - private void initClient(final String dashboardBucketName) { - logger.debug("Initializing the S3 client"); - if (!amazonS3.doesBucketExist(dashboardBucketName)) { - final String errorMessage = String.format("The dashboard bucket specified doesn't exist! bucketName: %s", dashboardBucketName); - logger.error(errorMessage); - throw new InvalidEnvironmentConfigException(errorMessage); - } - } -} diff --git a/src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontLogProcessingLambdaConfigOperation.java b/src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontLogProcessingLambdaConfigOperation.java deleted file mode 100644 index e0fecc70..00000000 --- a/src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontLogProcessingLambdaConfigOperation.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.command.gateway.CreateCloudFrontLogProcessingLambdaConfigCommand; -import com.nike.cerberus.domain.cloudformation.GatewayOutputs; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.domain.environment.CloudFrontLogProcessingLambdaConfig; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.lang3.StringUtils; -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.CF_OBJECT_MAPPER; - -/** - * Operation for creating config needed to drive log processing lambda - */ -public class CreateCloudFrontLogProcessingLambdaConfigOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConfigStore configStore; - - private final CloudFormationService cloudFormationService; - - private final ObjectMapper cloudformationObjectMapper; - - @Inject - public CreateCloudFrontLogProcessingLambdaConfigOperation(final ConfigStore configStore, - final CloudFormationService cloudFormationService, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { - this.configStore = configStore; - this.cloudFormationService = cloudFormationService; - this.cloudformationObjectMapper = cloudformationObjectMapper; - } - - @Override - public void run(CreateCloudFrontLogProcessingLambdaConfigCommand command) { - final String gateWayStackId = configStore.getStackId(StackName.GATEWAY); - final Map stackOutputs = cloudFormationService.getStackOutputs(gateWayStackId); - final GatewayOutputs gatewayOutputs = cloudformationObjectMapper.convertValue(stackOutputs, GatewayOutputs.class); - - CloudFrontLogProcessingLambdaConfig config = new CloudFrontLogProcessingLambdaConfig(); - config.setManualBlackListIpSetId(gatewayOutputs.getManualBlockIPSetID()); - config.setManualWhiteListIpSetId(gatewayOutputs.getWhiteListIPSetID()); - config.setRateLimitAutoBlackListIpSetId(gatewayOutputs.getAutoBlockIPSetID()); - config.setRequestPerMinuteLimit(command.getRequestPerMinuteLimit()); - config.setRateLimitViolationBlacklistPeriodInMinutes(command.getRateLimitViolationBlacklistPeriodInMinutes()); - config.setSlackWebHookUrl(command.getSlackWebHookUrl()); - config.setSlackIcon(command.getSlackIcon()); - config.setGoogleAnalyticsId(command.getGoogleAnalyticsId()); - - configStore.saveCFLogProcessorLambdaConfig(config); - - logger.info("Config Created and Uploaded to S3"); - } - - @Override - public boolean isRunnable(CreateCloudFrontLogProcessingLambdaConfigCommand command) { - return StringUtils.isNotBlank(configStore.getStackId(StackName.GATEWAY)); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaOperation.java b/src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaOperation.java deleted file mode 100644 index 1490ac1a..00000000 --- a/src/main/java/com/nike/cerberus/operation/gateway/CreateCloudFrontSecurityGroupUpdaterLambdaOperation.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.cloudformation.AmazonCloudFormation; -import com.amazonaws.services.cloudformation.AmazonCloudFormationClient; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.amazonaws.services.lambda.AWSLambda; -import com.amazonaws.services.lambda.model.InvokeRequest; -import com.amazonaws.services.lambda.model.InvokeResult; -import com.amazonaws.services.lambda.model.LogType; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.sns.AmazonSNS; -import com.amazonaws.services.sns.AmazonSNSClient; -import com.amazonaws.services.sns.model.SubscribeRequest; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; -import com.google.inject.Inject; -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.gateway.CreateCloudFrontSecurityGroupUpdaterLambdaCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.CloudFrontIpSynchronizerOutputs; -import com.nike.cerberus.domain.cloudformation.CloudFrontIpSynchronizerParameters; -import com.nike.cerberus.domain.environment.LambdaName; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; -import com.nike.cerberus.service.CloudFormationService; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Named; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Base64; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; - -/** - * Command for creating lambda needed to sync AWS CloudFront IPs to an SG to limit ingress to only CF IPs - */ -public class CreateCloudFrontSecurityGroupUpdaterLambdaOperation implements Operation { - - private static final String AWS_IP_CHANGE_TOPIC_ARN = "arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged"; - private static final String BAD_HASH = "03a8199d0c03ddfec0e542f8bf650ee7"; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final CloudFormationService cloudFormationService; - private final AmazonSNS amazonSNS; - private final ObjectMapper cloudformationObjectMapper; - private final EnvironmentMetadata environmentMetadata; - private final AWSLambda awsLambda; - private final AmazonS3 amazonS3; - - @Inject - public CreateCloudFrontSecurityGroupUpdaterLambdaOperation(final CloudFormationService cloudFormationService, - final EnvironmentMetadata environmentMetadata, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper, - AWSLambda awsLambda, - AmazonS3 amazonS3) { - - this.cloudFormationService = cloudFormationService; - this.cloudformationObjectMapper = cloudformationObjectMapper; - this.environmentMetadata = environmentMetadata; - this.awsLambda = awsLambda; - this.amazonS3 = amazonS3; - - final Region region = Region.getRegion(Regions.US_EAST_1); - AmazonCloudFormation amazonCloudFormation = new AmazonCloudFormationClient(); - amazonCloudFormation.setRegion(region); - amazonSNS = new AmazonSNSClient(); - amazonSNS.setRegion(region); - } - - @Override - public void run(CreateCloudFrontSecurityGroupUpdaterLambdaCommand command) { - if (! cloudFormationService.isStackPresent(StackName.CLOUD_FRONT_IP_SYNCHRONIZER.getName())) { - createLambda(); - } - - Map outputs = cloudFormationService.getStackOutputs(StackName.CLOUD_FRONT_IP_SYNCHRONIZER.getName()); - CloudFrontIpSynchronizerOutputs cloudFrontIpSynchronizerOutputs = cloudformationObjectMapper - .convertValue(outputs, CloudFrontIpSynchronizerOutputs.class); - - final String arn = cloudFrontIpSynchronizerOutputs.getCloudFrontOriginElbSgIpSyncFunctionArn(); - - // subscribe, if already subscribed it doesn't make a new sub - amazonSNS.subscribe(new SubscribeRequest(AWS_IP_CHANGE_TOPIC_ARN, "lambda", arn)); - - // force any new ELBs that have the tags we care about to be updated to only allow ingress from CloudFront - forceLambdaToUpdateSgs(arn); - } - - /** - * Forces the lambda to run and sync the IPs for CloudFront to be white listed on the origin elb - */ - private void forceLambdaToUpdateSgs(String arn) { - String json; - try { - json = IOUtils.toString(this.getClass().getClassLoader().getResourceAsStream("aws-ip-space-change-sns-sample-event.json")); - } catch (IOException e) { - String msg = "Failed to load mock sns message, to force Lambda first run"; - logger.error(msg, e); - throw new RuntimeException(msg, e); - } - // this will fail - InvokeResult result = awsLambda.invoke(new InvokeRequest().withFunctionName(arn).withPayload(String.format(json, BAD_HASH)).withLogType(LogType.Tail)); - // collect the error so we can parse it for the latest hash - String log = new String(Base64.getDecoder().decode(result.getLogResult()), Charset.forName("UTF-8")); - Pattern pattern = Pattern.compile("MD5 Mismatch: got\\s(.*?)\\sexp.*?"); - Matcher matcher = pattern.matcher(log); - boolean matched = matcher.find(); - if (! matched) { - throw new RuntimeException("failed to extract hash from: " + log); - } - - String realHash = matcher.group(1); - result = awsLambda.invoke(new InvokeRequest().withFunctionName(arn).withPayload(String.format(json, realHash)).withLogType(LogType.Tail)); - - logger.info("Forcing the Lambda to run and update Security Groups"); - logger.info(new String(result.getPayload().array(), Charset.forName("UTF-8"))); - } - - private void createLambda() { - final CloudFrontIpSynchronizerParameters cloudFrontIpSynchronizerParameters = new CloudFrontIpSynchronizerParameters() - .setLambdaBucket(environmentMetadata.getBucketName()) - .setLambdaKey(LambdaName.CLOUD_FRONT_SG_GROUP_IP_SYNC.getBucketKey()); - - final TypeReference> typeReference = new TypeReference>() {}; - final Map parameters = cloudformationObjectMapper.convertValue(cloudFrontIpSynchronizerParameters, typeReference); - - final String stackId = cloudFormationService.createStack(StackName.CLOUD_FRONT_IP_SYNCHRONIZER.getName(), - parameters, ConfigConstants.CF_ELB_IP_SYNC_STACK_TEMPLATE_PATH, true); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } - } - - /** - * Run the command if the stack already exists from a different Cerberus env or if the publish artifact command - * has been run for this env, so that if were creating the stack the artifact we need exists in s3. - */ - @Override - public boolean isRunnable(CreateCloudFrontSecurityGroupUpdaterLambdaCommand command) { - boolean theStackAlreadyExists = cloudFormationService - .isStackPresent(StackName.CLOUD_FRONT_IP_SYNCHRONIZER.getName()); - - boolean theLambdaArtifactExistsInS3 = amazonS3.doesObjectExist(environmentMetadata.getBucketName(), - LambdaName.CLOUD_FRONT_SG_GROUP_IP_SYNC.getBucketKey()); - - if (theStackAlreadyExists || theLambdaArtifactExistsInS3) { - return true; - } else { - logger.error("failed to detect the lambda at {}/{} you must run the lambda publish command first", - environmentMetadata.getBucketName(), LambdaName.CLOUD_FRONT_SG_GROUP_IP_SYNC.getBucketKey()); - return false; - } - - } -} diff --git a/src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayClusterOperation.java b/src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayClusterOperation.java deleted file mode 100644 index 3893bd9c..00000000 --- a/src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayClusterOperation.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2017 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.gateway; - -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.gateway.CreateGatewayClusterCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.domain.cloudformation.GatewayParameters; -import com.nike.cerberus.domain.environment.LambdaName; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; -import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.Ec2UserDataService; -import com.nike.cerberus.service.AmiTagCheckService; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.util.UuidSupplier; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Named; -import java.util.Map; -import java.util.Optional; - -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; - -/** - * Operation for creating the gateway cluster. - */ -public class CreateGatewayClusterOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final EnvironmentMetadata environmentMetadata; - - private final CloudFormationService cloudFormationService; - - private final Ec2UserDataService ec2UserDataService; - - private final AmiTagCheckService amiTagCheckService; - - private final UuidSupplier uuidSupplier; - - private final ConfigStore configStore; - - private final ObjectMapper cloudformationObjectMapper; - - @Inject - public CreateGatewayClusterOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final Ec2UserDataService ec2UserDataService, - final AmiTagCheckService amiTagCheckService, - final UuidSupplier uuidSupplier, - final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { - this.environmentMetadata = environmentMetadata; - this.cloudFormationService = cloudFormationService; - this.ec2UserDataService = ec2UserDataService; - this.amiTagCheckService = amiTagCheckService; - this.uuidSupplier = uuidSupplier; - this.configStore = configStore; - this.cloudformationObjectMapper = cloudformationObjectMapper; - } - - @Override - public void run(final CreateGatewayClusterCommand command) { - final String uniqueStackName = String.format("%s-%s", StackName.GATEWAY.getName(), uuidSupplier.get()); - final BaseOutputs baseOutputs = configStore.getBaseStackOutputs(); - final Optional gatewayServerCertificateArn = configStore.getServerCertificateArn(StackName.GATEWAY); - final Optional gatewayServerCertificateId = configStore.getServerCertificateId(StackName.GATEWAY); - final Optional pubKey = configStore.getCertPart(StackName.GATEWAY, ConfigConstants.CERT_PART_PUBKEY); - - if (!gatewayServerCertificateArn.isPresent() || !pubKey.isPresent()) { - throw new IllegalStateException("Gateway certificate has not been uploaded!"); - } - - // Make sure the given AmiId is for Gateway component. Check if it contains required tag - if ( !command.isSkipAmiTagCheck() ) { - amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), StackName.GATEWAY); - } - - final GatewayParameters gatewayParameters = new GatewayParameters() - .setVpcId(baseOutputs.getVpcId()) - .setInstanceProfileName(baseOutputs.getGatewayInstanceProfileName()) - .setGatewayServerSgId(baseOutputs.getGatewayServerSgId()) - .setGatewayElbSgId(baseOutputs.getGatewayElbSgId()) - .setToolsIngressSgId(baseOutputs.getToolsIngressSgId()) - .setVpcSubnetIdForAz1(baseOutputs.getVpcSubnetIdForAz1()) - .setVpcSubnetIdForAz2(baseOutputs.getVpcSubnetIdForAz2()) - .setVpcSubnetIdForAz3(baseOutputs.getVpcSubnetIdForAz3()) - .setHostedZoneId(command.getHostedZoneId()) - .setHostname(command.getHostname()) - .setWafLambdaBucket(environmentMetadata.getBucketName()) - .setWafLambdaKey(LambdaName.WAF.getBucketKey()) - .setCloudFrontLogProcessorLambdaIamRoleArn(baseOutputs.getCloudFrontLogProcessorLambdaIamRoleArn()); - - gatewayParameters.getSslConfigParameters().setCertPublicKey(pubKey.get()); - gatewayParameters.getSslConfigParameters().setSslCertificateArn(gatewayServerCertificateArn.get()); - gatewayParameters.getSslConfigParameters().setSslCertificateId(gatewayServerCertificateId.get()); - - gatewayParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); - gatewayParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); - gatewayParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - gatewayParameters.getLaunchConfigParameters().setUserData( - ec2UserDataService.getUserData(StackName.GATEWAY, command.getStackDelegate().getOwnerGroup())); - gatewayParameters.getLaunchConfigParameters().setDesiredInstances( - command.getStackDelegate().getDesiredInstances()); - gatewayParameters.getLaunchConfigParameters().setMinimumInstances( - command.getStackDelegate().getMinimumInstances()); - gatewayParameters.getLaunchConfigParameters().setMaximumInstances( - command.getStackDelegate().getMaximumInstances()); - - gatewayParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - gatewayParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); - gatewayParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); - - final TypeReference> typeReference = new TypeReference>() {}; - final Map parameters = cloudformationObjectMapper.convertValue(gatewayParameters, typeReference); - - final String stackId = cloudFormationService.createStack(cloudFormationService.getEnvStackName(uniqueStackName), - parameters, ConfigConstants.GATEWAY_STACK_TEMPLATE_PATH, true); - - logger.info("Uploading data to the configuration bucket."); - configStore.storeStackId(StackName.GATEWAY, stackId); - logger.info("Uploading complete."); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } - } - - @Override - public boolean isRunnable(final CreateGatewayClusterCommand command) { - boolean isRunnable = true; - final String cmsStackId = configStore.getStackId(StackName.CMS); - final String certificateName = configStore.getServerCertificateName(StackName.GATEWAY); - final boolean hasGatewayConfig = configStore.hasGatewayConfig(); - - if (StringUtils.isBlank(cmsStackId) || !cloudFormationService.isStackPresent(cmsStackId)) { - logger.error("No CMS stack defined for this environment!"); - isRunnable = false; - } - - if (StringUtils.isBlank(certificateName)) { - logger.error("Certificate has not been uploaded for Gateway!"); - isRunnable = false; - } - - if (!hasGatewayConfig) { - logger.error("No configuration for Gateway exists for this environment!"); - isRunnable = false; - } - - return isRunnable; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayConfigOperation.java b/src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayConfigOperation.java deleted file mode 100644 index 25df3138..00000000 --- a/src/main/java/com/nike/cerberus/operation/gateway/CreateGatewayConfigOperation.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.nike.cerberus.command.gateway.CreateGatewayConfigCommand; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.domain.cloudformation.CmsParameters; -import com.nike.cerberus.domain.cloudformation.VaultParameters; -import com.nike.cerberus.domain.configuration.GatewayConfiguration; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.domain.template.GatewayConfigurationInput; -import com.nike.cerberus.generator.GatewayConfigGenerator; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; - -/** - * Operation to create the gateway configuration. - */ -public class CreateGatewayConfigOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final GatewayConfigGenerator gatewayConfigGenerator; - - private final ConfigStore configStore; - - @Inject - public CreateGatewayConfigOperation(final GatewayConfigGenerator gatewayConfigGenerator, - final ConfigStore configStore) { - this.gatewayConfigGenerator = gatewayConfigGenerator; - this.configStore = configStore; - } - - @Override - public void run(final CreateGatewayConfigCommand command) { - final BaseOutputs baseOutputs = configStore.getBaseStackOutputs(); - final CmsParameters cmsParameters = configStore.getCmsStackParamters(); - final VaultParameters vaultParameters = configStore.getVaultStackParamters(); - final GatewayConfigurationInput input = new GatewayConfigurationInput() - .setDashboardHost(baseOutputs.getDashboardBucketWebsiteUrl().replaceAll("http://|https://", "")) - .setCmsHost(cnameToHost(cmsParameters.getCname())) - .setVaultHost(cnameToHost(vaultParameters.getCname())) - .setGatewayHost(command.getHostname()); - - logger.info("Generating the Gateway configuration."); - final GatewayConfiguration gatewayConfiguration = gatewayConfigGenerator.generate(input); - - logger.info("Uploading the Gateway configuration to the configuration bucket."); - configStore.storeGatewayConfig(gatewayConfiguration); - - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final CreateGatewayConfigCommand command) { - boolean isRunnable = true; - final String cmsStackId = configStore.getStackId(StackName.CMS); - - if (StringUtils.isBlank(cmsStackId)) { - logger.error("No CMS stack present for the specified environment, please create that first."); - isRunnable = false; - } - - return isRunnable; - } - - /** - * Removes the final '.' from the CNAME. - * - * @param cname The cname to convert - * @return The host derived from the CNAME - */ - private String cnameToHost(final String cname) { - return cname.substring(0, cname.length() - 1); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/gateway/PublishLambdaOperation.java b/src/main/java/com/nike/cerberus/operation/gateway/PublishLambdaOperation.java deleted file mode 100644 index 8757feaf..00000000 --- a/src/main/java/com/nike/cerberus/operation/gateway/PublishLambdaOperation.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2016 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.gateway; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.transfer.TransferManager; -import com.amazonaws.services.s3.transfer.Upload; -import com.google.common.io.Files; -import com.nike.cerberus.command.gateway.PublishLambdaCommand; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.operation.InvalidEnvironmentConfigException; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; - -/** - * Publishes the lambda artifact to the Cerberus environment's configuration bucket. - */ -public class PublishLambdaOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConfigStore configStore; - - private final TransferManager transferManager; - - private final AmazonS3 amazonS3; - - @Inject - public PublishLambdaOperation(final ConfigStore configStore, final AmazonS3 amazonS3) { - this.configStore = configStore; - this.transferManager = new TransferManager(amazonS3); - this.amazonS3 = amazonS3; - } - - @Override - public void run(final PublishLambdaCommand command) { - final URL artifactUrl = command.getArtifactUrl(); - - final BaseOutputs outputParameters = configStore.getBaseStackOutputs(); - final String configBucketName = outputParameters.getConfigBucketName(); - - if (StringUtils.isBlank(configBucketName)) { - final String errorMessage = "The specified environment isn't configured properly!"; - logger.error(errorMessage); - throw new IllegalStateException(errorMessage); - } - - initClient(configBucketName); - final File filePath = downloadArtifact(artifactUrl); - - try { - final Upload upload = - transferManager.upload(configBucketName, command.getLambdaName().getBucketKey(), filePath); - logger.info("Uploading lambda artifact."); - upload.waitForCompletion(); - logger.info("Uploading complete."); - } catch (InterruptedException e) { - logger.error("Interrupted while waiting for upload to complete!", e); - } finally { - transferManager.shutdownNow(false); - } - } - - @Override - public boolean isRunnable(final PublishLambdaCommand command) { - try { - this.configStore.getBaseStackOutputs(); - return true; - } catch (IllegalStateException ise) { - logger.error("The config bucket doesn't exist, yet! Aborting..."); - return false; - } - } - - private File downloadArtifact(final URL artifactUrl) { - final File tempDir = Files.createTempDir(); - final String filePath = tempDir.getPath() + File.pathSeparator + "lambda.artifact"; - logger.debug("Downloading artifact to {}", tempDir.getAbsolutePath()); - - try { - ReadableByteChannel rbc = Channels.newChannel(artifactUrl.openStream()); - FileOutputStream fos = new FileOutputStream(filePath); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - } catch (IOException e) { - logger.error("Failed to download the lambda artifact!", e); - throw new IllegalStateException("Failed to download the lambda artifact.", e); - } - - return new File(filePath); - } - - private void initClient(final String configBucketName) { - logger.debug("Initializing the S3 client"); - if (!amazonS3.doesBucketExist(configBucketName)) { - final String errorMessage = String.format("The bucket specified doesn't exist! bucketName: %s", - configBucketName); - logger.error(errorMessage); - throw new InvalidEnvironmentConfigException(errorMessage); - } - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/CreateCmsVaultTokenOperation.java b/src/main/java/com/nike/cerberus/operation/vault/CreateCmsVaultTokenOperation.java deleted file mode 100644 index c8646e87..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/CreateCmsVaultTokenOperation.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.nike.cerberus.command.vault.CreateCmsVaultTokenCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.model.VaultAuthResponse; -import com.nike.vault.client.model.VaultTokenAuthRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.Optional; - -/** - * Operation to create the Vault token used by CMS. - */ -public class CreateCmsVaultTokenOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final String tokenDisplayName = "cerberus-management-service-token"; - - private final ConfigStore configStore; - - private final VaultAdminClientFactory vaultAdminClientFactory; - - @Inject - public CreateCmsVaultTokenOperation(final ConfigStore configStore, - final VaultAdminClientFactory vaultAdminClientFactory) { - this.configStore = configStore; - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(final CreateCmsVaultTokenCommand command) { - logger.info("Getting client for Vault leader."); - final Optional vaultAdminClient = vaultAdminClientFactory.getClientForLeader(); - - if (vaultAdminClient.isPresent()) { - logger.info("Creating CMS Vault token."); - final VaultAuthResponse authResponse = vaultAdminClient.get().createToken( - new VaultTokenAuthRequest().setDisplayName(tokenDisplayName)); - - logger.info("Uploading CMS Vault token to configuration bucket."); - configStore.storeCmsVaultToken(authResponse.getClientToken()); - - logger.info("Uploading complete."); - } else { - throw new IllegalStateException("Unable to determine Vault leader, aborting..."); - } - } - - @Override - public boolean isRunnable(final CreateCmsVaultTokenCommand command) { - - boolean isRunnable = true; - - final Optional cmsVaultToken = configStore.getCmsVaultToken(); - final boolean hasVaultInstances = vaultAdminClientFactory.hasVaultInstances(); - final Optional vaultAdminClient = vaultAdminClientFactory.getClientForLeader(); - - if (command.isForceOverwrite()) { - if (cmsVaultToken.isPresent()) { - logger.warn("WARNING: Since " + CreateCmsVaultTokenCommand.FORCE_OVERWRITE_LONG_ARG + " was used there may be an old CMS Vault token that needs to be manually revoked in Vault. " - + "The old token can be looked up in the previous version of secrets.json stored in S3 in order to revoke it."); - } else { - // We don't have to exit here except this probably means someone doesn't understand --force-overwrite - logger.error(CreateCmsVaultTokenCommand.FORCE_OVERWRITE_LONG_ARG + " was used but there is no existing CMS Vault token. Please re-run without " + CreateCmsVaultTokenCommand.FORCE_OVERWRITE_LONG_ARG + " option."); - isRunnable = false; - } - } else if (cmsVaultToken.isPresent()) { - logger.error("CMS Vault token is already present. Use update command to rotate it (or in a non-production system consider using the " + CreateCmsVaultTokenCommand.FORCE_OVERWRITE_LONG_ARG + " option)"); - isRunnable = false; - } - - if (!hasVaultInstances) { - logger.error("No vault instances present for this environment!"); - isRunnable = false; - } - - if (!vaultAdminClient.isPresent()) { - logger.error("No Vault instance is the current leader!"); - isRunnable = false; - } - - return isRunnable; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/CreateVaultClusterOperation.java b/src/main/java/com/nike/cerberus/operation/vault/CreateVaultClusterOperation.java deleted file mode 100644 index 6a1b696b..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/CreateVaultClusterOperation.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2017 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.vault; - -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.vault.CreateVaultClusterCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.domain.cloudformation.VaultParameters; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; -import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.Ec2UserDataService; -import com.nike.cerberus.service.AmiTagCheckService; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.util.UuidSupplier; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Named; -import java.util.Map; -import java.util.Optional; - -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; - -/** - * Operation to create the Vault cluster. - */ -public class CreateVaultClusterOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final EnvironmentMetadata environmentMetadata; - - private final CloudFormationService cloudFormationService; - - private final Ec2UserDataService ec2UserDataService; - - private final AmiTagCheckService amiTagCheckService; - - private final UuidSupplier uuidSupplier; - - private final ConfigStore configStore; - - private final ObjectMapper cloudformationObjectMapper; - - @Inject - public CreateVaultClusterOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final Ec2UserDataService ec2UserDataService, - final AmiTagCheckService amiTagCheckService, - final UuidSupplier uuidSupplier, - final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { - this.environmentMetadata = environmentMetadata; - this.cloudFormationService = cloudFormationService; - this.ec2UserDataService = ec2UserDataService; - this.amiTagCheckService = amiTagCheckService; - this.uuidSupplier = uuidSupplier; - this.configStore = configStore; - this.cloudformationObjectMapper = cloudformationObjectMapper; - } - - @Override - public void run(final CreateVaultClusterCommand command) { - final String uniqueStackName = String.format("%s-%s", StackName.VAULT.getName(), uuidSupplier.get()); - final BaseOutputs baseOutputs = configStore.getBaseStackOutputs(); - final Optional vaultServerCertificateArn = configStore.getServerCertificateArn(StackName.VAULT); - final Optional pubKey = configStore.getCertPart(StackName.VAULT, ConfigConstants.CERT_PART_PUBKEY); - final String internalElbCname = configStore.getInternalElbCname(StackName.VAULT); - - if (!vaultServerCertificateArn.isPresent() || !pubKey.isPresent()) { - throw new IllegalStateException("Vault server certificate has not been uploaded!"); - } - - // Make sure the given AmiId is for Vault component. Check if it contains required tag - if ( !command.isSkipAmiTagCheck() ) { - amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), StackName.VAULT); - } - - final VaultParameters vaultParameters = new VaultParameters() - .setInstanceProfileName(baseOutputs.getVaultInstanceProfileName()) - .setVaultClientSgId(baseOutputs.getVaultClientSgId()) - .setVaultServerSgId(baseOutputs.getVaultServerSgId()) - .setVaultServerElbSgId(baseOutputs.getVaultServerElbSgId()) - .setConsulClientSgId(baseOutputs.getConsulClientSgId()) - .setConsulServerSgId(baseOutputs.getConsulServerSgId()) - .setToolsIngressSgId(baseOutputs.getToolsIngressSgId()) - .setVpcId(baseOutputs.getVpcId()) - .setVpcSubnetIdForAz1(baseOutputs.getVpcSubnetIdForAz1()) - .setVpcSubnetIdForAz2(baseOutputs.getVpcSubnetIdForAz2()) - .setVpcSubnetIdForAz3(baseOutputs.getVpcSubnetIdForAz3()) - .setHostedZoneId(baseOutputs.getVpcHostedZoneId()) - .setCname(internalElbCname); - - vaultParameters.getSslConfigParameters().setCertPublicKey(pubKey.get()); - vaultParameters.getSslConfigParameters().setSslCertificateArn(vaultServerCertificateArn.get()); - - vaultParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); - vaultParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); - vaultParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - vaultParameters.getLaunchConfigParameters().setUserData( - ec2UserDataService.getUserData(StackName.VAULT, command.getStackDelegate().getOwnerGroup())); - vaultParameters.getLaunchConfigParameters().setDesiredInstances( - command.getStackDelegate().getDesiredInstances()); - vaultParameters.getLaunchConfigParameters().setMinimumInstances( - command.getStackDelegate().getMinimumInstances()); - vaultParameters.getLaunchConfigParameters().setMaximumInstances( - command.getStackDelegate().getMaximumInstances()); - - vaultParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - vaultParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); - vaultParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); - - final TypeReference> typeReference = new TypeReference>() {}; - final Map parameters = cloudformationObjectMapper.convertValue(vaultParameters, typeReference); - - final String stackId = cloudFormationService.createStack(cloudFormationService.getEnvStackName(uniqueStackName), - parameters, ConfigConstants.VAULT_STACK_TEMPLATE_PATH, true); - - logger.info("Uploading data to the configuration bucket."); - configStore.storeStackId(StackName.VAULT, stackId); - logger.info("Uploading complete."); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } - } - - @Override - public boolean isRunnable(final CreateVaultClusterCommand command) { - boolean isRunnable = true; - final String consulStackId = configStore.getStackId(StackName.CONSUL); - final String certificateName = configStore.getServerCertificateName(StackName.VAULT); - final boolean hasVaultConfig = configStore.hasVaultConfig(); - - if (StringUtils.isBlank(consulStackId) || !cloudFormationService.isStackPresent(consulStackId)) { - logger.error("No Consul stack defined for this environment!"); - isRunnable = false; - } - - if (StringUtils.isBlank(certificateName)) { - logger.error("Certificate has not been uploaded for Vault!"); - isRunnable = false; - } - - if (!hasVaultConfig) { - logger.error("No configuration for Vault exists for this environment!"); - isRunnable = false; - } - - return isRunnable; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/CreateVaultConfigOperation.java b/src/main/java/com/nike/cerberus/operation/vault/CreateVaultConfigOperation.java deleted file mode 100644 index cdd92f2f..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/CreateVaultConfigOperation.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.vault.CreateVaultConfigCommand; -import com.nike.cerberus.domain.configuration.VaultConfiguration; -import com.nike.cerberus.generator.VaultConfigGenerator; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; - -/** - * Operation that generates and uploads the Vault configuration. - */ -public class CreateVaultConfigOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final VaultConfigGenerator vaultConfigGenerator; - - private final ConfigStore configStore; - - @Inject - public CreateVaultConfigOperation(final VaultConfigGenerator vaultConfigGenerator, - final ConfigStore configStore) { - this.vaultConfigGenerator = vaultConfigGenerator; - this.configStore = configStore; - } - - @Override - public void run(final CreateVaultConfigCommand command) { - logger.info("Retrieving configuration data from the configuration bucket."); - final String vaultAclToken = configStore.getVaultAclToken(); - - logger.info("Generating the Vault configuration."); - final VaultConfiguration vaultConfiguration = - vaultConfigGenerator.generate(ConfigConstants.CONSUL_DATACENTER, vaultAclToken); - - logger.info("Uploading the Vault configuration to the configuration bucket."); - configStore.storeVaultConfig(vaultConfiguration); - - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final CreateVaultConfigCommand command) { - boolean isRunnable = true; - final String vaultAclToken = configStore.getVaultAclToken(); - - if (StringUtils.isBlank(vaultAclToken)) { - logger.error("No Vault ACL token present for Consul, please generate that first."); - isRunnable = false; - } - - return isRunnable; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/DisableAuditBackendOperation.java b/src/main/java/com/nike/cerberus/operation/vault/DisableAuditBackendOperation.java deleted file mode 100644 index 91b95a8e..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/DisableAuditBackendOperation.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.nike.cerberus.command.vault.DisableAuditBackendCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.Optional; - -/** - * Handles disabling the audit backend specified in the command. - */ -public class DisableAuditBackendOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final VaultAdminClientFactory vaultAdminClientFactory; - - @Inject - public DisableAuditBackendOperation(final VaultAdminClientFactory vaultAdminClientFactory) { - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(DisableAuditBackendCommand command) { - logger.info("Getting client for Vault leader."); - final Optional client = vaultAdminClientFactory.getClientForLeader(); - - if (client.isPresent()) { - logger.info("Disabling the specified audit backend in Vault."); - client.get().disableAuditBackend(command.getAuditBackend().getType()); - logger.info("Audit disabled."); - } else { - throw new IllegalStateException("Unable to determine Vault leader, aborting..."); - } - } - - @Override - public boolean isRunnable(final DisableAuditBackendCommand command) { - return vaultAdminClientFactory.getClientForLeader().isPresent(); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/EnableAuditBackendOperation.java b/src/main/java/com/nike/cerberus/operation/vault/EnableAuditBackendOperation.java deleted file mode 100644 index 49b874fe..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/EnableAuditBackendOperation.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.google.common.collect.Maps; -import com.nike.cerberus.command.vault.EnableAuditBackendCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.model.VaultEnableAuditBackendRequest; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.Map; -import java.util.Optional; - -/** - * Operation for enabling the audit backend for Vault. - */ -public class EnableAuditBackendOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final VaultAdminClientFactory vaultAdminClientFactory; - - @Inject - public EnableAuditBackendOperation(final VaultAdminClientFactory vaultAdminClientFactory) { - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(final EnableAuditBackendCommand command) { - logger.info("Getting client for Vault leader."); - final Optional client = vaultAdminClientFactory.getClientForLeader(); - - if (client.isPresent()) { - Map options = Maps.newHashMap(); - - switch (command.getAuditBackend()) { - case FILE: - options = getFileOptions(command.getFilePath().toString()); - break; - case SYSLOG: - options = getSyslogOptions(); - break; - } - - final VaultEnableAuditBackendRequest request = new VaultEnableAuditBackendRequest() - .setType(command.getAuditBackend().getType()) - .setDescription("Cerberus configured audit backend.") - .setOptions(options); - - logger.info("Enabling the audit backend in Vault."); - client.get().enableAuditBackend(command.getAuditBackend().getType(), request); - logger.info("Audit enabled."); - } else { - throw new IllegalStateException("Unable to determine Vault leader, aborting..."); - } - } - - @Override - public boolean isRunnable(final EnableAuditBackendCommand command) { - return vaultAdminClientFactory.getClientForLeader().isPresent(); - } - - private Map getFileOptions(final String filePath) { - if (StringUtils.isBlank(filePath)) { - throw new IllegalArgumentException("File path is required for the file audit backend type."); - } - - final Map options = Maps.newHashMap(); - options.put("file_path", filePath); - options.put("log_raw", "false"); - options.put("hmac_accessor", "true"); - return options; - } - - private Map getSyslogOptions() { - final Map options = Maps.newHashMap(); - options.put("facility", "AUTH"); - options.put("tag", "vault"); - options.put("log_raw", "false"); - options.put("hmac_accessor", "true"); - return options; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/InitVaultClusterOperation.java b/src/main/java/com/nike/cerberus/operation/vault/InitVaultClusterOperation.java deleted file mode 100644 index b9ee4a36..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/InitVaultClusterOperation.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.vault.InitVaultClusterCommand; -import com.nike.cerberus.domain.environment.StackName; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.model.VaultInitResponse; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.List; - -/** - * Operation for initializing each Vault instance in the cluster. - */ -public class InitVaultClusterOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConfigStore configStore; - - private final VaultAdminClientFactory vaultAdminClientFactory; - - @Inject - public InitVaultClusterOperation(final ConfigStore configStore, - final VaultAdminClientFactory vaultAdminClientFactory) { - this.configStore = configStore; - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(final InitVaultClusterCommand command) { - logger.info("If you just created the Vault cluster, this command will fail until the Vault cluster has a chance to initialize. If that happens, just try again in a few minutes."); - - logger.info("Getting clients for Vault instances."); - final List clients = vaultAdminClientFactory.getClientsForCluster(); - - logger.info("Calling init on one Vault instance."); - final VaultInitResponse initResponse = - clients.get(0).init(ConfigConstants.VAULT_SECRET_SHARES, ConfigConstants.VAULT_SECRET_THRESHOLD); - - logger.info("Uploading Vault root token and keys to configuration bucket."); - configStore.storeVaultRootToken(initResponse.getRootToken()); - configStore.storeVaultKeys(initResponse.getKeys()); - - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final InitVaultClusterCommand command) { - return StringUtils.isNotBlank(configStore.getStackId(StackName.VAULT)); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/LoadDefaultVaultPoliciesOperation.java b/src/main/java/com/nike/cerberus/operation/vault/LoadDefaultVaultPoliciesOperation.java deleted file mode 100644 index 16e25e5e..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/LoadDefaultVaultPoliciesOperation.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.command.vault.LoadDefaultVaultPoliciesCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.model.VaultPolicy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import java.util.Optional; - -/** - * Operation for loading the default Vault policies expected by Cerberus. - */ -public class LoadDefaultVaultPoliciesOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final String defaultPoliciesPath = "/vault/default-policies.json"; - - private final VaultAdminClientFactory vaultAdminClientFactory; - - @Inject - public LoadDefaultVaultPoliciesOperation(final VaultAdminClientFactory vaultAdminClientFactory) { - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(final LoadDefaultVaultPoliciesCommand command) { - logger.info("Getting client for Vault leader."); - final Optional leaderClient = vaultAdminClientFactory.getClientForLeader(); - - if (leaderClient.isPresent()) { - for (Map.Entry entry : getDefaultPolicies().entrySet()) { - logger.info("Loading policy, {}.", entry.getKey()); - leaderClient.get().putPolicy(entry.getKey(), entry.getValue()); - } - - logger.info("All default policies loaded."); - } else { - throw new IllegalStateException("Unable to determine Vault leader, aborting..."); - } - } - - @Override - public boolean isRunnable(final LoadDefaultVaultPoliciesCommand command) { - return vaultAdminClientFactory.getClientForLeader().isPresent(); - } - - private Map getDefaultPolicies() { - final ObjectMapper objectMapper = new ObjectMapper(); - final TypeReference> typeReference = new TypeReference>() {}; - - try (InputStream defaultPolicies = getClass().getResourceAsStream(defaultPoliciesPath)) { - return objectMapper.readValue(defaultPolicies, typeReference); - } catch (IOException e) { - throw new IllegalStateException("Unable to read the default policies document from the classpath!", e); - } - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/UnsealVaultClusterOperation.java b/src/main/java/com/nike/cerberus/operation/vault/UnsealVaultClusterOperation.java deleted file mode 100644 index f2f2a718..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/UnsealVaultClusterOperation.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.nike.cerberus.command.vault.UnsealVaultClusterCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.List; - -/** - * Operation to unseal the Vault cluster. - */ -public class UnsealVaultClusterOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConfigStore configStore; - - private final VaultAdminClientFactory vaultAdminClientFactory; - - @Inject - public UnsealVaultClusterOperation(final ConfigStore configStore, - final VaultAdminClientFactory vaultAdminClientFactory) { - this.configStore = configStore; - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(final UnsealVaultClusterCommand command) { - logger.info("Getting Vault keys from configuration bucket."); - final List vaultKeys = configStore.getVaultKeys(); - - logger.info("Getting clients for Vault instances."); - final List clients = vaultAdminClientFactory.getClientsForCluster(); - - logger.info("Unsealing each Vault instance."); - clients.forEach(client -> - vaultKeys.forEach(vaultKey -> client.unseal(vaultKey, false)) - ); - - logger.info("Unsealing complete."); - } - - @Override - public boolean isRunnable(final UnsealVaultClusterCommand command) { - return !vaultAdminClientFactory.getClientsForCluster().isEmpty(); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/vault/VaultHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/vault/VaultHealthCheckOperation.java deleted file mode 100644 index ccf6b941..00000000 --- a/src/main/java/com/nike/cerberus/operation/vault/VaultHealthCheckOperation.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.beust.jcommander.internal.Lists; -import com.github.tomaslanger.chalk.Ansi; -import com.github.tomaslanger.chalk.Chalk; -import com.google.inject.Inject; -import com.nike.cerberus.command.vault.VaultHealthCheckCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.vault.VaultAdminClientFactory; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.model.VaultHealthResponse; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class VaultHealthCheckOperation implements Operation { - - private final VaultAdminClientFactory vaultAdminClientFactory; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - @Inject - public VaultHealthCheckOperation(VaultAdminClientFactory vaultAdminClientFactory) { - this.vaultAdminClientFactory = vaultAdminClientFactory; - } - - @Override - public void run(VaultHealthCheckCommand command) { - List msgList = Lists.newLinkedList(); - boolean toggle = false; - do { - final List clients = vaultAdminClientFactory.getClientsForCluster(); - clients.forEach(client -> { - try { - VaultHealthResponse response = client.health(); - msgList.add(String.format("%s: Initialized: %s, Sealed: %s, Standby: %s", - client.getVaultUrl(), - colorify(response.isInitialized()), - colorify(response.isSealed()), - colorify(response.isStandby()))); - } catch (Throwable t) { - msgList.add(String.format("ERROR: %s", t.getMessage())); - } - }); - - String sym = toggle ? "*" : "+"; - sym = StringUtils.repeat(sym, 20); - - if (command.isPoll()) { - logger.info(Ansi.eraseScreen()); - } - - logger.info(String.format("%s - Vault Health Status - %s", sym, sym)); - msgList.forEach(logger::info); - msgList.clear(); - toggle = !toggle; - - try { - Thread.sleep(TimeUnit.SECONDS.toMillis(1)); - } catch (InterruptedException e) { - break; - } - } while (command.isPoll()); - } - - - - private String colorify(boolean status) { - return status ? Chalk.on(Boolean.TRUE.toString()).green().bold().toString() : - Chalk.on(Boolean.FALSE.toString()).red().bold().toString(); - } - - @Override - public boolean isRunnable(VaultHealthCheckCommand command) { - final boolean hasInstances = vaultAdminClientFactory.hasVaultInstances(); - - if (!hasInstances) { - logger.warn("No Vault instances detected for this environment, exiting..."); - } - - return hasInstances; - } -} diff --git a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java index e63ece91..5e8744e8 100644 --- a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java +++ b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java @@ -45,11 +45,7 @@ public AmiTagCheckService(final AmazonEC2 ec2Client) { this.ec2Client = ec2Client; stackAmiTagValueMap = new HashMap<>(); - stackAmiTagValueMap.put(StackName.CONSUL, ConfigConstants.CONSUL_AMI_TAG_VALUE); - stackAmiTagValueMap.put(StackName.VAULT, ConfigConstants.VAULT_AMI_TAG_VALUE); stackAmiTagValueMap.put(StackName.CMS, ConfigConstants.CMS_AMI_TAG_VALUE); - stackAmiTagValueMap.put(StackName.GATEWAY, ConfigConstants.GATEWAY_AMI_TAG_VALUE); - } /** diff --git a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java index 78eb7b69..0bf0d5ed 100644 --- a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java +++ b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java @@ -33,9 +33,6 @@ * Service for generating EC2 user data for Cerberus instances. */ public class Ec2UserDataService { - - private static final String nginxWriteResolverConfPath = "/write-nginx-resolver-conf"; - private final EnvironmentMetadata environmentMetadata; private final ConfigStore configStore; @@ -51,27 +48,12 @@ public String getUserData(final StackName stackName, final String ownerGroup) { switch (stackName) { case CMS: return getCmsUserData(ownerGroup); - case GATEWAY: - return getGatewayUserData(ownerGroup); - case VAULT: - case CONSUL: - return getConsulAndVaultUserData(stackName, ownerGroup); default: throw new IllegalArgumentException("The stack specified does not support user data. stack: " + stackName.getName()); } } - private String getGatewayUserData(final String ownerGroup) { - final Map userDataMap = Maps.newHashMap(); - addStandardEnvironmentVariables(userDataMap, StackName.GATEWAY.getName(), ownerGroup); - - final StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(writeExportEnvVars(userDataMap)); - stringBuilder.append(getFileContentsFromClasspath(nginxWriteResolverConfPath)); - return encodeUserData(stringBuilder.toString()); - } - private String getCmsUserData(final String ownerGroup) { final Map userDataMap = Maps.newHashMap(); addStandardEnvironmentVariables(userDataMap, StackName.CMS.getName(), ownerGroup); @@ -79,15 +61,6 @@ private String getCmsUserData(final String ownerGroup) { return encodeUserData(writeExportEnvVars(userDataMap)); } - private String getConsulAndVaultUserData(final StackName stackName, final String ownerGroup) { - final Map userDataMap = Maps.newHashMap(); - addStandardEnvironmentVariables(userDataMap, stackName.getName(), ownerGroup); - - userDataMap.put("CONSUL_DC", ConfigConstants.CONSUL_DATACENTER); - - return encodeUserData(writeExportEnvVars(userDataMap)); - } - private void addStandardEnvironmentVariables(final Map userDataMap, final String appName, final String ownerGroup) { @@ -117,28 +90,4 @@ private String writeExportEnvVars(Map userDataMap) { private String encodeUserData(String userData) { return Base64.getEncoder().encodeToString(userData.getBytes(Charset.forName("UTF-8"))); } - - /** - * Removes the final '.' from the CNAME. - * - * @param cname The cname to convert - * @return The host derived from the CNAME - */ - private String cnameToHost(final String cname) { - return cname.substring(0, cname.length() - 1); - } - - /** - * Reads the contents of a file on the classpath. - * - * @param path Path to the resource - * @return Contents of resource - */ - private String getFileContentsFromClasspath(final String path) { - try { - return IOUtils.toString(getClass().getResourceAsStream(path), ConfigConstants.DEFAULT_ENCODING); - } catch (IOException e) { - throw new IllegalStateException("Unable to read file that should be in classpath!", e); - } - } } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 3b48d26e..1e0f7411 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -33,18 +33,9 @@ import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.cloudformation.CmsOutputs; import com.nike.cerberus.domain.cloudformation.CmsParameters; -import com.nike.cerberus.domain.cloudformation.ConsulOutputs; -import com.nike.cerberus.domain.cloudformation.ConsulParameters; -import com.nike.cerberus.domain.cloudformation.GatewayOutputs; import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.VaultOutputs; -import com.nike.cerberus.domain.cloudformation.VaultParameters; -import com.nike.cerberus.domain.configuration.ConsulConfiguration; -import com.nike.cerberus.domain.configuration.GatewayConfiguration; -import com.nike.cerberus.domain.configuration.VaultAclEntry; -import com.nike.cerberus.domain.configuration.VaultConfiguration; import com.nike.cerberus.domain.environment.BackupRegionInfo; -import com.nike.cerberus.domain.environment.CloudFrontLogProcessingLambdaConfig; import com.nike.cerberus.domain.environment.Environment; import com.nike.cerberus.domain.environment.Secrets; import com.nike.cerberus.domain.environment.StackName; @@ -61,7 +52,6 @@ import javax.inject.Named; import java.io.IOException; import java.io.StringReader; -import java.security.SecureRandom; import java.util.List; import java.util.Map; import java.util.Optional; @@ -81,8 +71,6 @@ import static com.nike.cerberus.ConfigConstants.JDBC_USERNAME_KEY; import static com.nike.cerberus.ConfigConstants.ROOT_USER_ARN_KEY; import static com.nike.cerberus.ConfigConstants.SYSTEM_CONFIGURED_CMS_PROPERTIES; -import static com.nike.cerberus.ConfigConstants.VAULT_ADDR_KEY; -import static com.nike.cerberus.ConfigConstants.VAULT_TOKEN_KEY; import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; import static com.nike.cerberus.module.CerberusModule.CONFIG_OBJECT_MAPPER; @@ -238,137 +226,6 @@ public void storeConfigKeyId(final String configKeyId) { } } - /** - * Checks if the consul configuration is present in the config store. - * - * @return If present - */ - public boolean hasConsulConfig() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - - return StringUtils.isNotBlank(secrets.getConsul().getAclMasterToken()) - && StringUtils.isNotBlank(secrets.getConsul().getGossipEncryptionToken()); - } - } - - /** - * Uploads the configuration files and saves the secrets to the config store. - * - * @param consulConfiguration Consul configuration to upload - */ - public void storeConsulConfig(final ConsulConfiguration consulConfiguration) { - saveEncryptedObject(ConfigConstants.CONSUL_SERVER_CONFIG_FILE, consulConfiguration.getServerConfiguration()); - saveEncryptedObject(ConfigConstants.CONSUL_CLIENT_CONFIG_FILE, consulConfiguration.getClientConfiguration()); - - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getConsul().setAclMasterToken(consulConfiguration.getInput().getAclMasterToken()); - secrets.getConsul().setGossipEncryptionToken(consulConfiguration.getInput().getGossipEncryptionToken()); - saveSecretsData(secrets); - } - } - - /** - * Retrieves the AclMasterToken for Consul from the config store. - */ - public String getAclMasterToken() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return secrets.getConsul().getAclMasterToken(); - } - } - - /** - * Retrieves the GossipEncryptionToken for Consul from the config store. - */ - public String getGossipEncryptionToken() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return secrets.getConsul().getGossipEncryptionToken(); - } - } - - /** - * Checks if the Vault ACL entry JSON is present in the config store. - * - * @return If present - */ - public boolean hasVaultAclEntry() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - - return StringUtils.isNotBlank(secrets.getConsul().getVaultAclToken()); - } - } - - /** - * Uploads the Vault ACL entry JSON and saves the secrets to the config store. - * - * @param vaultAclEntry Vaul ACL entry to upload - */ - public void storeVaultAclEntry(final VaultAclEntry vaultAclEntry) { - saveEncryptedObject(ConfigConstants.VAULT_ACL_ENTRY_FILE, vaultAclEntry.getEntry()); - - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getConsul().setVaultAclToken(vaultAclEntry.getAclToken()); - saveSecretsData(secrets); - } - } - - /** - * Retrieves the Vault ACL token for accessing Consul from the config store. - * - * @return Vault ACL token - */ - public String getVaultAclToken() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return secrets.getConsul().getVaultAclToken(); - } - } - - /** - * Checks if the Vault configuration file has already been uploaded to the config store. - * - * @return If present - */ - public boolean hasVaultConfig() { - final Optional vaultConfig = getEncryptedObject(ConfigConstants.VAULT_CONFIG_FILE); - return vaultConfig.isPresent(); - } - - /** - * Uploads the Vault configuration to the config store. - * - * @param vaultConfiguration Vault configuration to upload. - */ - public void storeVaultConfig(VaultConfiguration vaultConfiguration) { - saveEncryptedObject(ConfigConstants.VAULT_CONFIG_FILE, vaultConfiguration.getConfig()); - } - - /** - * Checks if the Gateway configuration file has already been uploaded to the config store. - * - * @return If present - */ - public boolean hasGatewayConfig() { - final Optional gatewaySiteConfig = getEncryptedObject(ConfigConstants.GATEWAY_SITE_CONFIG_FILE); - final Optional gatewayGlobalConfig = getEncryptedObject(ConfigConstants.GATEWAY_GLOBAL_CONFIG_FILE); - return gatewaySiteConfig.isPresent() && gatewayGlobalConfig.isPresent(); - } - - /** - * Uploads the Gateway configuration to the config store. - * - * @param gatewayConfiguration Gateway configuration to upload. - */ - public void storeGatewayConfig(GatewayConfiguration gatewayConfiguration) { - saveEncryptedObject(ConfigConstants.GATEWAY_SITE_CONFIG_FILE, gatewayConfiguration.getSiteConfig()); - saveEncryptedObject(ConfigConstants.GATEWAY_GLOBAL_CONFIG_FILE, gatewayConfiguration.getGlobalConfig()); - } - /** * Retrieves the CMS adming group from the config store. * @@ -444,44 +301,6 @@ public void storeCmsVaultToken(final String cmsVaultToken) { } } - /** - * Store just the Vault unseal keys. - * - * @param vaultKeys Vault unseal keys - */ - public void storeVaultKeys(final List vaultKeys) { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getVault().setKeys(vaultKeys); - saveSecretsData(secrets); - } - } - - /** - * Gets the Vault keys from the config store. - * - * @return List of Vault keys - */ - public List getVaultKeys() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return secrets.getVault().getKeys(); - } - } - - /** - * Store just the Vault root token. - * - * @param vaultRootToken Vault root token - */ - public void storeVaultRootToken(final String vaultRootToken) { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getVault().setRootToken(vaultRootToken); - saveSecretsData(secrets); - } - } - /** * Gets the Vault root token from the config store. * @@ -580,8 +399,6 @@ private Properties generateBaseCmsSystemProperties() { final BaseOutputs baseOutputs = getBaseStackOutputs(); final BaseParameters baseParameters = getBaseStackParameters(); - final VaultParameters vaultParameters = getVaultStackParamters(); - final Optional cmsVaultToken = getCmsVaultToken(); final Optional cmsDatabasePassword = getCmsDatabasePassword(); final GetCallerIdentityResult callerIdentity = securityTokenService.getCallerIdentity( @@ -589,8 +406,6 @@ private Properties generateBaseCmsSystemProperties() { final String rootUserArn = String.format("arn:aws:iam::%s:root", callerIdentity.getAccount()); final Properties properties = new Properties(); - properties.put(VAULT_ADDR_KEY, String.format("https://%s", cnameToHost(vaultParameters.getCname()))); - properties.put(VAULT_TOKEN_KEY, cmsVaultToken.get()); properties.put(ROOT_USER_ARN_KEY, rootUserArn); properties.put(ADMIN_ROLE_ARN_KEY, baseParameters.getAccountAdminArn()); properties.put(CMS_ROLE_ARN_KEY, baseOutputs.getCmsIamRoleArn()); @@ -681,33 +496,6 @@ public BaseOutputs getBaseStackOutputs() { return getStackOutputs(StackName.BASE, BaseOutputs.class); } - /** - * Get the Consul stack parameters. - * - * @return Consul parameters - */ - public ConsulParameters getConsulStackParameters() { - return getStackParameters(StackName.CONSUL, ConsulParameters.class); - } - - /** - * Get the Consul stack outputs. - * - * @return Consul outputs - */ - public ConsulOutputs getConsulStackOutputs() { - return getStackOutputs(StackName.CONSUL, ConsulOutputs.class); - } - - /** - * Get the Vault stack parameters. - * - * @return Vault parameters - */ - public VaultParameters getVaultStackParamters() { - return getStackParameters(StackName.VAULT, VaultParameters.class); - } - /** * Get the Vault stack outputs. * @@ -744,15 +532,6 @@ public GatewayParameters getGatewayStackParamters() { return getStackParameters(StackName.GATEWAY, GatewayParameters.class); } - /** - * Get the Gateway stack outputs. - * - * @return Gateway outputs - */ - public GatewayOutputs getGatewayStackOutputs() { - return getStackOutputs(StackName.GATEWAY, GatewayOutputs.class); - } - /** * Get the stack outputs for a specific stack name. * @@ -895,15 +674,6 @@ private void saveEnvironmentData(final Environment environment) { } } - public void saveCFLogProcessorLambdaConfig(final CloudFrontLogProcessingLambdaConfig config) { - try { - final String configData = configObjectMapper.writeValueAsString(config); - saveObject(ConfigConstants.CF_LOG_PROCESSOR_LAMBDA_CONFIG_FILE, configData); - } catch (JsonProcessingException e) { - throw new RuntimeException("Unable to convert the config data to JSON. Aborting save...", e); - } - } - private void saveObject(final String path, final String value) { initConfigStoreService(); diff --git a/src/main/resources/example-standup.yaml b/src/main/resources/example-standup.yaml index 3d4ccd80..a6b7805a 100644 --- a/src/main/resources/example-standup.yaml +++ b/src/main/resources/example-standup.yaml @@ -44,25 +44,6 @@ vpc-access-whitelist: cidrs: - 50.39.106.150/32 -# Consul-specific parameters -consul: - cert-path: /path/to/example/certs/ # directory containing pubkey.pem, key.pem, cert.pem, and ca.pem - ami-id: ami-1111 - instance-size: m3.medium - key-pair-name: example-key - # optional: desired, min, and max instance values - desired-instances: 3 - max-instances: 4 - min-instances: 3 - -# Vault-specific parameters -vault: - cert-path: /path/to/example/certs/ - ami-id: ami-2222 - instance-size: m3.medium - key-pair-name: example-key - # optional: desired, min, and max instance values - # Cerberus Management Service-specific parameters management-service: cert-path: /path/to/example/certs/ @@ -80,36 +61,4 @@ management-service: - auth.connector.onelogin.client_id=123 - auth.connector.onelogin.client_secret=312 - auth.connector.onelogin.subdomain=example # example.onelogin.com ~> 'example' - # optional: desired, min, and max instance values - - -gateway: - cert-path: /path/to/example/certs/ - ami-id: ami-4444 - instance-size: m3.medium - key-pair-name: cerberus-test - # optional: desired, min, and max instance values - -dashboard: - # The url of the dashboard artifact, probably the newest release on the dashboard Github release page - artifact-url: https://github.com/Nike-Inc/cerberus-management-dashboard/releases/download/v0.8.0/cerberus-dashboard.tar.gz - # The secondary artifact that will get merged into the above artifact before getting uploaded to s3, we use this for custom help page. - override-artifact-url: https://someplace.com/where/you/want/to/store/this.tar.gz - -# By default The gateway ELB for Cerberus is exposed to the public internet on port 443 -# We configure CloudFront and WAF to enable manual black and white lists, and auto blacklisting -edge-security: - # The artifact url for the CloudFront Lambda that process CloudFront Logs to enable things like rate limiting and KPI reporting - cloudfront-lambda-artifact-url: https://github.com/Nike-Inc/cerberus-cloudfront-lambda/releases/download/v1.1.0/cerberus-cloudfront-lambda.jar - # The artifact url to the lambda that ensures only CloudFront IPs are white listed to talk to the origin ELB - cloudfront-security-group-ip-sync-lambda-artifact-url: https://github.com/Nike-Inc/cerberus-lifecycle-cli/raw/master/update_security_groups.zip - # The maximum number of requests from an IP per minute that can flow through the gateway before being auto blocked. - rate-limit-per-minute: 100 - # Time in minutes to block an ip that violates the rate limit. - rate-limit-violation-block-period-in-minutes: 60 - # [Optional delete if you do not need] If you provide a Google Analytics tracking id, the KPI processor will send events to that GA Account. - google-analytics-tracking-id: abc123 - # [Optional delete if you do not need] If you provide a web hook url for slack the cloudfront lambda will send messages on errors and summary info. - slack-web-hook-url: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX - # [Optional delete if you do not need] If you provide an emoji or an icon url the lambda will use it when sending messages. - slack-icon: https://raw.githubusercontent.com/Nike-Inc/cerberus/master/images/cerberus-github-logo-black-filled-circle%40500px.png \ No newline at end of file + # optional -P parameters: desired, min, and max instance values diff --git a/src/main/resources/templates/consul-client.json.mustache b/src/main/resources/templates/consul-client.json.mustache deleted file mode 100644 index 1a733390..00000000 --- a/src/main/resources/templates/consul-client.json.mustache +++ /dev/null @@ -1,33 +0,0 @@ -{ - "data_dir": "/var/consul/data", - "ui_dir": "/var/consul/webui", - "log_level": "info", - "leave_on_terminate": true, - "client_addr": "0.0.0.0", - "disable_update_check": true, - "server": false, - "encrypt": "{{{gossipEncryptionToken}}}", - "ports": { - "dns": -1, - "http": 8580, - "https": 8500, - "rpc": 8400, - "serf_lan": 8301, - "serf_wan": 8302, - "server": 8300 - }, - "verify_outgoing": true, - "verify_incoming": true, - "ca_file": "/etc/consul/ca.pem", - "cert_file": "/etc/consul/cert.pem", - "key_file": "/etc/consul/key.pem", - "disable_remote_exec": true, - "acl_datacenter": "{{{datacenter}}}", - "acl_master_token": "{{{aclMasterToken}}}", - "acl_default_policy": "deny", - "acl_down_policy": "deny", - "acl_enforce_version_8": false, - "reconnect_timeout": "16h", - "reconnect_timeout_wan": "16h", - "raft_protocol": 3 -} \ No newline at end of file diff --git a/src/main/resources/templates/consul-server.json.mustache b/src/main/resources/templates/consul-server.json.mustache deleted file mode 100644 index ea52c16d..00000000 --- a/src/main/resources/templates/consul-server.json.mustache +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data_dir": "/var/consul/data", - "ui_dir": "/var/consul/webui", - "log_level": "info", - "leave_on_terminate": true, - "skip_leave_on_interrupt": false, - "client_addr": "0.0.0.0", - "disable_update_check": true, - "bootstrap_expect": 3, - "retry_interval": "15s", - "server": true, - "encrypt": "{{{gossipEncryptionToken}}}", - "ports": { - "dns": -1, - "http": 8580, - "https": 8500, - "rpc": 8400, - "serf_lan": 8301, - "serf_wan": 8302, - "server": 8300 - }, - "performance": { - "raft_multiplier": 1 - }, - "verify_outgoing": true, - "verify_incoming": true, - "ca_file": "/etc/consul/ca.pem", - "cert_file": "/etc/consul/cert.pem", - "key_file": "/etc/consul/key.pem", - "disable_remote_exec": true, - "acl_datacenter": "{{{datacenter}}}", - "acl_master_token": "{{{aclMasterToken}}}", - "acl_default_policy": "deny", - "acl_down_policy": "deny", - "acl_enforce_version_8": false, - "reconnect_timeout": "16h", - "reconnect_timeout_wan": "16h", - "raft_protocol": 3 -} diff --git a/src/main/resources/templates/nginx.conf.mustache b/src/main/resources/templates/nginx.conf.mustache deleted file mode 100644 index 4d4c21ab..00000000 --- a/src/main/resources/templates/nginx.conf.mustache +++ /dev/null @@ -1,69 +0,0 @@ -user www-data; -worker_processes auto; -pid /run/nginx.pid; - -events { - worker_connections 20000; -} - -http { - - ## - # Basic Settings - ## - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - - client_body_buffer_size 10K; - client_header_buffer_size 1k; - client_max_body_size 8m; - large_client_header_buffers 2 1k; - client_body_timeout 12; - client_header_timeout 12; - keepalive_timeout 15; - send_timeout 10; - types_hash_max_size 2048; - - server_tokens off; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - ## - # SSL Settings - ## - - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - - ## - # Logging Settings - ## - - log_format access_format '[$time_iso8601] source_ip="$remote_addr" request="$request" request_size="$request_length" request_time="$request_time" response_code="$status" response_size="$bytes_sent" http_referer="$http_referer" http_user_agent="$http_user_agent"'; - - access_log /var/log/nginx/access.log access_format; - error_log /var/log/nginx/error.log info; - - ## - # Gzip Settings - ## - - gzip on; - gzip_comp_level 2; - gzip_min_length 1000; - gzip_proxied expired no-cache no-store private auth; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; - gzip_disable "msie6"; - - ## - # Virtual Host Configs - ## - - include /etc/nginx/conf.d/*.conf; - include /etc/nginx/sites-enabled/*; -} \ No newline at end of file diff --git a/src/main/resources/templates/site-gateway.conf.mustache b/src/main/resources/templates/site-gateway.conf.mustache deleted file mode 100644 index 917226c4..00000000 --- a/src/main/resources/templates/site-gateway.conf.mustache +++ /dev/null @@ -1,76 +0,0 @@ -server { - listen 443 default_server; - - ssl on; - ssl_certificate /etc/ssl/gateway.pem; - ssl_certificate_key /etc/ssl/gateway.key; - ssl_protocols TLSv1.2; - - server_name {{{gatewayHost}}}; - - proxy_redirect off; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - ### - # - # Request Routing. ORDER IS IMPORTANT. - # - ### - - # Expose Nginx status on loopback. - location /sys/status { - stub_status on; - access_log off; - allow 127.0.0.1; - deny all; - } - - # Simple health response - location = /sys/health { - access_log off; - add_header Content-Type text/plain; - return 200 "Gateway is Healthy"; - } - - location /robots.txt { - add_header Content-Type text/plain; - return 200 "User-agent: *\nDisallow: /\n"; - } - - # Dashboard - location /dashboard { - add_header X-Frame-Options deny; - add_header Content-Security-Policy "frame-ancestors 'none'"; - add_header X-Content-Security-Policy "frame-ancestors 'none'"; - set $dashboard "{{{dashboardHost}}}"; - rewrite ^/dashboard/(.*) /$1 break; - proxy_set_header Host $dashboard; - proxy_pass http://$dashboard; - } - - # Allow lookup-self call for a token to go straight to Vault - location ~ /v1/auth/token/lookup-self { - set $vault "{{{vaultHost}}}"; - proxy_pass https://$vault; - } - - # Everything else under /v1/secret goes to Vault - location ~ /v1/secret { - set $vault "{{{vaultHost}}}"; - proxy_pass https://$vault; - } - - # Everything goes to CMS - location ~ /v\d { - set $cms "{{{cmsHost}}}"; - proxy_pass https://$cms; - } - - # Redirect root to the dashboard - location / { - return 301 https://{{{gatewayHost}}}/dashboard/; - } -} diff --git a/src/main/resources/templates/vault-acl.json.mustache b/src/main/resources/templates/vault-acl.json.mustache deleted file mode 100644 index d685808c..00000000 --- a/src/main/resources/templates/vault-acl.json.mustache +++ /dev/null @@ -1,6 +0,0 @@ -{ - "ID": "{{{aclToken}}}", - "Name": "vault", - "Type": "client", - "Rules": "key \"vault\" {policy = \"write\"}" -} \ No newline at end of file diff --git a/src/main/resources/templates/vault.json.mustache b/src/main/resources/templates/vault.json.mustache deleted file mode 100644 index 8af3efcb..00000000 --- a/src/main/resources/templates/vault.json.mustache +++ /dev/null @@ -1,24 +0,0 @@ -{ - "listener": { - "tcp": { - "address": "0.0.0.0:8200", - "tls_cert_file": "/etc/vault/cert.pem", - "tls_key_file": "/etc/vault/key.pem", - "tls_min_version": "tls12" - } - }, - "backend": { - "consul": { - "scheme": "https", - "address": "127.0.0.1:8500", - "path": "vault", - "datacenter": "{{{datacenter}}}", - "tls_ca_file": "/etc/consul/ca.pem", - "tls_cert_file": "/etc/consul/cert.pem", - "tls_key_file": "/etc/consul/key.pem", - "tls_skip_verify": "true", - "token": "{{{aclToken}}}" - } - }, - "default_lease_ttl": "1h", -} \ No newline at end of file diff --git a/src/main/resources/vault/default-policies.json b/src/main/resources/vault/default-policies.json deleted file mode 100644 index ed7c47c4..00000000 --- a/src/main/resources/vault/default-policies.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "lookup-self": { - "rules": "path \"auth/token/lookup\" {policy = \"read\"}" - } -} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index 9c8b380d..ad717eeb 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -22,16 +22,10 @@ import com.nike.cerberus.command.StackDelegate; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; -import com.nike.cerberus.command.consul.CreateConsulClusterCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.command.dashboard.PublishDashboardCommand; -import com.nike.cerberus.command.gateway.CreateCloudFrontLogProcessingLambdaConfigCommand; -import com.nike.cerberus.command.gateway.CreateGatewayClusterCommand; -import com.nike.cerberus.command.gateway.PublishLambdaCommand; -import com.nike.cerberus.command.vault.CreateVaultClusterCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; import org.junit.Before; @@ -130,12 +124,12 @@ public void test_upload_cert_without_overwrite() { public void test_upload_cert_with_overwrite() { String commandName = UploadCertFilesCommand.COMMAND_NAME; - String[] userInput = {"-f", "/path/to/environment.yaml", commandName, "--stack-name", "consul", "--overwrite"}; + String[] userInput = {"-f", "/path/to/environment.yaml", commandName, "--stack-name", "cms", "--overwrite"}; String[] expected = { "-f", "/path/to/environment.yaml", commandName, - "--stack-name", "consul", + "--stack-name", "cms", "--cert-path", "/home/fieldju/development/cerberus_environments/demo/certs/", "--overwrite" }; @@ -145,56 +139,6 @@ public void test_upload_cert_with_overwrite() { assertArgsAreEqual(expected, actual, commandName); } - @Test - public void test_create_consul_cluster() { - String commandName = CreateConsulClusterCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - StackDelegate.AMI_ID_LONG_ARG, "ami-1111", - StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", - StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - StackDelegate.COST_CENTER_LONG_ARG, "11111", - StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "cloud platform engineering", - StackDelegate.DESIRED_INSTANCES_LONG_ARG, "2", - StackDelegate.MAX_INSTANCES_LONG_ARG, "4", - StackDelegate.MIN_INSTANCES_LONG_ARG, "2" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - - @Test - public void test_create_vault_cluster() { - String commandName = CreateVaultClusterCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - StackDelegate.AMI_ID_LONG_ARG, "ami-2222", - StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", - StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - StackDelegate.COST_CENTER_LONG_ARG, "11111", - StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "cloud platform engineering", - StackDelegate.DESIRED_INSTANCES_LONG_ARG, "2", - StackDelegate.MAX_INSTANCES_LONG_ARG, "4", - StackDelegate.MIN_INSTANCES_LONG_ARG, "2" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - @Test public void test_create_management_service_cluster() { String commandName = CreateCmsClusterCommand.COMMAND_NAME; @@ -217,50 +161,6 @@ public void test_create_management_service_cluster() { assertArgsAreEqual(expected, actual, commandName); } - @Test - public void test_create_gateway_cluster() { - String commandName = CreateGatewayClusterCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - StackDelegate.AMI_ID_LONG_ARG, "ami-4444", - StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", - StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - StackDelegate.COST_CENTER_LONG_ARG, "11111", - StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "cloud platform engineering", - CreateGatewayClusterCommand.HOSTNAME_LONG_ARG, "demo.cerberis-oss.io", - CreateGatewayClusterCommand.HOSTED_ZONE_ID_LONG_ARG, "X5CT6JROG9F2DR" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - - @Test - public void test_publish_dashboard() { - String commandName = PublishDashboardCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - PublishDashboardCommand.ARTIFACT_URL_LONG_ARG, - "https://github.com/Nike-Inc/cerberus-management-dashboard/releases/download/v0.8.0/cerberus-dashboard.tar.gz", - PublishDashboardCommand.OVERRIDE_ARTIFACT_URL_LONG_ARG, - "https://someplace.com/where/you/want/to/store/this.tar.gz" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - @Test public void test_vpc_access_whitelist() { String commandName = WhitelistCidrForVpcAccessCommand.COMMAND_NAME; @@ -310,13 +210,13 @@ public void test_create_cms_config() { public void test_update_stack_with_no_overwrite_flag_or_dyn_props() { String commandName = UpdateStackCommand.COMMAND_NAME; - String[] userInput = {"-f", "/path/to/environment.yaml", commandName, EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "gateway"}; + String[] userInput = {"-f", "/path/to/environment.yaml", commandName, EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms"}; String[] expected = { "-f", "/path/to/environment.yaml", commandName, - EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "gateway", - StackDelegate.AMI_ID_LONG_ARG, "ami-4444", + EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms", + StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", StackDelegate.COST_CENTER_LONG_ARG, "11111", @@ -333,13 +233,13 @@ public void test_update_stack_with_no_overwrite_flag_or_dyn_props() { public void test_update_stack_with_overwrite_flag_and_dyn_props() { String commandName = UpdateStackCommand.COMMAND_NAME; - String[] userInput = {"-f", "/path/to/environment.yaml", commandName, EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "gateway", "--overwrite-template", "-P", "k=v"}; + String[] userInput = {"-f", "/path/to/environment.yaml", commandName, EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms", "--overwrite-template", "-P", "k=v"}; String[] expected = { "-f", "/path/to/environment.yaml", commandName, - EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "gateway", - StackDelegate.AMI_ID_LONG_ARG, "ami-4444", + EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms", + StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", StackDelegate.COST_CENTER_LONG_ARG, "11111", @@ -354,63 +254,6 @@ public void test_update_stack_with_overwrite_flag_and_dyn_props() { assertArgsAreEqual(expected, actual, commandName); } - @Test - public void test_publish_lambda_cf_sg_ip() { - String commandName = PublishLambdaCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", PublishLambdaCommand.COMMAND_NAME, PublishLambdaCommand.LAMBDA_NAME_LONG_ARG, "CLOUD_FRONT_SG_GROUP_IP_SYNC"}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - PublishLambdaCommand.LAMBDA_NAME_LONG_ARG, "CLOUD_FRONT_SG_GROUP_IP_SYNC", - PublishLambdaCommand.ARTIFACT_URL_LONG_ARG, "https://github.com/Nike-Inc/cerberus-lifecycle-cli/raw/master/update_security_groups.zip" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - - @Test - public void test_create_cloud_front_log_processor_lambda_config() { - String commandName = CreateCloudFrontLogProcessingLambdaConfigCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - CreateCloudFrontLogProcessingLambdaConfigCommand.RATE_LIMIT_PER_MINUTE_LONG_ARG, "100", - CreateCloudFrontLogProcessingLambdaConfigCommand.RATE_LIMIT_VIOLATION_BLOCK_PERIOD_IN_MINUTES_LONG_ARG, "60", - CreateCloudFrontLogProcessingLambdaConfigCommand.SLACK_WEB_HOOK_URL_LONG_ARG, "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX", - CreateCloudFrontLogProcessingLambdaConfigCommand.SLACK_ICON_LONG_ARG, "https://raw.githubusercontent.com/Nike-Inc/cerberus/master/images/cerberus-github-logo-black-filled-circle%40500px.png", - CreateCloudFrontLogProcessingLambdaConfigCommand.GOOGLE_ANALYTICS_TRACKING_ID_LONG_ARG, "abc123" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - - @Test - public void test_publish_lambda_waf() { - String commandName = PublishLambdaCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", PublishLambdaCommand.COMMAND_NAME, PublishLambdaCommand.LAMBDA_NAME_LONG_ARG, "WAF"}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - PublishLambdaCommand.LAMBDA_NAME_LONG_ARG, "WAF", - PublishLambdaCommand.ARTIFACT_URL_LONG_ARG, "https://github.com/Nike-Inc/cerberus-cloudfront-lambda/releases/download/v1.1.0/cerberus-cloudfront-lambda.jar" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - private void assertArgsAreEqual(String[] expected, String[] actual, String commandName) { if (expected.length != actual.length) { fail(String.format("The args not the same length, expected length: %s, actual length: %s\nExpected Args: %s\nActual Args: %s", expected.length, actual.length, String.join(" ", expected), String.join(" ", actual))); diff --git a/src/test/java/com/nike/cerberus/generator/VaultAclGeneratorTest.java b/src/test/java/com/nike/cerberus/generator/VaultAclGeneratorTest.java deleted file mode 100644 index 3caf303e..00000000 --- a/src/test/java/com/nike/cerberus/generator/VaultAclGeneratorTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016 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.generator; - -import com.github.mustachejava.Mustache; -import com.github.mustachejava.MustacheFactory; -import com.nike.cerberus.domain.configuration.VaultAclEntry; -import com.nike.cerberus.domain.template.VaultAclInput; -import com.nike.cerberus.util.UuidSupplier; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import java.io.IOException; -import java.io.Writer; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class VaultAclGeneratorTest { - - @Test - public void testGenerate() throws IOException { - UuidSupplier uuidSupplier = mock(UuidSupplier.class); - MustacheFactory mustacheFactory = mock(MustacheFactory.class); - Mustache mustache = mock(Mustache.class); - - String aclToken = "acl-token"; - when(uuidSupplier.get()).thenReturn(aclToken); - when(mustacheFactory.compile("templates/vault-acl.json.mustache")).thenReturn(mustache); - - ArgumentCaptor writer = ArgumentCaptor.forClass(Writer.class); - ArgumentCaptor input = ArgumentCaptor.forClass(VaultAclInput.class); - - when(mustache.execute(writer.capture(), input.capture())).thenReturn(mock(Writer.class)); - - VaultAclGenerator generator = new VaultAclGenerator(uuidSupplier, mustacheFactory); - - // invoke method under test - VaultAclEntry result = generator.generate(); - - assertEquals(aclToken, input.getValue().getAclToken()); - assertEquals(aclToken, result.getAclToken()); - assertEquals(writer.getValue().toString(), result.getEntry()); // test could be improved, compares empty Strings here - } - -} \ No newline at end of file diff --git a/src/test/resources/environment.yaml b/src/test/resources/environment.yaml index d817cdf1..7392a44b 100644 --- a/src/test/resources/environment.yaml +++ b/src/test/resources/environment.yaml @@ -43,24 +43,6 @@ vpc-access-whitelist: cidrs: - 50.39.106.150/32 -consul: - cert-path: /home/fieldju/development/cerberus_environments/demo/certs/ - ami-id: ami-1111 - instance-size: m3.medium - key-pair-name: cerberus-test - desired-instances: 2 - max-instances: 4 - min-instances: 2 - -vault: - cert-path: /home/fieldju/development/cerberus_environments/demo/certs/ - ami-id: ami-2222 - instance-size: m3.medium - key-pair-name: cerberus-test - desired-instances: 2 - max-instances: 4 - min-instances: 2 - management-service: cert-path: /home/fieldju/development/cerberus_environments/demo/certs/ ami-id: ami-3333 @@ -74,34 +56,4 @@ management-service: - auth.connector.onelogin.api_region=us - auth.connector.onelogin.client_id=123 - auth.connector.onelogin.client_secret=312 - - auth.connector.onelogin.subdomain=nike - -gateway: - cert-path: /home/fieldju/development/cerberus_environments/demo/certs/ - ami-id: ami-4444 - instance-size: m3.medium - key-pair-name: cerberus-test - -dashboard: - # The url of the dashboard artifact, probably the newest release on the dashboard Github release page - artifact-url: https://github.com/Nike-Inc/cerberus-management-dashboard/releases/download/v0.8.0/cerberus-dashboard.tar.gz - # The secondary artifact that will get merged into the above artifact before getting uploaded to s3, we use this for custom help page. - override-artifact-url: https://someplace.com/where/you/want/to/store/this.tar.gz - -# By default The gateway elb for Cerberus is exposed to the public internet on port 443 -# We configure CloudFront and WAF to enable things like manual black and white lists as well as auto black listing -edge-security: - # The artifact url for the CloudFront Lambda that process CloudFront Logs to enable things like rate limiting and KPI reporting - cloudfront-lambda-artifact-url: https://github.com/Nike-Inc/cerberus-cloudfront-lambda/releases/download/v1.1.0/cerberus-cloudfront-lambda.jar - # The artifact url to the lambda that ensures only CloudFront IPs are white listed to talk to the origin ELB - cloudfront-security-group-ip-sync-lambda-artifact-url: https://github.com/Nike-Inc/cerberus-lifecycle-cli/raw/master/update_security_groups.zip - # The maximum number of requests from an IP per minute that can flow through the gateway before being auto blocked. - rate-limit-per-minute: 100 - # Time in minutes to block an ip that violates the rate limit. - rate-limit-violation-block-period-in-minutes: 60 - # [Optional delete if you do not need] If you provide a Google Analytics tracking id, the KPI processor will send events to that GA Account. - google-analytics-tracking-id: abc123 - # [Optional delete if you do not need] If you provide a web hook url for slack the cloudfront lambda will send messages on errors and summary info. - slack-web-hook-url: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX - # [Optional delete if you do not need] If you provide an emoji or an icon url the lambda will use it when sending messages. - slack-icon: https://raw.githubusercontent.com/Nike-Inc/cerberus/master/images/cerberus-github-logo-black-filled-circle%40500px.png \ No newline at end of file + - auth.connector.onelogin.subdomain=nike \ No newline at end of file From 6fc15c3cacfa8b622612f23ce6370feb9ac892d0 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Tue, 7 Nov 2017 12:22:09 -0800 Subject: [PATCH 08/69] Convert to getInstanceString() (#71) --- src/main/java/com/nike/cerberus/service/SaltGenerator.java | 6 +++++- .../java/com/nike/cerberus/service/SaltGeneratorTest.java | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/nike/cerberus/service/SaltGenerator.java b/src/main/java/com/nike/cerberus/service/SaltGenerator.java index b19a4872..72d98bac 100644 --- a/src/main/java/com/nike/cerberus/service/SaltGenerator.java +++ b/src/main/java/com/nike/cerberus/service/SaltGenerator.java @@ -1,5 +1,6 @@ package com.nike.cerberus.service; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; import java.util.Random; @@ -9,7 +10,10 @@ */ public class SaltGenerator { - private Random random = new SecureRandom(); + private Random random = SecureRandom.getInstanceStrong(); + + public SaltGenerator() throws NoSuchAlgorithmException { + } /** * Generate a Salt as a Base64 encoded String we can store in a properties file diff --git a/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java b/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java index aa1f6094..740b37ea 100644 --- a/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java +++ b/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java @@ -3,13 +3,15 @@ import org.apache.commons.lang3.StringUtils; import org.junit.Test; +import java.security.NoSuchAlgorithmException; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class SaltGeneratorTest { @Test - public void sanityTestSalt() { + public void sanityTestSalt() throws NoSuchAlgorithmException { SaltGenerator generator = new SaltGenerator(); String s1 = generator.generateSalt(); String s2 = generator.generateSalt(); From a41929ab008384810796361db60b6462468af094 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Tue, 7 Nov 2017 13:54:35 -0800 Subject: [PATCH 09/69] Renaming hash salt config parameter name (#72) --- src/main/java/com/nike/cerberus/ConfigConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index aa818a19..8c98644f 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -70,7 +70,7 @@ public class ConfigConstants { public static final String CMK_ARNS_KEY = "cms.encryption.cmk.arns"; - public static final String HASH_SALT = "auth.token.hashSalt"; + public static final String HASH_SALT = "cms.auth.token.hash.salt"; public static final ImmutableSet SYSTEM_CONFIGURED_CMS_PROPERTIES = ImmutableSet.of( ROOT_USER_ARN_KEY, From 471c6a6ea9df0fd63ae07d48715ccff4496afbc6 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 7 Nov 2017 14:43:17 -0800 Subject: [PATCH 10/69] Add Command to generate Certificate and Keys using BouncyCastle and ACME Providers. (#74) * Adding generate cert command that has an Integration Test that is passing with LetsEncrypt ACME api * change short arg flag so that it is unique * remove short args and adjust certDir description * Update various language and constant names to increase clarity --- .gitignore | 6 + gradle/dependencies.gradle | 18 +- gradle/integration.gradle | 6 + ...GenerateCertsOperationIntegrationTest.java | 188 ++++++++ .../com/nike/cerberus/cli/CerberusRunner.java | 2 + .../command/core/GenerateCertsCommand.java | 164 +++++++ .../core/GenerateCertsOperation.java | 111 +++++ .../core/UploadCertFilesOperation.java | 33 +- .../cerberus/service/CertificateService.java | 440 ++++++++++++++++++ .../nike/cerberus/service/ConsoleService.java | 42 ++ 10 files changed, 996 insertions(+), 14 deletions(-) create mode 100644 src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java create mode 100644 src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java create mode 100644 src/main/java/com/nike/cerberus/service/CertificateService.java diff --git a/.gitignore b/.gitignore index 8741515e..f7bc66ae 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,9 @@ gradle-app.setting # Python cache **/__pycache__/ +/certs/ +build/certs/ +*.pem +*.key +*.crt +*.csr diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 85795e49..939de122 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -19,7 +19,7 @@ allprojects { jcenter() } - def awsSDKVersion = '1.11.155' + def awsSDKVersion = '1.11.220' //noinspection GroovyAssignabilityCheck dependencies { @@ -33,6 +33,8 @@ allprojects { compile group: 'com.amazonaws', name: 'aws-java-sdk-sts', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-sns', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-lambda', version: awsSDKVersion + compile group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: awsSDKVersion + compile 'com.nike:vault-client:1.4.1' compile 'com.squareup.okhttp3:okhttp:3.3.1' @@ -57,9 +59,23 @@ allprojects { compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.6' compile group: 'com.google.code.findbugs', name: 'annotations', version: '3.0.0' + // Cert Generation + compile group: 'dnsjava', name: 'dnsjava', version: '2.1.8' + compile group: 'org.shredzone.acme4j', name: 'acme4j-client', version: '0.13' + compile group: 'org.shredzone.acme4j', name: 'acme4j-utils', version: '0.13' + testCompile "junit:junit:4.12" testCompile ("org.mockito:mockito-core:1.10.19") { exclude group: 'org.hamcrest' } + testCompile 'com.fieldju:commons:1.2.0' + testCompile group: 'io.netty', name: 'netty-handler', version: '4.1.16.Final' + } + + configurations.all { + // check for updates every build + resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + } + } diff --git a/gradle/integration.gradle b/gradle/integration.gradle index 4195bccb..f2780c7e 100644 --- a/gradle/integration.gradle +++ b/gradle/integration.gradle @@ -33,4 +33,10 @@ configurations { task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath +} + +integrationTest { + testLogging { + showStandardStreams = true + } } \ No newline at end of file diff --git a/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java b/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java new file mode 100644 index 00000000..4126993b --- /dev/null +++ b/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017 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.amazonaws.services.identitymanagement.AmazonIdentityManagement; +import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; +import com.amazonaws.services.identitymanagement.model.DeleteServerCertificateRequest; +import com.amazonaws.services.identitymanagement.model.UploadServerCertificateRequest; +import com.amazonaws.services.identitymanagement.model.UploadServerCertificateResult; +import com.amazonaws.services.route53.AmazonRoute53Client; +import com.google.common.collect.ImmutableSet; +import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.service.CertificateService; +import com.nike.cerberus.service.ConsoleService; +import io.netty.handler.ssl.SslContextBuilder; +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.UUID; + +import static com.fieldju.commons.PropUtils.getPropWithDefaultValue; +import static com.fieldju.commons.PropUtils.getRequiredProperty; +import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_CHAIN_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_CSR_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_FULL_CERT_WITH_CHAIN_CRT_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_PKCS1_KEY_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_PKCS8_KEY_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_PUBLIC_KEY_FILE; +import static com.nike.cerberus.service.CertificateService.USER_KEY_FILE; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.contains; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +/** + * This integration test will generate certs using an provided ACME api and test that the certs are usable my an AWS ALB and Netty's SSL Context Builder. + * + * Running this Integration Test will automatically accept the TOS of the ACME provider if present. + * By Running this test you are agreeing with the Terms of Service of the ACME Provider API you provide. + * + * Required Props (can be set via env or system props) + * + * CERT_GEN_TEST_DOMAIN: A domain that will be used as the common name for the generated certs, + * you must also provide a hosted zone id that can create records for this domain. + * + * CERT_GEN_HOSTED_ZONE_ID: The hosted zone id that can create records for CERT_GEN_TEST_DOMAIN + * + * CERT_GEN_CONTACT_EMAIL: The email to use as the contact when generating the cert + * + * Optional Props + * + * CERT_GEN_ACME_API: the ACME api to test against, defaults to 'acme://letsencrypt.org/staging' + * + * EX: CERT_GEN_TEST_DOMAIN=cerberus-oss.io CERT_GEN_HOSTED_ZONE_ID=123123 CERT_GEN_CONTACT_EMAIL=justin.field@nike.com ./gradlew clean integrationTest -DintegrationTest.debug -DintegrationTest.single=GenerateCertsOperationIntegrationTest + * + */ +public class GenerateCertsOperationIntegrationTest { + + public static String CERT_DIR = "build/certs/"; + + @Mock + private GenerateCertsCommand command; + + @Mock + private ConsoleService consoleService; + + @Mock + private EnvironmentMetadata environmentMetadata; + + private GenerateCertsOperation operation; + + private File certDir; + + @Before + public void before() { + initMocks(this); + + CertificateService certificateService = new CertificateService(consoleService, + AmazonRoute53Client.builder().withRegion("us-west-2").build()); + + operation = new GenerateCertsOperation(certificateService, environmentMetadata, consoleService); + + certDir = new File(CERT_DIR); + } + + @Test + public void test_that_command_generates_valid_certs() throws IOException { + when(environmentMetadata.getName()).thenReturn("integration-test"); + when(environmentMetadata.getRegionName()).thenReturn("us-west-2"); + when(command.getBaseDomainName()).thenReturn(getRequiredProperty("CERT_GEN_TEST_DOMAIN", + "CERT_GEN_TEST_DOMAIN is a required env or system prop and must be a domain that you have a" + + " hosted zone for that can create records")); + when(command.getHostedZoneId()).thenReturn(getRequiredProperty("CERT_GEN_HOSTED_ZONE_ID", + "The hosted zone id that can create records for CERT_GEN_TEST_DOMAIN")); + when(consoleService.readLine(contains("Would you like to proceed?"))).thenReturn("y"); + when(command.enableLetsEncryptCertfix()).thenReturn(true); + when(command.getCertDir()).thenReturn(certDir.getAbsolutePath()); + when(consoleService.readLine(contains("accept"))) + .thenReturn("i accept"); + when(command.getAcmeApiUrl()).thenReturn(getPropWithDefaultValue("CERT_GEN_ACME_API", + "acme://letsencrypt.org/staging")); + when(command.getContactEmail()).thenReturn(getRequiredProperty("CERT_GEN_CONTACT_EMAIL", + "The email contact to use when generating the cert")); + + operation.run(command); + + ImmutableSet.of( + USER_KEY_FILE, + DOMAIN_PKCS1_KEY_FILE, + DOMAIN_PKCS8_KEY_FILE, + DOMAIN_CSR_FILE, + DOMAIN_FULL_CERT_WITH_CHAIN_CRT_FILE, + DOMAIN_CERT_FILE, + DOMAIN_CERT_CHAIN_FILE, + DOMAIN_PUBLIC_KEY_FILE + ).forEach(expectedFileName -> { + File expectedFile = new File(certDir.getAbsolutePath() + File.separator + expectedFileName); + assertTrue("The expected file should exist after running the generate certs command", expectedFile.exists()); + }); + + // test that the certs are usable by and ALB + assertThatCertsAreUsableByAws(); + + // test that the PKCS8 Private key and ACME generated cert are loadable by CMS + assertThatCertsAreUsableByCms(); + + System.out.println("The Keys and Certificates where properly generated and passed validation"); + } + + private void assertThatCertsAreUsableByAws() throws IOException { + String uuid = UUID.randomUUID().toString(); + AmazonIdentityManagement amazonIdentityManagement = AmazonIdentityManagementClient.builder().withRegion(Regions.DEFAULT_REGION).build(); + final UploadServerCertificateRequest request = new UploadServerCertificateRequest() + .withServerCertificateName("cert-gen-integration-test-" + uuid) + .withPath("/cert-gen-integration-test/" + uuid + "/") + .withCertificateBody(new String(Files.readAllBytes((new File(certDir.getAbsolutePath() + File.separator + DOMAIN_CERT_FILE).toPath())))) + .withCertificateChain(new String(Files.readAllBytes((new File(certDir.getAbsolutePath() + File.separator + DOMAIN_CERT_CHAIN_FILE).toPath())))) + .withPrivateKey(new String(Files.readAllBytes(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_PKCS1_KEY_FILE).toPath()))); + + final UploadServerCertificateResult result = amazonIdentityManagement.uploadServerCertificate(request); + + assertTrue(StringUtils.isNotBlank(result.getServerCertificateMetadata().getArn())); + + amazonIdentityManagement.deleteServerCertificate( + new DeleteServerCertificateRequest() + .withServerCertificateName(result.getServerCertificateMetadata().getServerCertificateName()) + ); + } + + private void assertThatCertsAreUsableByCms() { + String[] cmsSupportedProtocols = new String[] { + "TLSv1.2" + }; + + try { + SslContextBuilder.forServer( + new File(certDir.getAbsolutePath() + File.separator + DOMAIN_CERT_FILE), + new File(certDir.getAbsolutePath() + File.separator + DOMAIN_PKCS8_KEY_FILE) + ).protocols(cmsSupportedProtocols).build(); + } catch (Exception e) { + fail("Failed to generate the SSL Context that CMS uses to validate Cert and PKCS8 private key. Reason: " + e.getMessage()); + } + } + +} diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 7974d5ef..c47d74b3 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -32,6 +32,7 @@ import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; +import com.nike.cerberus.command.core.GenerateCertsCommand; import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; import com.nike.cerberus.command.core.ViewConfigCommand; import com.nike.cerberus.command.core.CreateBaseCommand; @@ -170,6 +171,7 @@ private void registerAllCommands() { registerCommand(new RollingRebootWithHealthCheckCommand()); registerCommand(new CreateCerberusBackupCommand()); registerCommand(new SetBackupAdminPrincipalsCommand()); + registerCommand(new GenerateCertsCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java new file mode 100644 index 00000000..c301bd3f --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.GenerateCertsOperation; + +import java.util.ArrayList; +import java.util.List; + +import static com.nike.cerberus.command.core.GenerateCertsCommand.COMMAND_DESCRIPTION; +import static com.nike.cerberus.command.core.GenerateCertsCommand.COMMAND_NAME; + +@Parameters( + commandNames = COMMAND_NAME, + commandDescription = COMMAND_DESCRIPTION +) +public class GenerateCertsCommand implements Command { + + public static final String COMMAND_NAME = "generate-certificates"; + public static final String COMMAND_DESCRIPTION = "Generates the TLS certificates needed to enable https " + + "through out the system, using an ACME provider such as LetsEncrypt"; + + public static final String BASE_DOMAIN_LONG_ARG = "--base-domain"; + + public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; + + public static final String ADDITIONAL_SUBJECT_ALT_NAME_LONG_ARG = "--additional-subject-alternative-name"; + + public static final String ENABLE_LE_CERTFIX_LONG_ARG = "--enable-letsecrypt-certfix"; + + public static final String CERT_FOLDER_LONG_ARG = "--local-certificate-directory"; + + public static final String ACME_API_LONG_ARG = "--acme-api-url"; + + public static final String CONTACT_EMAIL_LONG_ARG = "--contact-email"; + + public static final String LETS_ENCRYPT_ACME_API_URI = "acme://letsencrypt.org"; + + + @Parameter( + names = { + BASE_DOMAIN_LONG_ARG + }, + description = "The base domain for the environment that this command will use to generate the following " + + "subject name {env}.{base-domain} and with the following subject alternative name {env}.{region}.{base-domain}\n" + + "ex: cerberus -e demo -r us-west-2 generate-certificates --base-domain cerberus.example would make a cert for demo.cerberus.example " + + "with a sans of demo.us-west-2.cerberus.example so that we can create a CNAME record for " + + "demo.cerberus.example that will point to an ALB in us-west-2 with a CNAME record of " + + "demo.us-west-2.cerberus.example and the cert will be valid for both", + required = true + ) + private String baseDomainName; + + public String getBaseDomainName() { + return baseDomainName; + } + + @Parameter( + names = { + HOSTED_ZONE_ID_LONG_ARG + }, + description = "The AWS Route 53 hosted zone id that is configured to create records for the base domain", + required = true + ) + private String hostedZoneId; + + public String getHostedZoneId() { + return hostedZoneId; + } + + @Parameter( + names = { + ADDITIONAL_SUBJECT_ALT_NAME_LONG_ARG + }, + description = "Alternative subject names, this should be any additional cnames that need to be secured. such as " + ) + private List subjectAlternativeNames = new ArrayList<>(); + + public List getSubjectAlternativeNames() { + return subjectAlternativeNames; + } + + @Parameter( + names = { + ENABLE_LE_CERTFIX_LONG_ARG + }, + description = "This command uses the acme4j client to communicate with the ACME server, " + + "it supports uses a hardcoded letsencrypt cert to get around ssl errors, you can use this " + + "flag if your truststore is not configured to trust LE certificates, which can be found " + + " here: https://letsencrypt.org/certificates/" + ) + private boolean EnableLetsEncryptCertfix = false; + + public boolean enableLetsEncryptCertfix() { + return EnableLetsEncryptCertfix; + } + + @Parameter( + names = { + CERT_FOLDER_LONG_ARG + }, + description = "A local writable folder to store the generated key and cert files", + required = true + ) + private String certDir; + + public String getCertDir() { + return certDir; + } + + @Parameter( + names = { + ACME_API_LONG_ARG + }, + description = "The ACME provider API URL to use, e.g. Let's Encrypt: " + LETS_ENCRYPT_ACME_API_URI, + required = true + ) + private String acmeApiUrl; + + public String getAcmeApiUrl() { + return acmeApiUrl; + } + + @Parameter( + names = { + CONTACT_EMAIL_LONG_ARG + }, + description = "The email contact to use when creating the certificates", + required = true + ) + private String contactEmail; + + public String getContactEmail() { + return contactEmail; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return GenerateCertsOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java new file mode 100644 index 00000000..7aac6cf5 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CertificateService; +import com.nike.cerberus.service.ConsoleService; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import static com.nike.cerberus.service.ConsoleService.DefaultAction.NO; + +/** + * Operation that uses the cert service to generate the certificates needed to enable https in a Cerberus Env. + */ +public class GenerateCertsOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final CertificateService certService; + private final EnvironmentMetadata environmentMetadata; + private final ConsoleService consoleService; + + @Inject + public GenerateCertsOperation(CertificateService certService, + EnvironmentMetadata environmentMetadata, + ConsoleService consoleService) { + + this.certService = certService; + this.environmentMetadata = environmentMetadata; + this.consoleService = consoleService; + } + + @SuppressFBWarnings( + value = "REC_CATCH_EXCEPTION", + justification = "I do not want to catch 1000 individual exceptions" + ) + @Override + public void run(GenerateCertsCommand command) { + // The common name ex: demo.cerberus.io + String commonName = String.format("%s.%s", environmentMetadata.getName(), command.getBaseDomainName()); + // The region specific subject alternate name. EX demo.us-west-2.ceberus.io + String regionSpecificSAN = String.format("%s.%s.%s", + environmentMetadata.getName(), environmentMetadata.getRegionName(), command.getBaseDomainName()); + + Set subjectAlternativeNames = new HashSet<>(); + subjectAlternativeNames.addAll(command.getSubjectAlternativeNames()); // add any additional SANs to the SAN set + subjectAlternativeNames.add(regionSpecificSAN); // add the default region specific san + + // confirm with user + consoleService.askUserToProceed(String.format("Preparing to generate certs with Common Name: %s and Subject Alternative Names: %s", + commonName, String.join(", ", subjectAlternativeNames)), NO); + + // Enable the use of the hard coded lets encrypt cert if enabled + if (command.enableLetsEncryptCertfix()) { + log.warn("Setting acme4j.le.certfix system property to 'true', this only works if you use the special acme:// lets encrypt addr. See: https://shredzone.org/maven/acme4j/usage/session.html and https://shredzone.org/maven/acme4j/provider.html"); + System.setProperty("acme4j.le.certfix", "true"); + } + + try { + // Use a temp dir or local dir if provided + File certDir = new File(command.getCertDir()); + + // check that we can write to the provided dir + FileUtils.forceMkdir(certDir); + if (! certDir.isDirectory() || ! certDir.canWrite()) { + throw new RuntimeException("The certificate directory is not a directory or is not writable, path: " + certDir.getAbsolutePath()); + } + + // generate the certs + certService.generateCerts( + certDir, + command.getAcmeApiUrl(), + commonName, + subjectAlternativeNames, + command.getHostedZoneId(), + command.getContactEmail() + ); + } catch (Exception e) { + throw new RuntimeException("Failed to generate certs", e); + } + } + + @Override + public boolean isRunnable(GenerateCertsCommand command) { + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index 974ee597..4a25565c 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -18,11 +18,11 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CertificateService; import com.nike.cerberus.service.IdentityManagementService; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.UuidSupplier; @@ -38,17 +38,24 @@ import java.nio.file.Path; import java.util.Set; +import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_CHAIN_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_PKCS1_KEY_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_PKCS8_KEY_FILE; +import static com.nike.cerberus.service.CertificateService.DOMAIN_PUBLIC_KEY_FILE; + /** * Handles uploading of certificate files to IAM and the config store. */ public class UploadCertFilesOperation implements Operation { - private static final String CA_FILE_NAME = "ca.pem"; - private static final String CERT_FILE_NAME = "cert.pem"; - private static final String KEY_FILE_NAME = "key.pem"; - private static final String PKCS8_KEY_FILE_NAME = "pkcs8-key.pem"; - private static final String PUB_KEY_FILE_NAME = "pubkey.pem"; - public static final Set EXPECTED_FILE_NAMES = ImmutableSet.of(CA_FILE_NAME, CERT_FILE_NAME, KEY_FILE_NAME, PKCS8_KEY_FILE_NAME, PUB_KEY_FILE_NAME); + public static final Set EXPECTED_FILE_NAMES = ImmutableSet.of( + DOMAIN_CERT_CHAIN_FILE, + DOMAIN_CERT_FILE, + DOMAIN_PKCS1_KEY_FILE, + DOMAIN_PKCS8_KEY_FILE, + DOMAIN_PUBLIC_KEY_FILE + ); private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -75,11 +82,11 @@ public UploadCertFilesOperation(final EnvironmentMetadata environmentMetadata, public void run(final UploadCertFilesCommand command) { final StackName stackName = command.getStackName(); final Path certPath = command.getCertPath(); - final String caContents = getFileContents(certPath, CA_FILE_NAME); - final String certContents = getFileContents(certPath, CERT_FILE_NAME); - final String keyContents = getFileContents(certPath, KEY_FILE_NAME); - final String pkcs8KeyContents = getFileContents(certPath, PKCS8_KEY_FILE_NAME); - final String pubKeyContents = getFileContents(certPath, PUB_KEY_FILE_NAME); + final String caContents = getFileContents(certPath, DOMAIN_CERT_CHAIN_FILE); + final String certContents = getFileContents(certPath, DOMAIN_CERT_FILE); + final String keyContents = getFileContents(certPath, DOMAIN_PKCS1_KEY_FILE); + final String pkcs8KeyContents = getFileContents(certPath, DOMAIN_PKCS8_KEY_FILE); + final String pubKeyContents = getFileContents(certPath, DOMAIN_PUBLIC_KEY_FILE); final String certificateName = stackName.getName() + "_" + uuidSupplier.get(); logger.info("Uploading certificate to IAM for {}, with name of {}.", stackName.getName(), certificateName); @@ -127,6 +134,6 @@ private String getFileContents(final Path path, final String filename) { } private String getPath() { - return "/cloudfront/cerberus/" + environmentMetadata.getName() + "/"; + return "/cerberus/" + environmentMetadata.getName() + "/"; } } diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java new file mode 100644 index 00000000..b3485233 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2017 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.services.route53.AmazonRoute53; +import com.amazonaws.services.route53.model.Change; +import com.amazonaws.services.route53.model.ChangeAction; +import com.amazonaws.services.route53.model.ChangeBatch; +import com.amazonaws.services.route53.model.ChangeResourceRecordSetsRequest; +import com.amazonaws.services.route53.model.RRType; +import com.amazonaws.services.route53.model.ResourceRecord; +import com.amazonaws.services.route53.model.ResourceRecordSet; +import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator; +import org.bouncycastle.util.io.pem.PemObject; +import org.shredzone.acme4j.Authorization; +import org.shredzone.acme4j.Certificate; +import org.shredzone.acme4j.Registration; +import org.shredzone.acme4j.RegistrationBuilder; +import org.shredzone.acme4j.Session; +import org.shredzone.acme4j.Status; +import org.shredzone.acme4j.challenge.Challenge; +import org.shredzone.acme4j.challenge.Dns01Challenge; +import org.shredzone.acme4j.exception.AcmeConflictException; +import org.shredzone.acme4j.exception.AcmeException; +import org.shredzone.acme4j.util.CSRBuilder; +import org.shredzone.acme4j.util.CertificateUtils; +import org.shredzone.acme4j.util.KeyPairUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xbill.DNS.Lookup; +import org.xbill.DNS.Record; +import org.xbill.DNS.SimpleResolver; +import org.xbill.DNS.Type; + +import javax.inject.Inject; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.net.URI; +import java.security.KeyPair; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Service for managing Cerificates and calls to the ACME cert provider + * + * TODO: Update TOS + * TODO: Rotate ACME user private key + * TODO: Support Venafi ACME Impl + */ +@SuppressFBWarnings(value = { + "DM_DEFAULT_ENCODING", + "REC_CATCH_EXCEPTION" +}) +public class CertificateService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + // File name of the PKCS #1 private key pem for the ACME registration, this represents a "User" + public static final String USER_KEY_FILE = "acme-user-private-key-pkcs1.pem"; + + // File name of the PKCS #1 private key pem for the Cerberus domain + public static final String DOMAIN_PKCS1_KEY_FILE = "domain-private-key-pkcs1.pem"; + + // File name of the PKCS #8 private key pem for the Cerberus domain + public static final String DOMAIN_PKCS8_KEY_FILE = "domain-private-key-pkcs8.pem"; + + // File name of pub key pem + public static final String DOMAIN_PUBLIC_KEY_FILE = "domain-public-key.pem"; + + // File name of the certificate signing request + public static final String DOMAIN_CSR_FILE = "domain.csr"; + + // File name of the signed certificate with chain + public static final String DOMAIN_FULL_CERT_WITH_CHAIN_CRT_FILE = "domain-full-cert-with-chain.crt"; + + // File name of the signed certificate + public static final String DOMAIN_CERT_FILE = "domain-cert.crt"; + + // File name of the certificate chain + public static final String DOMAIN_CERT_CHAIN_FILE = "chain.crt"; + + // RSA key size of generated key pairs + private static final int KEY_SIZE = 2048; + + protected static final String CHALLENGE_ENTRY_TEMPLATE = "_acme-challenge.%s"; + + private final ConsoleService console; + private final AmazonRoute53 route53; + + @Inject + public CertificateService(ConsoleService console, + AmazonRoute53 route53) { + + this.console = console; + this.route53 = route53; + } + + /** + * Finds your {@link Registration} at the ACME server. It will be found by your user's + * public key. If your key is not known to the server yet, a new registration will be + * created. + *

+ * This is a simple way of finding your {@link Registration}. A better way is to get + * the URI of your new registration with {@link Registration#getLocation()} and store + * it somewhere. If you need to get access to your account later, reconnect to it via + * {@link Registration#bind(Session, URI)} by using the stored location. + * + * @param session + * {@link Session} to bind with + * @return {@link Registration} connected to your account + */ + protected Registration findOrRegisterAccount(Session session, String contactEmail) throws AcmeException, IOException { + Registration reg; + try { + // Try to create a new Registration. + reg = new RegistrationBuilder().addContact("mailto:" + contactEmail).create(session); + log.info("Registered a new user, URI: " + reg.getLocation()); + + // This is a new account. Let the user accept the Terms of Service. + // We won't be able to authorize domains until the ToS is accepted. + URI agreement = reg.getAgreement(); + + if (! (agreement == null)) { + acceptAgreement(reg, agreement); + } + } catch (AcmeConflictException ex) { + // The Key Pair is already registered. getLocation() contains the + // URL of the existing registration's location. Bind it to the session. + reg = Registration.bind(session, ex.getLocation()); + log.info("Account already exist, URI: {}, no need to create new account", reg.getLocation()); + } + return reg; + } + + /** + * Prompts a user to read and accept or deny a ACME providers TOS + * + * @param reg The registration object + * @param agreement The link to the TOS + */ + protected void acceptAgreement(Registration reg, URI agreement) { + try { + log.info("Please download and review the Terms of Service: " + agreement); + String userResponse = console.readLine("Type \"I Accept\" to accept the Terms of Service, anything else will exit: "); + if (! StringUtils.equalsIgnoreCase("I Accept", userResponse)) { + throw new RuntimeException("User did not accept the Terms of Service"); + } + reg.modify().setAgreement(agreement).commit(); + log.info("Updated user's ToS"); + } catch (AcmeException | IOException e) { + throw new RuntimeException("Failed to accept ACME TOS", e); + } + } + + /** + * Creates a Txt record in Route53 + * + * @param name The record name + * @param digest The value for the txt record + * @param hostedZoneId The hosted zone id to create the record in + */ + protected void generateChallengeTxtEntry(String name, String digest, String hostedZoneId) { + ResourceRecordSet recordSet = new ResourceRecordSet() + .withName(name) + .withType(RRType.TXT) + .withTTL(10L) + .withResourceRecords( + new ResourceRecord(String.format("\"%s\"", digest)) + ); + + createOrUpdateRecordSets(recordSet, hostedZoneId); + } + + protected void createOrUpdateRecordSets(ResourceRecordSet recordSet, String hostedZoneId) { + ChangeBatch changeBatch = new ChangeBatch().withChanges( + new Change() + .withAction(ChangeAction.UPSERT) + .withResourceRecordSet(recordSet) + ); + + route53.changeResourceRecordSets(new ChangeResourceRecordSetsRequest() + .withChangeBatch(changeBatch) + .withHostedZoneId(hostedZoneId)); + } + + /** + * Uses DNS to get a txt record + * + * @param cname the txt record to loop up + * @return Value of record if present + */ + protected Optional getTxtRecordValue(String cname) { + try { + Lookup lookup = new Lookup(cname, Type.TXT); + lookup.setResolver(new SimpleResolver()); + lookup.setCache(null); + Record[] records = lookup.run(); + return Optional.of(records[0].rdataToString()); + } catch (Exception e) { + log.error("Failed to look up txt record for {} reason: {}", cname, e.getMessage()); + return Optional.empty(); + } + } + + /** + * Loads a key pair from specified file. If the file does not exist, + * a new key pair is generated and saved. + * + * @return {@link KeyPair}. + */ + protected KeyPair loadOrCreateKeyPair(File file) throws IOException { + if (file.exists()) { + try (FileReader fr = new FileReader(file)) { + return KeyPairUtils.readKeyPair(fr); + } + } else { + KeyPair domainKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE); + try (FileWriter fw = new FileWriter(file)) { + KeyPairUtils.writeKeyPair(domainKeyPair, fw); + } + return domainKeyPair; + } + } + + + /** + * Generates the certs needed for a Cerberus Environment + * + * @param certDir The folder to store the generated files + * @param commonName The primary CNAME for the cert + * @param subjectAlternativeNames Additional CNAMEs for the cert + * @param hostedZoneId The hosted zone id that can be used to create txt records for the ACME ownership challenges + */ + public void generateCerts(File certDir, + String acmeServerUrl, + String commonName, + Set subjectAlternativeNames, + String hostedZoneId, + String contactEmail) { + + try { + List names = new LinkedList<>(); + names.add(commonName); // the first entry counts as the common name + names.addAll(subjectAlternativeNames); + + KeyPair userKeyPair = loadOrCreateKeyPair(new File(certDir.getAbsolutePath() + File.separator + USER_KEY_FILE)); + + Session session = new Session(acmeServerUrl, userKeyPair); + Registration registration = findOrRegisterAccount(session, contactEmail); + + for (String name : names) { + Authorization authorization = registration.authorizeDomain(name); + getChallenges(authorization).forEach(challenge -> doChallenge(challenge, name, hostedZoneId)); + } + + KeyPair domainKeyPair = loadOrCreateKeyPair(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_PKCS1_KEY_FILE)); + createPKCS8PrivateKeyPemFileFromKeyPair(domainKeyPair, certDir); + createPKCS1PublicKeyPem(domainKeyPair, certDir); + CSRBuilder csrb = new CSRBuilder(); + csrb.addDomains(names); + csrb.sign(domainKeyPair); + + try (Writer csrWriter = new FileWriter(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_CSR_FILE))) { + csrb.write(csrWriter); + } + + // Now request a signed certificate. + Certificate certificate = registration.requestCertificate(csrb.getEncoded()); + + log.info("Success! The certificate has been generated!"); + log.info("Certificate URI: " + certificate.getLocation()); + + // Download the leaf certificate and certificate chain. + X509Certificate cert = certificate.download(); + X509Certificate[] chain = certificate.downloadChain(); + + // Write a combined file containing the certificate and chain. + try (FileWriter fullCertWriter = new FileWriter(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_FULL_CERT_WITH_CHAIN_CRT_FILE))) { + CertificateUtils.writeX509CertificateChain(fullCertWriter, cert, chain); + } + // write just the cert + try (FileWriter certWriter = new FileWriter(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_CERT_FILE))) { + CertificateUtils.writeX509Certificate(cert, certWriter); + } + // write just the chain + try (FileWriter chainWriter = new FileWriter(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_CERT_CHAIN_FILE))) { + CertificateUtils.writeX509CertificateChain(chainWriter, null, chain); + } + + } catch (Exception e) { + throw new RuntimeException("Failed to generate certs", e); + } + } + + /** + * Takes a challenge and performs it + * + * @param challenge The challenge + * @param domainName The domain that is being verified + * @param hostedZone The hosted zone id if applicable + */ + protected void doChallenge(Challenge challenge, String domainName, String hostedZone) { + switch (challenge.getType()) { + case Dns01Challenge.TYPE: + doDns01Challenge((Dns01Challenge) challenge, domainName, hostedZone); + break; + default: + throw new RuntimeException("Unsupported challenge type: " + challenge.getType()); + } + } + + /** + * Attempts to get an acceptable challenge from the ACME provider. + * + * This CLI currently only supports DNS-01 Challenges + * + * @param authorization The authorization object from the ACME provider + * @return a Collection of challenges to complete. + */ + protected Collection getChallenges(Authorization authorization) { + List desiredChallengeCombos = ImmutableList.of( + new String[]{Dns01Challenge.TYPE} + ); + + for (String[] desiredChallengeCombo : desiredChallengeCombos) { + Collection challenges = authorization.findCombination(desiredChallengeCombo); + if (! challenges.isEmpty()) { + return challenges; + } + } + + String apiSupportedChallenges = new Gson().toJson(authorization.getCombinations()); + String cliSupportedMethods = new Gson().toJson(desiredChallengeCombos); + throw new RuntimeException("Failed to find a supportable combination of domain verification challenges. " + + "Supported challenge combos by the API: " + apiSupportedChallenges + + ", challenge combos supported by the CLI: " + cliSupportedMethods); + } + + /** + * Performs the ACME DNS 01 Challenge by creating txt records in Route 53 + * + * @param challenge The ACME challenge info with digest + * @param domainName The domain name that is being verified + * @param hostedZoneId The hosted zone id that has permissions to create dns records for the domain name + */ + protected void doDns01Challenge(Dns01Challenge challenge, String domainName, String hostedZoneId) { + String digest = challenge.getDigest(); + try { + String cname = String.format(CHALLENGE_ENTRY_TEMPLATE, domainName); + generateChallengeTxtEntry(cname, digest, hostedZoneId); + Optional recordValue; + do { + log.info("Waiting for name: '{}' to have txt record digest value: '{}' before triggering challenge", cname, digest); + Thread.sleep(TimeUnit.SECONDS.toMillis(60)); + recordValue = getTxtRecordValue(cname); + } while (! recordValue.isPresent() || ! recordValue.get().equals(String.format("\"%s\"", digest))); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to complete DNS 01 challenge", e); + } + + triggerChallenge(challenge); + } + + /** + * Triggers the ACME challenge polling for its status to be complete + * + * @param challenge The challenge that is ready to be triggered + */ + protected void triggerChallenge(Challenge challenge) { + try { + challenge.trigger(); + + // TODO better polling is possible, this can cause infinite loops + while (challenge.getStatus() != Status.VALID) { + log.info("Waiting for challenge to complete"); + Thread.sleep(3000); + challenge.update(); + } + log.info("challenge accepted"); + } catch (AcmeException | InterruptedException e) { + log.error("failed to trigger and wait for challenge to be accepted", e); + } + } + + protected void createPKCS1PublicKeyPem(KeyPair keyPair, File certDir) { + try { + FileWriter pubKeyWriter = new FileWriter(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_PUBLIC_KEY_FILE)); + try (JcaPEMWriter jw = new JcaPEMWriter(pubKeyWriter)) { + jw.writeObject(keyPair.getPublic()); + } + } catch (IOException e) { + throw new RuntimeException("Failed to create pub key pem", e); + } + } + + /** + * Netty, which is what CMS is using requires the private key to be in PKCS8 format + * http://netty.io/wiki/sslcontextbuilder-and-private-key.html + */ + protected void createPKCS8PrivateKeyPemFileFromKeyPair(KeyPair keyPair, File certDir) { + try { + JcaPKCS8Generator pkcs8Generator = new JcaPKCS8Generator(keyPair.getPrivate(), null); + PemObject pemObject = pkcs8Generator.generate(); + FileWriter fileWriter = new FileWriter(new File(certDir.getAbsolutePath() + + File.separator + DOMAIN_PKCS8_KEY_FILE)); + try (JcaPEMWriter pemWriter = new JcaPEMWriter(fileWriter)) { + pemWriter.writeObject(pemObject); + } + } catch (Exception e) { + throw new RuntimeException("Failed to create PKCS8 private key pem", e); + } + } +} diff --git a/src/main/java/com/nike/cerberus/service/ConsoleService.java b/src/main/java/com/nike/cerberus/service/ConsoleService.java index 31f7bac2..b54e615c 100644 --- a/src/main/java/com/nike/cerberus/service/ConsoleService.java +++ b/src/main/java/com/nike/cerberus/service/ConsoleService.java @@ -17,6 +17,7 @@ package com.nike.cerberus.service; import com.google.inject.Singleton; +import org.apache.commons.lang3.StringUtils; import java.io.BufferedReader; import java.io.IOException; @@ -45,4 +46,45 @@ public char[] readPassword(String format, Object... args) throws IOException { } return readLine(format, args).toCharArray(); } + + public void askUserToProceed(String additionalMessage, DefaultAction defaultAction) { + String userInput; + try { + StringBuilder sb = new StringBuilder(); + if (StringUtils.isNotBlank(additionalMessage)) { + sb.append(additionalMessage).append('\n'); + } + sb.append("Would you like to proceed? ") + .append(defaultAction.isYesDefault() ? "(Y/n)" : "(y/N)") + .append(": "); + userInput = readLine(sb.toString()); + } catch (IOException e) { + throw new RuntimeException("Failed to ask user to proceed", e); + } + + if (StringUtils.isBlank(userInput) && defaultAction.isYesDefault()) { + return; + } + + if (StringUtils.isNotBlank(userInput) && (userInput.equalsIgnoreCase("y") || userInput.equalsIgnoreCase("yes"))) { + return; + } + + throw new RuntimeException("User declined to proceed"); + } + + public enum DefaultAction { + YES(true), + NO(false); + + private boolean yesIsDefault; + + protected boolean isYesDefault() { + return yesIsDefault; + } + + DefaultAction(boolean yesIsDefault) { + this.yesIsDefault = yesIsDefault; + } + } } From 2aabeb2e0f90a542c4ed935dbddea1f68e254da1 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Wed, 8 Nov 2017 12:27:17 -0800 Subject: [PATCH 11/69] Add CLI commands for new Infrastructure (#75) * Fix merge issues * Fix command name imports * Add CLI commands for new infrastructure * Configure YAML to work with new commands --- .../com/nike/cerberus/ConfigConstants.java | 14 +- .../com/nike/cerberus/cli/CerberusRunner.java | 14 +- .../cli/EnvironmentConfigToArgsMapper.java | 90 +++- .../nike/cerberus/command/StackDelegate.java | 30 +- .../command/cms/CreateCmsClusterCommand.java | 2 +- .../command/cms/CreateCmsCmkCommand.java | 2 +- .../command/cms/CreateCmsConfigCommand.java | 4 +- .../command/core/CreateBaseCommand.java | 51 +- .../command/core/CreateDatabaseCommand.java | 55 +++ .../core/CreateLoadBalancerCommand.java | 54 +++ .../command/core/CreateRoute53Command.java | 66 +++ .../core/CreateSecurityGroupsCommand.java | 56 +++ .../command/core/CreateVpcCommand.java | 53 +++ .../command/core/CreateWafCommand.java | 54 +++ .../command/core/PrintStackInfoCommand.java | 2 +- .../core/RestoreCerberusBackupCommand.java | 2 +- .../RollingRebootWithHealthCheckCommand.java | 16 + .../command/core/UpdateStackCommand.java | 21 - .../command/core/UploadCertFilesCommand.java | 2 +- .../command/core/ViewConfigCommand.java | 2 +- .../WhitelistCidrForVpcAccessCommand.java | 6 +- .../domain/cloudformation/BaseOutputs.java | 393 +--------------- .../domain/cloudformation/BaseParameters.java | 112 +---- .../domain/cloudformation/CmsOutputs.java | 54 +-- .../domain/cloudformation/CmsParameters.java | 87 +--- .../cloudformation/DatabaseOutputs.java | 67 +++ .../cloudformation/DatabaseParameters.java | 148 ++++++ .../cloudformation/DeprecatedBaseOutputs.java | 444 ++++++++++++++++++ .../DeprecatedBaseParameters.java | 86 ++++ .../cloudformation/GatewayParameters.java | 1 - .../LaunchConfigParameters.java | 2 +- .../cloudformation/LoadBalancerOutputs.java | 67 +++ .../LoadBalancerParameters.java | 104 ++++ .../Route53Outputs.java} | 29 +- .../cloudformation/Route53Parameters.java | 56 +++ .../cloudformation/SecurityGroupOutputs.java | 78 +++ .../SecurityGroupParameters.java | 70 +++ .../cloudformation/TagParametersDelegate.java | 29 ++ .../domain/cloudformation/VpcOutputs.java | 100 ++++ .../domain/cloudformation/VpcParameters.java | 126 +++++ .../domain/cloudformation/WafOutputs.java | 56 +++ .../domain/cloudformation/WafParameters.java | 49 ++ .../domain/environment/StackName.java | 17 +- .../domain/input/EnvironmentConfig.java | 9 + .../cerberus/domain/input/SecurityGroups.java | 33 ++ .../cms/CreateCmsClusterOperation.java | 70 ++- .../cms/CreateCmsConfigOperation.java | 2 +- .../operation/core/CreateBaseOperation.java | 132 +----- .../core/CreateDatabaseOperation.java | 106 +++++ .../core/CreateLoadBalancerOperation.java | 102 ++++ .../core/CreateRoute53Operation.java | 92 ++++ .../core/CreateSecurityGroupsOperation.java | 91 ++++ .../operation/core/CreateVpcOperation.java | 108 +++++ .../operation/core/CreateWafOperation.java | 95 ++++ .../core/PrintStackInfoOperation.java | 2 +- ...RollingRebootWithHealthCheckOperation.java | 16 + .../SetBackupAdminPrincipalsOperation.java | 13 +- .../operation/core/UpdateStackOperation.java | 40 +- .../core/UploadCertFilesOperation.java | 2 +- ...> WhitelistCidrForVpcAccessOperation.java} | 16 +- .../service/CloudFormationService.java | 37 +- .../UnexpectedDataEncodingException.java | 2 +- .../com/nike/cerberus/store/ConfigStore.java | 140 +++++- .../resources/cloudformation/cms-cluster.yaml | 18 +- .../resources/cloudformation/database.yaml | 47 +- .../cloudformation/load-balancer.yaml | 20 +- .../resources/cloudformation/route53.yaml | 2 +- .../cloudformation/security-groups.yaml | 26 +- src/main/resources/cloudformation/vpc.yaml | 23 +- .../cloudformation/web-app-firewall.yaml | 10 +- src/main/resources/example-standup.yaml | 2 +- .../EnvironmentConfigToArgsMapperTest.java | 10 +- src/test/resources/environment.yaml | 2 +- 73 files changed, 3005 insertions(+), 1034 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateVpcCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java rename src/main/java/com/nike/cerberus/domain/{configuration/VaultAclEntry.java => cloudformation/Route53Outputs.java} (53%) create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/WafOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java create mode 100644 src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java rename src/main/java/com/nike/cerberus/operation/core/{WhitelistCidrForVpcAccessOpertaion.java => WhitelistCidrForVpcAccessOperation.java} (87%) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 8c98644f..a3075337 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -46,10 +46,22 @@ public class ConfigConstants { public static final String BASE_STACK_NAME = "base"; - public static final String BASE_STACK_TEMPLATE_PATH = "/cloudformation/vpc-and-base.json"; + public static final String BASE_STACK_TEMPLATE_PATH = "/cloudformation/base.yaml"; public static final String CMS_STACK_TEMPLATE_PATH = "/cloudformation/cms-cluster.json"; + public static final String DATABASE_STACK_TEMPLATE_PATH = "/cloudformation/database.yaml"; + + public static final String LOAD_BALANCER_STACK_TEMPLATE_PATH = "/cloudformation/load-balancer.yaml"; + + public static final String ROUTE53_TEMPLATE_PATH = "/cloudformation/route53.yaml"; + + public static final String SECURITY_GROUPS_STACK_TEMPLATE_PATH = "/cloudformation/security-groups.yaml"; + + public static final String VPC_STACK_TEMPLATE_PATH = "/cloudformation/vpc.yaml"; + + public static final String WAF_STACK_TEMPLATE_PATH = "/cloudformation/web-app-firewall.yaml"; + public static final String CMS_ENV_CONFIG_PATH = "data/cms/environment.properties"; public static final String VERSION_PROPERTY = "cli.version"; diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index c47d74b3..369f6bf0 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,12 @@ import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.core.CreateLoadBalancerCommand; +import com.nike.cerberus.command.core.CreateRoute53Command; +import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; +import com.nike.cerberus.command.core.CreateVpcCommand; +import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; import com.nike.cerberus.command.core.ViewConfigCommand; import com.nike.cerberus.command.core.CreateBaseCommand; @@ -172,6 +178,12 @@ private void registerAllCommands() { registerCommand(new CreateCerberusBackupCommand()); registerCommand(new SetBackupAdminPrincipalsCommand()); registerCommand(new GenerateCertsCommand()); + registerCommand(new CreateVpcCommand()); + registerCommand(new CreateWafCommand()); + registerCommand(new CreateDatabaseCommand()); + registerCommand(new CreateRoute53Command()); + registerCommand(new CreateSecurityGroupsCommand()); + registerCommand(new CreateLoadBalancerCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 5edecdef..fd65ed7c 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -21,6 +21,12 @@ import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.core.CreateLoadBalancerCommand; +import com.nike.cerberus.command.core.CreateRoute53Command; +import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; +import com.nike.cerberus.command.core.CreateVpcCommand; +import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; @@ -88,6 +94,18 @@ private static List getArgsForCommand(EnvironmentConfig environmentConfi return getUpdateStackCommandArgs(environmentConfig, passedArgs); case UpdateCmsConfigCommand.COMMAND_NAME: return getCreateCmsConfigCommandArgs(environmentConfig); + case CreateVpcCommand.COMMAND_NAME: + return getCreateVpcCommandArgs(environmentConfig); + case CreateSecurityGroupsCommand.COMMAND_NAME: + return getCreateSecurityGroupsCommandArgs(environmentConfig); + case CreateDatabaseCommand.COMMAND_NAME: + return getCreateDatabaseCommandArgs(environmentConfig); + case CreateLoadBalancerCommand.COMMAND_NAME: + return getCreateLoadBalancerCommandArgs(environmentConfig); + case CreateRoute53Command.COMMAND_NAME: + return getCreateRoute53CommandArgs(environmentConfig); + case CreateWafCommand.COMMAND_NAME: + return getCreateWafCommandArgs(environmentConfig); default: return new LinkedList<>(); } @@ -144,19 +162,10 @@ private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List args.add(StackDelegate.KEY_PAIR_NAME_LONG_ARG); args.add(stack.getKeyPairName()); - if (stack.getDesiredInstances() != null) { - args.add(StackDelegate.DESIRED_INSTANCES_LONG_ARG); - args.add(stack.getDesiredInstances()); - } - if (stack.getMinInstances() != null) { - args.add(StackDelegate.MIN_INSTANCES_LONG_ARG); - args.add(stack.getMinInstances()); - } - if (stack.getMaxInstances() != null) { - args.add(StackDelegate.MAX_INSTANCES_LONG_ARG); - args.add(stack.getMaxInstances()); - } + addTagArgs(environmentConfig, args); + } + private static void addTagArgs(EnvironmentConfig environmentConfig, List args) { args.add(StackDelegate.COST_CENTER_LONG_ARG); args.add(environmentConfig.getCostCenter()); args.add(StackDelegate.OWNER_EMAIL_LONG_ARG); @@ -200,14 +209,10 @@ private static List getUploadCertFilesCommandArgs(EnvironmentConfig envi private static List getCreateBaseCommandArgs(EnvironmentConfig config) { List args = new LinkedList<>(); - args.add(CreateBaseCommand.OWNER_EMAIL_LONG_ARG); - args.add(config.getOwnerEmail()); - args.add(CreateBaseCommand.COST_CENTER_LONG_ARG); - args.add(config.getCostCenter()); + addTagArgs(config, args); + args.add(CreateBaseCommand.ADMIN_ROLE_ARN_LONG_ARG); args.add(config.getAdminRoleArn()); - args.add(CreateBaseCommand.VPC_HOSTED_ZONE_NAME_LONG_ARG); - args.add(config.getVpcHostedZoneName()); return args; } @@ -253,6 +258,55 @@ private static List getUpdateStackCommandArgs(EnvironmentConfig environm return args; } + private static List getCreateVpcCommandArgs(EnvironmentConfig config) { + List args = new LinkedList<>(); + addTagArgs(config, args); + return args; + } + + private static List getCreateSecurityGroupsCommandArgs(EnvironmentConfig config) { + List args = new LinkedList<>(); + + if (config.getSecurityGroups() != null && + config.getSecurityGroups().getLoadBalancerCidrBlock() != null) { + args.add(CreateSecurityGroupsCommand.LOAD_BALANCER_CIDR_BLOCK_LONG_ARG); + args.add(config.getSecurityGroups().getLoadBalancerCidrBlock()); + } + + addTagArgs(config, args); + + return args; + } + + private static List getCreateDatabaseCommandArgs(EnvironmentConfig config) { + List args = new LinkedList<>(); + addTagArgs(config, args); + return args; + } + + private static List getCreateLoadBalancerCommandArgs(EnvironmentConfig config) { + List args = new LinkedList<>(); + addTagArgs(config, args); + return args; + } + + private static List getCreateRoute53CommandArgs(EnvironmentConfig config) { + List args = new LinkedList<>(); + + args.add(CreateRoute53Command.HOSTNAME_LONG_ARG); + args.add(config.getHostname()); + args.add(CreateRoute53Command.HOSTED_ZONE_ID); + args.add(config.getHostedZoneId()); + + return args; + } + + private static List getCreateWafCommandArgs(EnvironmentConfig config) { + List args = new LinkedList<>(); + addTagArgs(config, args); + return args; + } + private static String getStackName(String[] passedArgs) { for (int i = 0; i < passedArgs.length; i++) { if (StringUtils.equals(passedArgs[i], STACK_NAME_KEY)) { diff --git a/src/main/java/com/nike/cerberus/command/StackDelegate.java b/src/main/java/com/nike/cerberus/command/StackDelegate.java index af98eab6..1e41e03b 100644 --- a/src/main/java/com/nike/cerberus/command/StackDelegate.java +++ b/src/main/java/com/nike/cerberus/command/StackDelegate.java @@ -16,8 +16,12 @@ package com.nike.cerberus.command; +import com.beust.jcommander.DynamicParameter; import com.beust.jcommander.Parameter; +import java.util.HashMap; +import java.util.Map; + /** * Represents CloudFormation stack parameters that are common to all Cerberus cluster components. */ @@ -29,9 +33,7 @@ public class StackDelegate { public static final String OWNER_GROUP_LONG_ARG = "--owner-group"; public static final String OWNER_EMAIL_LONG_ARG = "--owner-email"; public static final String COST_CENTER_LONG_ARG = "--costcenter"; - public static final String DESIRED_INSTANCES_LONG_ARG = "--desired-instances"; - public static final String MAX_INSTANCES_LONG_ARG = "--max-instances"; - public static final String MIN_INSTANCES_LONG_ARG = "--min-instances"; + public static final String PARAMETER_SHORT_ARG = "-P"; @Parameter(names = AMI_ID_LONG_ARG, description = "The AMI ID for the specified stack.", required = true) private String amiId; @@ -57,14 +59,8 @@ public class StackDelegate { required = true) private String costcenter; - @Parameter(names = DESIRED_INSTANCES_LONG_ARG, description = "Desired number of auto scaling instances.") - private int desiredInstances = 3; - - @Parameter(names = MAX_INSTANCES_LONG_ARG, description = "Maximum number of auto scaling instances (must be larger than min).") - private int maximumInstances = 4; - - @Parameter(names = MIN_INSTANCES_LONG_ARG, description = "Minimum number of auto scaling instances") - private int minimumInstances = 3; + @DynamicParameter(names = PARAMETER_SHORT_ARG, description = "Dynamic parameters for overriding the values for specific parameters in the CloudFormation.") + private Map dynamicParameters = new HashMap<>(); public String getAmiId() { return amiId; @@ -90,15 +86,7 @@ public String getCostcenter() { return costcenter; } - public int getDesiredInstances() { - return desiredInstances; - } - - public int getMaximumInstances() { - return maximumInstances; - } - - public int getMinimumInstances() { - return minimumInstances; + public Map getDynamicParameters() { + return dynamicParameters; } } diff --git a/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java b/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java index df95951c..23a6eb65 100644 --- a/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java +++ b/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java b/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java index b6406535..1d526d7a 100644 --- a/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java +++ b/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java @@ -24,7 +24,7 @@ import java.util.List; -import static com.nike.cerberus.command.cms.UpdateCmsConfigCommand.COMMAND_NAME; +import static com.nike.cerberus.command.cms.CreateCmsCmkCommand.COMMAND_NAME; /** * Command to create the CMS cluster. diff --git a/src/main/java/com/nike/cerberus/command/cms/CreateCmsConfigCommand.java b/src/main/java/com/nike/cerberus/command/cms/CreateCmsConfigCommand.java index 6788eb42..c00da752 100644 --- a/src/main/java/com/nike/cerberus/command/cms/CreateCmsConfigCommand.java +++ b/src/main/java/com/nike/cerberus/command/cms/CreateCmsConfigCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.Map; -import static com.nike.cerberus.command.cms.CreateCmsClusterCommand.COMMAND_NAME; +import static com.nike.cerberus.command.cms.CreateCmsConfigCommand.COMMAND_NAME; /** * Command to create the CMS cluster. diff --git a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java index 8b69e2cb..e5ca9fa1 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,60 +18,46 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import com.beust.jcommander.ParametersDelegate; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.Command; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; +import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.CreateBaseOperation; +import com.nike.cerberus.service.CloudFormationService; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Map; import static com.nike.cerberus.command.core.CreateBaseCommand.COMMAND_NAME; +import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** * Command for creating the base components for Cerberus. */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Creates the base components to support Cerberus.") +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the IAM roles, KMS keys, and S3 buckets for Cerberus") public class CreateBaseCommand implements Command { public static final String COMMAND_NAME = "create-base"; + public static final String ADMIN_ROLE_ARN_LONG_ARG = "--admin-role-arn"; - public static final String VPC_HOSTED_ZONE_NAME_LONG_ARG = "--vpc-hosted-zone-name"; - public static final String OWNER_EMAIL_LONG_ARG = "--owner-email"; - public static final String COST_CENTER_LONG_ARG = "--costcenter"; @Parameter(names = ADMIN_ROLE_ARN_LONG_ARG, description = "A IAM role ARN that will be given elevated privileges for the KMS CMK created.", required = true) private String adminRoleArn; - @Parameter(names = VPC_HOSTED_ZONE_NAME_LONG_ARG, - description = "The Route 53 hosted zone name that will be created for CNAME records used by internal ELBs.", - required = true) - private String vpcHostedZoneName; - - @Parameter(names = OWNER_EMAIL_LONG_ARG, - description = "The e-mail for who owns the provisioned resources. Will be tagged on all resources.", - required = true) - private String ownerEmail; - - @Parameter(names = COST_CENTER_LONG_ARG, - description = "Costcenter for where to bill provisioned resources. Will be tagged on all resources.", - required = true) - private String costcenter; - public String getAdminRoleArn() { return adminRoleArn; } - public String getVpcHostedZoneName() { - return vpcHostedZoneName; - } - - public String getOwnerEmail() { - return ownerEmail; - } - - public String getCostcenter() { - return costcenter; - } - @Override public String getCommandName() { return COMMAND_NAME; @@ -81,4 +67,5 @@ public String getCommandName() { public Class> getOperationClass() { return CreateBaseOperation.class; } + } diff --git a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java new file mode 100644 index 00000000..9306c517 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 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.TagParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateDatabaseOperation; + +import static com.nike.cerberus.command.core.CreateDatabaseCommand.COMMAND_NAME; + +/** + * Command to create the database for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the database to be used by the Cerberus Management Service (CMS)") +public class CreateDatabaseCommand implements Command { + + public static final String COMMAND_NAME = "create-database"; + + @ParametersDelegate + private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); + + public TagParametersDelegate getTagsDelegate() { + return tagsDelegate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateDatabaseOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java new file mode 100644 index 00000000..3da54d77 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 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.Parameters; +import com.beust.jcommander.ParametersDelegate; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateLoadBalancerOperation; + +import static com.nike.cerberus.command.core.CreateLoadBalancerCommand.COMMAND_NAME; + +/** + * Command to create the load balancer for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the load balancer used for Cerberus.") +public class CreateLoadBalancerCommand implements Command { + + public static final String COMMAND_NAME = "create-load-balancer"; + + @ParametersDelegate + private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); + + public TagParametersDelegate getTagsDelegate() { + return tagsDelegate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateLoadBalancerOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java new file mode 100644 index 00000000..d02e979e --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateRoute53Operation; + +import static com.nike.cerberus.command.core.CreateRoute53Command.COMMAND_NAME; + +/** + * Command to create the Route53 record for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the Route53 record for use by Cerberus") +public class CreateRoute53Command implements Command { + + public static final String COMMAND_NAME = "create-route53-record"; + + public static final String HOSTNAME_LONG_ARG = "--hostname"; + + public static final String HOSTED_ZONE_ID = "--hosted-zone-id"; + + @Parameter(names = HOSTNAME_LONG_ARG, + description = "The hostname of the Route53 record to be created for Cerberus (e.g. .cerberus.example.com)") + private String cerberusHostname; + + @Parameter(names = HOSTED_ZONE_ID, + description = "The Route53 Hosted Zone in which to create the new Cerberus record") + private String hostedZoneId; + + public String getCerberusHostname() { + return cerberusHostname; + } + + public String getHostedZoneId() { + return hostedZoneId; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateRoute53Operation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java new file mode 100644 index 00000000..5752ce53 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateSecurityGroupsOperation; + +import static com.nike.cerberus.command.core.CreateSecurityGroupsCommand.COMMAND_NAME; + +/** + * Command to create the security groups for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the IAM roles, KMS keys, and S3 buckets for Cerberus") +public class CreateSecurityGroupsCommand implements Command { + + public static final String COMMAND_NAME = "create-security-groups"; + + public static final String LOAD_BALANCER_CIDR_BLOCK_LONG_ARG = "--load-balancer-cidr"; + + @Parameter(names = LOAD_BALANCER_CIDR_BLOCK_LONG_ARG, + description = "The CIDR from which to allow traffic to the load balancer") + private String loadBalancerCidr; + + public String getLoadBalancerCidr() { + return loadBalancerCidr; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateSecurityGroupsOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateVpcCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateVpcCommand.java new file mode 100644 index 00000000..aac7e8a4 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateVpcCommand.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 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.Parameters; +import com.beust.jcommander.ParametersDelegate; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateVpcOperation; + +import static com.nike.cerberus.command.core.CreateVpcCommand.COMMAND_NAME; + +/** + * Command to create the VPC in which Cerberus components will live. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the VPC in which Cerberus components live") +public class CreateVpcCommand implements Command { + + public static final String COMMAND_NAME = "create-vpc"; + + @ParametersDelegate + private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); + + public TagParametersDelegate getTagsDelegate() { + return tagsDelegate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateVpcOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java new file mode 100644 index 00000000..4866c97a --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 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.Parameters; +import com.beust.jcommander.ParametersDelegate; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateLoadBalancerOperation; + +import static com.nike.cerberus.command.core.CreateWafCommand.COMMAND_NAME; + +/** + * Command to create the WAF for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the Web Application Firewall (WAF) for the load balancer.") +public class CreateWafCommand implements Command { + + public static final String COMMAND_NAME = "create-waf"; + + @ParametersDelegate + private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); + + public TagParametersDelegate getTagsDelegate() { + return tagsDelegate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateLoadBalancerOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java b/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java index 534e298f..a13f0ef0 100644 --- a/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/command/core/RestoreCerberusBackupCommand.java b/src/main/java/com/nike/cerberus/command/core/RestoreCerberusBackupCommand.java index 2d65fce9..7ce8ba8c 100644 --- a/src/main/java/com/nike/cerberus/command/core/RestoreCerberusBackupCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/RestoreCerberusBackupCommand.java @@ -22,7 +22,7 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.RestoreCerberusBackupOperation; -import static com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand.COMMAND_NAME; +import static com.nike.cerberus.command.core.RestoreCerberusBackupCommand.COMMAND_NAME; /** * Command for restoring Safe Deposit Box Metadata and Vault secret data for SDBs from backups that are in S3 from diff --git a/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java b/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java index a0b2bbab..c34b584d 100644 --- a/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2017 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; diff --git a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java index 7d818b9d..4bf88a5e 100644 --- a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java @@ -72,15 +72,6 @@ public class UpdateStackCommand implements Command { description = "Flag for overwriting existing CloudFormation template") private boolean overwriteTemplate; - @Parameter(names = StackDelegate.DESIRED_INSTANCES_LONG_ARG, description = "Desired number of auto scaling instances.") - private Integer desiredInstances; - - @Parameter(names = StackDelegate.MAX_INSTANCES_LONG_ARG, description = "Maximum number of auto scaling instances.") - private Integer maximumInstances; - - @Parameter(names = StackDelegate.MIN_INSTANCES_LONG_ARG, description = "Minimum number of autos scaling instances") - private Integer minimumInstances; - @Parameter(names = SKIP_AMI_TAG_CHECK_ARG, description = SKIP_AMI_TAG_CHECK_DESCRIPTION) private boolean skipAmiTagCheck; @@ -124,18 +115,6 @@ public Map getDynamicParameters() { return dynamicParameters; } - public Integer getDesiredInstances() { - return desiredInstances; - } - - public Integer getMaximumInstances() { - return maximumInstances; - } - - public Integer getMinimumInstances() { - return minimumInstances; - } - public boolean isSkipAmiTagCheck() { return skipAmiTagCheck; } diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java b/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java index e3052d19..9a0c3ca8 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/command/core/ViewConfigCommand.java b/src/main/java/com/nike/cerberus/command/core/ViewConfigCommand.java index 671b3c20..eb24742f 100644 --- a/src/main/java/com/nike/cerberus/command/core/ViewConfigCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/ViewConfigCommand.java @@ -22,7 +22,7 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.ViewConfigOperation; -import static com.nike.cerberus.command.cms.CreateCmsClusterCommand.COMMAND_NAME; +import static com.nike.cerberus.command.core.ViewConfigCommand.COMMAND_NAME; /** * Command to view configuration files in S3. diff --git a/src/main/java/com/nike/cerberus/command/core/WhitelistCidrForVpcAccessCommand.java b/src/main/java/com/nike/cerberus/command/core/WhitelistCidrForVpcAccessCommand.java index 8144659e..9646d411 100644 --- a/src/main/java/com/nike/cerberus/command/core/WhitelistCidrForVpcAccessCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/WhitelistCidrForVpcAccessCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.WhitelistCidrForVpcAccessOpertaion; +import com.nike.cerberus.operation.core.WhitelistCidrForVpcAccessOperation; import java.util.ArrayList; import java.util.List; @@ -58,6 +58,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return WhitelistCidrForVpcAccessOpertaion.class; + return WhitelistCidrForVpcAccessOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java index 49aa498f..c9fb582b 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 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,306 +21,18 @@ */ public class BaseOutputs { - private String vpcId; - - private String gatewayIamRoleArn; - - private String cmsIamRoleArn; - - private String consulIamRoleArn; - - private String vaultIamRoleArn; - - private String cloudFrontLogProcessorLambdaIamRoleArn; - - private String gatewayInstanceProfileName; - - private String cmsInstanceProfileName; - - private String consulInstanceProfileName; - - private String vaultInstanceProfileName; - - private String toolsIngressSgId; - - private String gatewayElbSgId; - - private String gatewayServerSgId; - - private String cmsElbSgId; - - private String cmsSgId; - - private String cmsDbSgId; - - private String vaultServerElbSgId; - - private String vaultClientSgId; - - private String vaultServerSgId; - - private String consulClientSgId; - - private String consulServerSgId; - - private String configFileKeyId; - - private String dashboardBucketName; - - private String dashboardBucketWebsiteUrl; - - private String configBucketName; - private String configBucketDomainName; - private String cmsDbId; - - private String cmsDbAddress; - - private String cmsDbPort; - - private String cmsDbJdbcConnectionString; - - private String vpcHostedZoneId; + private String configBucketName; - private String vpcSubnetIdForAz1; + private String configFileKeyId; - private String vpcSubnetIdForAz2; + private String cmsIamRoleArn; - private String vpcSubnetIdForAz3; + private String cmsIamRoleName; private String cmsKmsPolicyId; - private String subnetCidrBlockForAz1; - - private String subnetCidrBlockForAz2; - - private String subnetCidrBlockForAz3; - - public String getVpcId() { - return vpcId; - } - - public BaseOutputs setVpcId(String vpcId) { - this.vpcId = vpcId; - return this; - } - - public String getGatewayIamRoleArn() { - return gatewayIamRoleArn; - } - - public BaseOutputs setGatewayIamRoleArn(String gatewayIamRoleArn) { - this.gatewayIamRoleArn = gatewayIamRoleArn; - return this; - } - - public String getCmsIamRoleArn() { - return cmsIamRoleArn; - } - - public BaseOutputs setCmsIamRoleArn(String cmsIamRoleArn) { - this.cmsIamRoleArn = cmsIamRoleArn; - return this; - } - - public String getConsulIamRoleArn() { - return consulIamRoleArn; - } - - public BaseOutputs setConsulIamRoleArn(String consulIamRoleArn) { - this.consulIamRoleArn = consulIamRoleArn; - return this; - } - - public String getVaultIamRoleArn() { - return vaultIamRoleArn; - } - - public BaseOutputs setVaultIamRoleArn(String vaultIamRoleArn) { - this.vaultIamRoleArn = vaultIamRoleArn; - return this; - } - - public String getCloudFrontLogProcessorLambdaIamRoleArn() { - return cloudFrontLogProcessorLambdaIamRoleArn; - } - - public void setCloudFrontLogProcessorLambdaIamRoleArn(String cloudFrontLogProcessorLambdaIamRoleArn) { - this.cloudFrontLogProcessorLambdaIamRoleArn = cloudFrontLogProcessorLambdaIamRoleArn; - } - - public String getGatewayInstanceProfileName() { - return gatewayInstanceProfileName; - } - - public BaseOutputs setGatewayInstanceProfileName(String gatewayInstanceProfileName) { - this.gatewayInstanceProfileName = gatewayInstanceProfileName; - return this; - } - - public String getCmsInstanceProfileName() { - return cmsInstanceProfileName; - } - - public BaseOutputs setCmsInstanceProfileName(String cmsInstanceProfileName) { - this.cmsInstanceProfileName = cmsInstanceProfileName; - return this; - } - - public String getConsulInstanceProfileName() { - return consulInstanceProfileName; - } - - public BaseOutputs setConsulInstanceProfileName(String consulInstanceProfileName) { - this.consulInstanceProfileName = consulInstanceProfileName; - return this; - } - - public String getVaultInstanceProfileName() { - return vaultInstanceProfileName; - } - - public BaseOutputs setVaultInstanceProfileName(String vaultInstanceProfileName) { - this.vaultInstanceProfileName = vaultInstanceProfileName; - return this; - } - - public String getToolsIngressSgId() { - return toolsIngressSgId; - } - - public BaseOutputs setToolsIngressSgId(String toolsIngressSgId) { - this.toolsIngressSgId = toolsIngressSgId; - return this; - } - - public String getGatewayElbSgId() { - return gatewayElbSgId; - } - - public BaseOutputs setGatewayElbSgId(String gatewayElbSgId) { - this.gatewayElbSgId = gatewayElbSgId; - return this; - } - - public String getGatewayServerSgId() { - return gatewayServerSgId; - } - - public BaseOutputs setGatewayServerSgId(String gatewayServerSgId) { - this.gatewayServerSgId = gatewayServerSgId; - return this; - } - - public String getCmsElbSgId() { - return cmsElbSgId; - } - - public BaseOutputs setCmsElbSgId(String cmsElbSgId) { - this.cmsElbSgId = cmsElbSgId; - return this; - } - - public String getCmsSgId() { - return cmsSgId; - } - - public BaseOutputs setCmsSgId(String cmsSgId) { - this.cmsSgId = cmsSgId; - return this; - } - - public String getCmsDbSgId() { - return cmsDbSgId; - } - - public BaseOutputs setCmsDbSgId(String cmsDbSgId) { - this.cmsDbSgId = cmsDbSgId; - return this; - } - - public String getVaultServerElbSgId() { - return vaultServerElbSgId; - } - - public BaseOutputs setVaultServerElbSgId(String vaultServerElbSgId) { - this.vaultServerElbSgId = vaultServerElbSgId; - return this; - } - - public String getVaultClientSgId() { - return vaultClientSgId; - } - - public BaseOutputs setVaultClientSgId(String vaultClientSgId) { - this.vaultClientSgId = vaultClientSgId; - return this; - } - - public String getVaultServerSgId() { - return vaultServerSgId; - } - - public BaseOutputs setVaultServerSgId(String vaultServerSgId) { - this.vaultServerSgId = vaultServerSgId; - return this; - } - - public String getConsulClientSgId() { - return consulClientSgId; - } - - public BaseOutputs setConsulClientSgId(String consulClientSgId) { - this.consulClientSgId = consulClientSgId; - return this; - } - - public String getConsulServerSgId() { - return consulServerSgId; - } - - public BaseOutputs setConsulServerSgId(String consulServerSgId) { - this.consulServerSgId = consulServerSgId; - return this; - } - - public String getConfigFileKeyId() { - return configFileKeyId; - } - - public BaseOutputs setConfigFileKeyId(String configFileKeyId) { - this.configFileKeyId = configFileKeyId; - return this; - } - - public String getDashboardBucketName() { - return dashboardBucketName; - } - - public BaseOutputs setDashboardBucketName(String dashboardBucketName) { - this.dashboardBucketName = dashboardBucketName; - return this; - } - - public String getDashboardBucketWebsiteUrl() { - return dashboardBucketWebsiteUrl; - } - - public BaseOutputs setDashboardBucketWebsiteUrl(String dashboardBucketWebsiteUrl) { - this.dashboardBucketWebsiteUrl = dashboardBucketWebsiteUrl; - return this; - } - - public String getConfigBucketName() { - return configBucketName; - } - - public BaseOutputs setConfigBucketName(String configBucketName) { - this.configBucketName = configBucketName; - return this; - } - public String getConfigBucketDomainName() { return configBucketDomainName; } @@ -330,75 +42,39 @@ public BaseOutputs setConfigBucketDomainName(String configBucketDomainName) { return this; } - public String getCmsDbId() { - return cmsDbId; - } - - public BaseOutputs setCmsDbId(String cmsDbId) { - this.cmsDbId = cmsDbId; - return this; - } - - public String getCmsDbAddress() { - return cmsDbAddress; - } - - public BaseOutputs setCmsDbAddress(String cmsDbAddress) { - this.cmsDbAddress = cmsDbAddress; - return this; - } - - public String getCmsDbPort() { - return cmsDbPort; - } - - public BaseOutputs setCmsDbPort(String cmsDbPort) { - this.cmsDbPort = cmsDbPort; - return this; - } - - public String getCmsDbJdbcConnectionString() { - return cmsDbJdbcConnectionString; - } - - public BaseOutputs setCmsDbJdbcConnectionString(String cmsDbJdbcConnectionString) { - this.cmsDbJdbcConnectionString = cmsDbJdbcConnectionString; - return this; - } - - public String getVpcHostedZoneId() { - return vpcHostedZoneId; + public String getConfigBucketName() { + return configBucketName; } - public BaseOutputs setVpcHostedZoneId(String vpcHostedZoneId) { - this.vpcHostedZoneId = vpcHostedZoneId; + public BaseOutputs setConfigBucketName(String configBucketName) { + this.configBucketName = configBucketName; return this; } - public String getVpcSubnetIdForAz1() { - return vpcSubnetIdForAz1; + public String getConfigFileKeyId() { + return configFileKeyId; } - public BaseOutputs setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { - this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; + public BaseOutputs setConfigFileKeyId(String configFileKeyId) { + this.configFileKeyId = configFileKeyId; return this; } - public String getVpcSubnetIdForAz2() { - return vpcSubnetIdForAz2; + public String getCmsIamRoleArn() { + return cmsIamRoleArn; } - public BaseOutputs setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { - this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; + public BaseOutputs setCmsIamRoleArn(String cmsIamRoleArn) { + this.cmsIamRoleArn = cmsIamRoleArn; return this; } - public String getVpcSubnetIdForAz3() { - return vpcSubnetIdForAz3; + public String getCmsIamRoleName() { + return cmsIamRoleName; } - public BaseOutputs setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { - this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; + public BaseOutputs setCmsIamRoleName(String cmsIamRoleName) { + this.cmsIamRoleName = cmsIamRoleName; return this; } @@ -410,31 +86,4 @@ public BaseOutputs setCmsKmsPolicyId(String cmsKmsPolicyId) { this.cmsKmsPolicyId = cmsKmsPolicyId; return this; } - - public String getSubnetCidrBlockForAz1() { - return subnetCidrBlockForAz1; - } - - public BaseOutputs setSubnetCidrBlockForAz1(String subnetCidrBlockForAz1) { - this.subnetCidrBlockForAz1 = subnetCidrBlockForAz1; - return this; - } - - public String getSubnetCidrBlockForAz2() { - return subnetCidrBlockForAz2; - } - - public BaseOutputs setSubnetCidrBlockForAz2(String subnetCidrBlockForAz2) { - this.subnetCidrBlockForAz2 = subnetCidrBlockForAz2; - return this; - } - - public String getSubnetCidrBlockForAz3() { - return subnetCidrBlockForAz3; - } - - public BaseOutputs setSubnetCidrBlockForAz3(String subnetCidrBlockForAz3) { - this.subnetCidrBlockForAz3 = subnetCidrBlockForAz3; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java index 93d57227..5101b012 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,26 +25,6 @@ public class BaseParameters implements TagParameters { private String accountAdminArn; - private String az1; - - private String az2; - - private String az3; - - private String cmsDbAllocatedStorage; - - private String cmsDbInstanceSize; - - private String cmsDbName; - - private String cmsDbMasterUsername; - - private String cmsDbMasterPassword; - - private Integer cmsDbPort; - - private String vpcHostedZoneName; - @JsonUnwrapped private TagParametersDelegate tagParameters = new TagParametersDelegate(); @@ -57,96 +37,6 @@ public BaseParameters setAccountAdminArn(String accountAdminArn) { return this; } - public String getAz1() { - return az1; - } - - public BaseParameters setAz1(String az1) { - this.az1 = az1; - return this; - } - - public String getAz2() { - return az2; - } - - public BaseParameters setAz2(String az2) { - this.az2 = az2; - return this; - } - - public String getAz3() { - return az3; - } - - public BaseParameters setAz3(String az3) { - this.az3 = az3; - return this; - } - - public String getCmsDbAllocatedStorage() { - return cmsDbAllocatedStorage; - } - - public BaseParameters setCmsDbAllocatedStorage(String cmsDbAllocatedStorage) { - this.cmsDbAllocatedStorage = cmsDbAllocatedStorage; - return this; - } - - public String getCmsDbInstanceSize() { - return cmsDbInstanceSize; - } - - public BaseParameters setCmsDbInstanceSize(String cmsDbInstanceSize) { - this.cmsDbInstanceSize = cmsDbInstanceSize; - return this; - } - - public String getCmsDbName() { - return cmsDbName; - } - - public BaseParameters setCmsDbName(String cmsDbName) { - this.cmsDbName = cmsDbName; - return this; - } - - public String getCmsDbMasterUsername() { - return cmsDbMasterUsername; - } - - public BaseParameters setCmsDbMasterUsername(String cmsDbMasterUsername) { - this.cmsDbMasterUsername = cmsDbMasterUsername; - return this; - } - - public String getCmsDbMasterPassword() { - return cmsDbMasterPassword; - } - - public BaseParameters setCmsDbMasterPassword(String cmsDbMasterPassword) { - this.cmsDbMasterPassword = cmsDbMasterPassword; - return this; - } - - public Integer getCmsDbPort() { - return cmsDbPort; - } - - public BaseParameters setCmsDbPort(Integer cmsDbPort) { - this.cmsDbPort = cmsDbPort; - return this; - } - - public String getVpcHostedZoneName() { - return vpcHostedZoneName; - } - - public BaseParameters setVpcHostedZoneName(String vpcHostedZoneName) { - this.vpcHostedZoneName = vpcHostedZoneName; - return this; - } - @Override public TagParametersDelegate getTagParameters() { return tagParameters; diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsOutputs.java index 6a628ec6..054c2a5b 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsOutputs.java @@ -25,15 +25,7 @@ public class CmsOutputs { private String launchConfigurationLogicalId; - private String elbLogicalId; - - private String elbCanonicalHostedZoneNameId; - - private String elbDnsName; - - private String elbSourceSecurityGroupName; - - private String elbSourceSecurityGroupOwnerAlias; + private String cmsInstanceProfileName; public String getAutoscalingGroupLogicalId() { return autoscalingGroupLogicalId; @@ -53,48 +45,12 @@ public CmsOutputs setLaunchConfigurationLogicalId(String launchConfigurationLogi return this; } - public String getElbLogicalId() { - return elbLogicalId; - } - - public CmsOutputs setElbLogicalId(String elbLogicalId) { - this.elbLogicalId = elbLogicalId; - return this; - } - - public String getElbCanonicalHostedZoneNameId() { - return elbCanonicalHostedZoneNameId; - } - - public CmsOutputs setElbCanonicalHostedZoneNameId(String elbCanonicalHostedZoneNameId) { - this.elbCanonicalHostedZoneNameId = elbCanonicalHostedZoneNameId; - return this; - } - - public String getElbDnsName() { - return elbDnsName; - } - - public CmsOutputs setElbDnsName(String elbDnsName) { - this.elbDnsName = elbDnsName; - return this; - } - - public String getElbSourceSecurityGroupName() { - return elbSourceSecurityGroupName; - } - - public CmsOutputs setElbSourceSecurityGroupName(String elbSourceSecurityGroupName) { - this.elbSourceSecurityGroupName = elbSourceSecurityGroupName; - return this; - } - - public String getElbSourceSecurityGroupOwnerAlias() { - return elbSourceSecurityGroupOwnerAlias; + public String getCmsInstanceProfileName() { + return cmsInstanceProfileName; } - public CmsOutputs setElbSourceSecurityGroupOwnerAlias(String elbSourceSecurityGroupOwnerAlias) { - this.elbSourceSecurityGroupOwnerAlias = elbSourceSecurityGroupOwnerAlias; + public CmsOutputs setCmsInstanceProfileName(String cmsInstanceProfileName) { + this.cmsInstanceProfileName = cmsInstanceProfileName; return this; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java index dec33c65..21f1d42b 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java @@ -23,15 +23,11 @@ */ public class CmsParameters implements LaunchConfigParameters { - private String vpcId; + private String baseStackName; - private String instanceProfileName; + private String loadBalancerStackName; - private String cmsElbSgId; - - private String cmsSgId; - - private String toolsIngressSgId; + private String sgStackName; private String vpcSubnetIdForAz1; @@ -39,61 +35,36 @@ public class CmsParameters implements LaunchConfigParameters { private String vpcSubnetIdForAz3; - private String hostedZoneId; - - private String cname; - - @JsonUnwrapped - private SslConfigParametersDelegate sslConfigParameters = new SslConfigParametersDelegate(); - @JsonUnwrapped private LaunchConfigParametersDelegate launchConfigParameters = new LaunchConfigParametersDelegate(); @JsonUnwrapped private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getVpcId() { - return vpcId; - } - - public CmsParameters setVpcId(String vpcId) { - this.vpcId = vpcId; - return this; - } - - public String getInstanceProfileName() { - return instanceProfileName; - } - - public CmsParameters setInstanceProfileName(String instanceProfileName) { - this.instanceProfileName = instanceProfileName; - return this; - } - - public String getCmsElbSgId() { - return cmsElbSgId; + public String getBaseStackName() { + return baseStackName; } - public CmsParameters setCmsElbSgId(String cmsElbSgId) { - this.cmsElbSgId = cmsElbSgId; + public CmsParameters setBaseStackName(String baseStackName) { + this.baseStackName = baseStackName; return this; } - public String getCmsSgId() { - return cmsSgId; + public String getLoadBalancerStackName() { + return loadBalancerStackName; } - public CmsParameters setCmsSgId(String cmsSgId) { - this.cmsSgId = cmsSgId; + public CmsParameters setLoadBalancerStackName(String loadBalancerStackName) { + this.loadBalancerStackName = loadBalancerStackName; return this; } - public String getToolsIngressSgId() { - return toolsIngressSgId; + public String getSgStackName() { + return sgStackName; } - public CmsParameters setToolsIngressSgId(String toolsIngressSgId) { - this.toolsIngressSgId = toolsIngressSgId; + public CmsParameters setSgStackName(String sgStackName) { + this.sgStackName = sgStackName; return this; } @@ -124,34 +95,6 @@ public CmsParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { return this; } - public String getHostedZoneId() { - return hostedZoneId; - } - - public CmsParameters setHostedZoneId(String hostedZoneId) { - this.hostedZoneId = hostedZoneId; - return this; - } - - public String getCname() { - return cname; - } - - public CmsParameters setCname(String cname) { - this.cname = cname; - return this; - } - - @Override - public SslConfigParametersDelegate getSslConfigParameters() { - return sslConfigParameters; - } - - public CmsParameters setSslConfigParameters(SslConfigParametersDelegate sslConfigParameters) { - this.sslConfigParameters = sslConfigParameters; - return this; - } - public LaunchConfigParametersDelegate getLaunchConfigParameters() { return launchConfigParameters; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java new file mode 100644 index 00000000..e15042c9 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 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 database stack outputs. + */ +public class DatabaseOutputs { + + private Integer cmsDbAddress; + + private String cmsDbInstanceId1; + + private String cmsDbInstanceId2; + + private String cmsDbJdbcConnectionString; + + public Integer getCmsDbAddress() { + return cmsDbAddress; + } + + public DatabaseOutputs setCmsDbAddress(Integer cmsDbAddress) { + this.cmsDbAddress = cmsDbAddress; + return this; + } + + public String getCmsDbInstanceId1() { + return cmsDbInstanceId1; + } + + public DatabaseOutputs setCmsDbInstanceId1(String cmsDbInstanceId1) { + this.cmsDbInstanceId1 = cmsDbInstanceId1; + return this; + } + + public String getCmsDbInstanceId2() { + return cmsDbInstanceId2; + } + + public DatabaseOutputs setCmsDbInstanceId2(String cmsDbInstanceId2) { + this.cmsDbInstanceId2 = cmsDbInstanceId2; + return this; + } + + public String getCmsDbJdbcConnectionString() { + return cmsDbJdbcConnectionString; + } + + public DatabaseOutputs setCmsDbJdbcConnectionString(String cmsDbJdbcConnectionString) { + this.cmsDbJdbcConnectionString = cmsDbJdbcConnectionString; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java new file mode 100644 index 00000000..350f2d75 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017 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; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +/** + * Represents the database stack inputs. + */ +public class DatabaseParameters implements TagParameters { + + private String cmsDbInstanceAz1; + + private String cmsDbInstanceAz2; + + private String cmsDbInstanceAz3; + + private String cmsDbInstanceSize; + + private String cmsDbMasterPassword; + + private String cmsDbMasterUsername; + + private String cmsDbName; + + private String vpcSubnetIdForAz1; + + private String vpcSubnetIdForAz2; + + private String vpcSubnetIdForAz3; + + @JsonUnwrapped + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public String getCmsDbInstanceAz1() { + return cmsDbInstanceAz1; + } + + public DatabaseParameters setCmsDbInstanceAz1(String cmsDbInstanceAz1) { + this.cmsDbInstanceAz1 = cmsDbInstanceAz1; + return this; + } + + public String getCmsDbInstanceAz2() { + return cmsDbInstanceAz2; + } + + public DatabaseParameters setCmsDbInstanceAz2(String cmsDbInstanceAz2) { + this.cmsDbInstanceAz2 = cmsDbInstanceAz2; + return this; + } + + public String getCmsDbInstanceAz3() { + return cmsDbInstanceAz3; + } + + public DatabaseParameters setCmsDbInstanceAz3(String cmsDbInstanceAz3) { + this.cmsDbInstanceAz3 = cmsDbInstanceAz3; + return this; + } + + public String getCmsDbInstanceSize() { + return cmsDbInstanceSize; + } + + public DatabaseParameters setCmsDbInstanceSize(String cmsDbInstanceSize) { + this.cmsDbInstanceSize = cmsDbInstanceSize; + return this; + } + + public String getCmsDbMasterPassword() { + return cmsDbMasterPassword; + } + + public DatabaseParameters setCmsDbMasterPassword(String cmsDbMasterPassword) { + this.cmsDbMasterPassword = cmsDbMasterPassword; + return this; + } + + public String getCmsDbMasterUsername() { + return cmsDbMasterUsername; + } + + public DatabaseParameters setCmsDbMasterUsername(String cmsDbMasterUsername) { + this.cmsDbMasterUsername = cmsDbMasterUsername; + return this; + } + + public String getCmsDbName() { + return cmsDbName; + } + + public DatabaseParameters setCmsDbName(String cmsDbName) { + this.cmsDbName = cmsDbName; + return this; + } + + public String getVpcSubnetIdForAz1() { + return vpcSubnetIdForAz1; + } + + public DatabaseParameters setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { + this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; + return this; + } + + public String getVpcSubnetIdForAz2() { + return vpcSubnetIdForAz2; + } + + public DatabaseParameters setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { + this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; + return this; + } + + public String getVpcSubnetIdForAz3() { + return vpcSubnetIdForAz3; + } + + public DatabaseParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { + this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; + return this; + } + + @Override + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + + public DatabaseParameters setTagParameters(TagParametersDelegate tagParameters) { + this.tagParameters = tagParameters; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java new file mode 100644 index 00000000..457812e5 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2016 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 base stack outputs. + * + * @deprecated TODO: remove this class when old backup and restore is removed + * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments + */ +@Deprecated +public class DeprecatedBaseOutputs { + + private String vpcId; + + private String gatewayIamRoleArn; + + private String cmsIamRoleArn; + + private String consulIamRoleArn; + + private String vaultIamRoleArn; + + private String cloudFrontLogProcessorLambdaIamRoleArn; + + private String gatewayInstanceProfileName; + + private String cmsInstanceProfileName; + + private String consulInstanceProfileName; + + private String vaultInstanceProfileName; + + private String toolsIngressSgId; + + private String gatewayElbSgId; + + private String gatewayServerSgId; + + private String cmsElbSgId; + + private String cmsSgId; + + private String cmsDbSgId; + + private String vaultServerElbSgId; + + private String vaultClientSgId; + + private String vaultServerSgId; + + private String consulClientSgId; + + private String consulServerSgId; + + private String configFileKeyId; + + private String dashboardBucketName; + + private String dashboardBucketWebsiteUrl; + + private String configBucketName; + + private String configBucketDomainName; + + private String cmsDbId; + + private String cmsDbAddress; + + private String cmsDbPort; + + private String cmsDbJdbcConnectionString; + + private String vpcHostedZoneId; + + private String vpcSubnetIdForAz1; + + private String vpcSubnetIdForAz2; + + private String vpcSubnetIdForAz3; + + private String cmsKmsPolicyId; + + private String subnetCidrBlockForAz1; + + private String subnetCidrBlockForAz2; + + private String subnetCidrBlockForAz3; + + public String getVpcId() { + return vpcId; + } + + public DeprecatedBaseOutputs setVpcId(String vpcId) { + this.vpcId = vpcId; + return this; + } + + public String getGatewayIamRoleArn() { + return gatewayIamRoleArn; + } + + public DeprecatedBaseOutputs setGatewayIamRoleArn(String gatewayIamRoleArn) { + this.gatewayIamRoleArn = gatewayIamRoleArn; + return this; + } + + public String getCmsIamRoleArn() { + return cmsIamRoleArn; + } + + public DeprecatedBaseOutputs setCmsIamRoleArn(String cmsIamRoleArn) { + this.cmsIamRoleArn = cmsIamRoleArn; + return this; + } + + public String getConsulIamRoleArn() { + return consulIamRoleArn; + } + + public DeprecatedBaseOutputs setConsulIamRoleArn(String consulIamRoleArn) { + this.consulIamRoleArn = consulIamRoleArn; + return this; + } + + public String getVaultIamRoleArn() { + return vaultIamRoleArn; + } + + public DeprecatedBaseOutputs setVaultIamRoleArn(String vaultIamRoleArn) { + this.vaultIamRoleArn = vaultIamRoleArn; + return this; + } + + public String getCloudFrontLogProcessorLambdaIamRoleArn() { + return cloudFrontLogProcessorLambdaIamRoleArn; + } + + public void setCloudFrontLogProcessorLambdaIamRoleArn(String cloudFrontLogProcessorLambdaIamRoleArn) { + this.cloudFrontLogProcessorLambdaIamRoleArn = cloudFrontLogProcessorLambdaIamRoleArn; + } + + public String getGatewayInstanceProfileName() { + return gatewayInstanceProfileName; + } + + public DeprecatedBaseOutputs setGatewayInstanceProfileName(String gatewayInstanceProfileName) { + this.gatewayInstanceProfileName = gatewayInstanceProfileName; + return this; + } + + public String getCmsInstanceProfileName() { + return cmsInstanceProfileName; + } + + public DeprecatedBaseOutputs setCmsInstanceProfileName(String cmsInstanceProfileName) { + this.cmsInstanceProfileName = cmsInstanceProfileName; + return this; + } + + public String getConsulInstanceProfileName() { + return consulInstanceProfileName; + } + + public DeprecatedBaseOutputs setConsulInstanceProfileName(String consulInstanceProfileName) { + this.consulInstanceProfileName = consulInstanceProfileName; + return this; + } + + public String getVaultInstanceProfileName() { + return vaultInstanceProfileName; + } + + public DeprecatedBaseOutputs setVaultInstanceProfileName(String vaultInstanceProfileName) { + this.vaultInstanceProfileName = vaultInstanceProfileName; + return this; + } + + public String getToolsIngressSgId() { + return toolsIngressSgId; + } + + public DeprecatedBaseOutputs setToolsIngressSgId(String toolsIngressSgId) { + this.toolsIngressSgId = toolsIngressSgId; + return this; + } + + public String getGatewayElbSgId() { + return gatewayElbSgId; + } + + public DeprecatedBaseOutputs setGatewayElbSgId(String gatewayElbSgId) { + this.gatewayElbSgId = gatewayElbSgId; + return this; + } + + public String getGatewayServerSgId() { + return gatewayServerSgId; + } + + public DeprecatedBaseOutputs setGatewayServerSgId(String gatewayServerSgId) { + this.gatewayServerSgId = gatewayServerSgId; + return this; + } + + public String getCmsElbSgId() { + return cmsElbSgId; + } + + public DeprecatedBaseOutputs setCmsElbSgId(String cmsElbSgId) { + this.cmsElbSgId = cmsElbSgId; + return this; + } + + public String getCmsSgId() { + return cmsSgId; + } + + public DeprecatedBaseOutputs setCmsSgId(String cmsSgId) { + this.cmsSgId = cmsSgId; + return this; + } + + public String getCmsDbSgId() { + return cmsDbSgId; + } + + public DeprecatedBaseOutputs setCmsDbSgId(String cmsDbSgId) { + this.cmsDbSgId = cmsDbSgId; + return this; + } + + public String getVaultServerElbSgId() { + return vaultServerElbSgId; + } + + public DeprecatedBaseOutputs setVaultServerElbSgId(String vaultServerElbSgId) { + this.vaultServerElbSgId = vaultServerElbSgId; + return this; + } + + public String getVaultClientSgId() { + return vaultClientSgId; + } + + public DeprecatedBaseOutputs setVaultClientSgId(String vaultClientSgId) { + this.vaultClientSgId = vaultClientSgId; + return this; + } + + public String getVaultServerSgId() { + return vaultServerSgId; + } + + public DeprecatedBaseOutputs setVaultServerSgId(String vaultServerSgId) { + this.vaultServerSgId = vaultServerSgId; + return this; + } + + public String getConsulClientSgId() { + return consulClientSgId; + } + + public DeprecatedBaseOutputs setConsulClientSgId(String consulClientSgId) { + this.consulClientSgId = consulClientSgId; + return this; + } + + public String getConsulServerSgId() { + return consulServerSgId; + } + + public DeprecatedBaseOutputs setConsulServerSgId(String consulServerSgId) { + this.consulServerSgId = consulServerSgId; + return this; + } + + public String getConfigFileKeyId() { + return configFileKeyId; + } + + public DeprecatedBaseOutputs setConfigFileKeyId(String configFileKeyId) { + this.configFileKeyId = configFileKeyId; + return this; + } + + public String getDashboardBucketName() { + return dashboardBucketName; + } + + public DeprecatedBaseOutputs setDashboardBucketName(String dashboardBucketName) { + this.dashboardBucketName = dashboardBucketName; + return this; + } + + public String getDashboardBucketWebsiteUrl() { + return dashboardBucketWebsiteUrl; + } + + public DeprecatedBaseOutputs setDashboardBucketWebsiteUrl(String dashboardBucketWebsiteUrl) { + this.dashboardBucketWebsiteUrl = dashboardBucketWebsiteUrl; + return this; + } + + public String getConfigBucketName() { + return configBucketName; + } + + public DeprecatedBaseOutputs setConfigBucketName(String configBucketName) { + this.configBucketName = configBucketName; + return this; + } + + public String getConfigBucketDomainName() { + return configBucketDomainName; + } + + public DeprecatedBaseOutputs setConfigBucketDomainName(String configBucketDomainName) { + this.configBucketDomainName = configBucketDomainName; + return this; + } + + public String getCmsDbId() { + return cmsDbId; + } + + public DeprecatedBaseOutputs setCmsDbId(String cmsDbId) { + this.cmsDbId = cmsDbId; + return this; + } + + public String getCmsDbAddress() { + return cmsDbAddress; + } + + public DeprecatedBaseOutputs setCmsDbAddress(String cmsDbAddress) { + this.cmsDbAddress = cmsDbAddress; + return this; + } + + public String getCmsDbPort() { + return cmsDbPort; + } + + public DeprecatedBaseOutputs setCmsDbPort(String cmsDbPort) { + this.cmsDbPort = cmsDbPort; + return this; + } + + public String getCmsDbJdbcConnectionString() { + return cmsDbJdbcConnectionString; + } + + public DeprecatedBaseOutputs setCmsDbJdbcConnectionString(String cmsDbJdbcConnectionString) { + this.cmsDbJdbcConnectionString = cmsDbJdbcConnectionString; + return this; + } + + public String getVpcHostedZoneId() { + return vpcHostedZoneId; + } + + public DeprecatedBaseOutputs setVpcHostedZoneId(String vpcHostedZoneId) { + this.vpcHostedZoneId = vpcHostedZoneId; + return this; + } + + public String getVpcSubnetIdForAz1() { + return vpcSubnetIdForAz1; + } + + public DeprecatedBaseOutputs setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { + this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; + return this; + } + + public String getVpcSubnetIdForAz2() { + return vpcSubnetIdForAz2; + } + + public DeprecatedBaseOutputs setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { + this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; + return this; + } + + public String getVpcSubnetIdForAz3() { + return vpcSubnetIdForAz3; + } + + public DeprecatedBaseOutputs setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { + this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; + return this; + } + + public String getCmsKmsPolicyId() { + return cmsKmsPolicyId; + } + + public DeprecatedBaseOutputs setCmsKmsPolicyId(String cmsKmsPolicyId) { + this.cmsKmsPolicyId = cmsKmsPolicyId; + return this; + } + + public String getSubnetCidrBlockForAz1() { + return subnetCidrBlockForAz1; + } + + public DeprecatedBaseOutputs setSubnetCidrBlockForAz1(String subnetCidrBlockForAz1) { + this.subnetCidrBlockForAz1 = subnetCidrBlockForAz1; + return this; + } + + public String getSubnetCidrBlockForAz2() { + return subnetCidrBlockForAz2; + } + + public DeprecatedBaseOutputs setSubnetCidrBlockForAz2(String subnetCidrBlockForAz2) { + this.subnetCidrBlockForAz2 = subnetCidrBlockForAz2; + return this; + } + + public String getSubnetCidrBlockForAz3() { + return subnetCidrBlockForAz3; + } + + public DeprecatedBaseOutputs setSubnetCidrBlockForAz3(String subnetCidrBlockForAz3) { + this.subnetCidrBlockForAz3 = subnetCidrBlockForAz3; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java new file mode 100644 index 00000000..07356ceb --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 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; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +/** + * Represents the base stack inputs. + * + * @deprecated TODO: remove this class when old backup and restore is removed + * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments + */ +@Deprecated +public class DeprecatedBaseParameters implements TagParameters { + + private String accountAdminArn; + + private String az1; + + private String az2; + + private String az3; + + @JsonUnwrapped + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public String getAz1() { + return az1; + } + + public DeprecatedBaseParameters setAz1(String az1) { + this.az1 = az1; + return this; + } + + public String getAz2() { + return az2; + } + + public DeprecatedBaseParameters setAz2(String az2) { + this.az2 = az2; + return this; + } + + public String getAz3() { + return az3; + } + + public DeprecatedBaseParameters setAz3(String az3) { + this.az3 = az3; + return this; + } + + public String getAccountAdminArn() { + return accountAdminArn; + } + + public DeprecatedBaseParameters setAccountAdminArn(String accountAdminArn) { + this.accountAdminArn = accountAdminArn; + return this; + } + + @Override + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + + public DeprecatedBaseParameters setTagParameters(TagParametersDelegate tagParameters) { + this.tagParameters = tagParameters; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java index 7c11efcc..78ec6d0c 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java @@ -148,7 +148,6 @@ public GatewayParameters setHostname(String hostname) { return this; } - @Override public SslConfigParametersDelegate getSslConfigParameters() { return sslConfigParameters; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java index b595d994..80a22352 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java @@ -19,7 +19,7 @@ /** * Interface implemented by stack parameter POJOs that have launch configuration parameters. */ -public interface LaunchConfigParameters extends TagParameters, SslConfigParameters { +public interface LaunchConfigParameters extends TagParameters { LaunchConfigParametersDelegate getLaunchConfigParameters(); } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java new file mode 100644 index 00000000..dae70bf6 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 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 load balancer stack outputs. + */ +public class LoadBalancerOutputs { + + private Integer cmsTargetGroup; + + private String loadBalancerAccessLogBucket; + + private String loadBalancerDnsName; + + private String loadBalancerLogicalId; + + public Integer getCmsTargetGroup() { + return cmsTargetGroup; + } + + public LoadBalancerOutputs setCmsTargetGroup(Integer cmsTargetGroup) { + this.cmsTargetGroup = cmsTargetGroup; + return this; + } + + public String getLoadBalancerAccessLogBucket() { + return loadBalancerAccessLogBucket; + } + + public LoadBalancerOutputs setLoadBalancerAccessLogBucket(String loadBalancerAccessLogBucket) { + this.loadBalancerAccessLogBucket = loadBalancerAccessLogBucket; + return this; + } + + public String getLoadBalancerDnsName() { + return loadBalancerDnsName; + } + + public LoadBalancerOutputs setLoadBalancerDnsName(String loadBalancerDnsName) { + this.loadBalancerDnsName = loadBalancerDnsName; + return this; + } + + public String getLoadBalancerLogicalId() { + return loadBalancerLogicalId; + } + + public LoadBalancerOutputs setLoadBalancerLogicalId(String loadBalancerLogicalId) { + this.loadBalancerLogicalId = loadBalancerLogicalId; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java new file mode 100644 index 00000000..775e311e --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 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; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +/** + * Represents the load balancer stack inputs. + */ +public class LoadBalancerParameters implements TagParameters { + + private String sgStackName; + + private String sslCertificateArn; + + private String vpcId; + + private String vpcSubnetIdForAz1; + + private String vpcSubnetIdForAz2; + + private String vpcSubnetIdForAz3; + + @JsonUnwrapped + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public String getSgStackName() { + return sgStackName; + } + + public LoadBalancerParameters setSgStackName(String sgStackName) { + this.sgStackName = sgStackName; + return this; + } + + public String getSslCertificateArn() { + return sslCertificateArn; + } + + public LoadBalancerParameters setSslCertificateArn(String sslCertificateArn) { + this.sslCertificateArn = sslCertificateArn; + return this; + } + + public String getVpcId() { + return vpcId; + } + + public LoadBalancerParameters setVpcId(String vpcId) { + this.vpcId = vpcId; + return this; + } + + public String getVpcSubnetIdForAz1() { + return vpcSubnetIdForAz1; + } + + public LoadBalancerParameters setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { + this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; + return this; + } + + public String getVpcSubnetIdForAz2() { + return vpcSubnetIdForAz2; + } + + public LoadBalancerParameters setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { + this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; + return this; + } + + public String getVpcSubnetIdForAz3() { + return vpcSubnetIdForAz3; + } + + public LoadBalancerParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { + this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; + return this; + } + + @Override + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + + public LoadBalancerParameters setTagParameters(TagParametersDelegate tagParameters) { + this.tagParameters = tagParameters; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/configuration/VaultAclEntry.java b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java similarity index 53% rename from src/main/java/com/nike/cerberus/domain/configuration/VaultAclEntry.java rename to src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java index 3e784824..c3ab1a8c 100644 --- a/src/main/java/com/nike/cerberus/domain/configuration/VaultAclEntry.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,32 +14,21 @@ * limitations under the License. */ -package com.nike.cerberus.domain.configuration; +package com.nike.cerberus.domain.cloudformation; /** - * POJO creating the ACL token and entry JSON. + * Represents the route53 stack outputs. */ -public class VaultAclEntry { +public class Route53Outputs { - private String aclToken; + private String recordSet; - private String entry; - - public String getAclToken() { - return aclToken; - } - - public VaultAclEntry setAclToken(String aclToken) { - this.aclToken = aclToken; - return this; - } - - public String getEntry() { - return entry; + public String getRecordSet() { + return recordSet; } - public VaultAclEntry setEntry(String entry) { - this.entry = entry; + public Route53Outputs setRecordSet(String recordSet) { + this.recordSet = recordSet; return this; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java new file mode 100644 index 00000000..1d40127e --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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 route53 stack inputs. + */ +public class Route53Parameters { + + private String loadBalancerStackName; + + private String hostname; + + private String hostedZoneId; + + public String getLoadBalancerStackName() { + return loadBalancerStackName; + } + + public Route53Parameters setLoadBalancerStackName(String loadBalancerStackName) { + this.loadBalancerStackName = loadBalancerStackName; + return this; + } + + public String getHostname() { + return hostname; + } + + public Route53Parameters setHostname(String hostname) { + this.hostname = hostname; + return this; + } + + public String getHostedZoneId() { + return hostedZoneId; + } + + public Route53Parameters setHostedZoneId(String hostedZoneId) { + this.hostedZoneId = hostedZoneId; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupOutputs.java new file mode 100644 index 00000000..d924434e --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupOutputs.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 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 security group stack outputs. + */ +public class SecurityGroupOutputs { + + private Integer cmsDbPort; + + private String cmsSgId; + + private String cmsDbSgId; + + private String loadBalancerSgId; + + private String whitelistIngressSgId; + + public Integer getCmsDbPort() { + return cmsDbPort; + } + + public SecurityGroupOutputs setCmsDbPort(Integer cmsDbPort) { + this.cmsDbPort = cmsDbPort; + return this; + } + + public String getCmsSgId() { + return cmsSgId; + } + + public SecurityGroupOutputs setCmsSgId(String cmsSgId) { + this.cmsSgId = cmsSgId; + return this; + } + + public String getCmsDbSgId() { + return cmsDbSgId; + } + + public SecurityGroupOutputs setCmsDbSgId(String cmsDbSgId) { + this.cmsDbSgId = cmsDbSgId; + return this; + } + + public String getLoadBalancerSgId() { + return loadBalancerSgId; + } + + public SecurityGroupOutputs setLoadBalancerSgId(String loadBalancerSgId) { + this.loadBalancerSgId = loadBalancerSgId; + return this; + } + + public String getWhitelistIngressSgId() { + return whitelistIngressSgId; + } + + public SecurityGroupOutputs setWhitelistIngressSgId(String whitelistIngressSgId) { + this.whitelistIngressSgId = whitelistIngressSgId; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java new file mode 100644 index 00000000..502ebe09 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 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; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +/** + * Represents the security group stack inputs. + */ +public class SecurityGroupParameters implements TagParameters { + + private Integer cmsDbPort; + + private String loadBalancerCidrBlock; + + private String vpcId; + + @JsonUnwrapped + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public Integer getCmsDbPort() { + return cmsDbPort; + } + + public SecurityGroupParameters setCmsDbPort(Integer cmsDbPort) { + this.cmsDbPort = cmsDbPort; + return this; + } + + public String getLoadBalancerCidrBlock() { + return loadBalancerCidrBlock; + } + + public SecurityGroupParameters setLoadBalancerCidrBlock(String loadBalancerCidrBlock) { + this.loadBalancerCidrBlock = loadBalancerCidrBlock; + return this; + } + + public String getVpcId() { + return vpcId; + } + + public SecurityGroupParameters setVpcId(String vpcId) { + this.vpcId = vpcId; + return this; + } + + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + + public SecurityGroupParameters setTagParameters(TagParametersDelegate tagParameters) { + this.tagParameters = tagParameters; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java index 2a1c6798..ce193fa5 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java @@ -16,17 +16,38 @@ package com.nike.cerberus.domain.cloudformation; +import com.beust.jcommander.DynamicParameter; +import com.beust.jcommander.Parameter; + +import java.util.HashMap; +import java.util.Map; + +import static com.nike.cerberus.command.StackDelegate.COST_CENTER_LONG_ARG; +import static com.nike.cerberus.command.StackDelegate.OWNER_EMAIL_LONG_ARG; +import static com.nike.cerberus.command.StackDelegate.PARAMETER_SHORT_ARG; + /** * CloudFormation input parameters common to all Cerberus CloudFormation stacks. */ public class TagParametersDelegate { + @Parameter(names = "--tag-name", + description = "The environment name (e.g. 'cerberus-demo', 'cerberus-preprod')") private String tagName; + @Parameter(names = OWNER_EMAIL_LONG_ARG, + description = "The e-mail for who owns the provisioned resources. Will be tagged on all resources.", + required = true) private String tagEmail; + @Parameter(names = COST_CENTER_LONG_ARG, + description = "Costcenter for where to bill provisioned resources. Will be tagged on all resources.", + required = true) private String tagCostcenter; + @DynamicParameter(names = PARAMETER_SHORT_ARG, description = "Dynamic parameters for overriding the values for specific parameters in the CloudFormation.") + private Map dynamicParameters = new HashMap<>(); + public String getTagName() { return tagName; } @@ -53,4 +74,12 @@ public TagParametersDelegate setTagCostcenter(String tagCostcenter) { this.tagCostcenter = tagCostcenter; return this; } + + public Map getDynamicParameters() { + return dynamicParameters; + } + + public void setDynamicParameters(Map dynamicParameters) { + this.dynamicParameters = dynamicParameters; + } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java new file mode 100644 index 00000000..ba589877 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 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 vpc stack outputs. + */ +public class VpcOutputs { + + private Integer subnetCidrBlockForAz1; + + private String subnetCidrBlockForAz2; + + private String subnetCidrBlockForAz3; + + private String vpcSubnetIdForAz1; + + private String vpcSubnetIdForAz2; + + private String vpcSubnetIdForAz3; + + private String vpcId; + + public Integer getSubnetCidrBlockForAz1() { + return subnetCidrBlockForAz1; + } + + public VpcOutputs setSubnetCidrBlockForAz1(Integer subnetCidrBlockForAz1) { + this.subnetCidrBlockForAz1 = subnetCidrBlockForAz1; + return this; + } + + public String getSubnetCidrBlockForAz2() { + return subnetCidrBlockForAz2; + } + + public VpcOutputs setSubnetCidrBlockForAz2(String subnetCidrBlockForAz2) { + this.subnetCidrBlockForAz2 = subnetCidrBlockForAz2; + return this; + } + + public String getSubnetCidrBlockForAz3() { + return subnetCidrBlockForAz3; + } + + public VpcOutputs setSubnetCidrBlockForAz3(String subnetCidrBlockForAz3) { + this.subnetCidrBlockForAz3 = subnetCidrBlockForAz3; + return this; + } + + public String getVpcSubnetIdForAz1() { + return vpcSubnetIdForAz1; + } + + public VpcOutputs setVpcSubnetForAz1(String vpcSubnetForAz1) { + this.vpcSubnetIdForAz1 = vpcSubnetForAz1; + return this; + } + + public String getVpcSubnetIdForAz2() { + return vpcSubnetIdForAz2; + } + + public VpcOutputs setVpcSubnetForAz2(String vpcSubnetForAz2) { + this.vpcSubnetIdForAz2 = vpcSubnetForAz2; + return this; + } + + public String getVpcSubnetIdForAz3() { + return vpcSubnetIdForAz3; + } + + public VpcOutputs setVpcSubnetForAz3(String vpcSubnetForAz3) { + this.vpcSubnetIdForAz3 = vpcSubnetForAz3; + return this; + } + + public String getVpcId() { + return vpcId; + } + + public VpcOutputs setVpcId(String vpcId) { + this.vpcId = vpcId; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java new file mode 100644 index 00000000..d9ac17ee --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017 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; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +/** + * Represents the vpc stack inputs. + */ +public class VpcParameters implements TagParameters { + + private String az1; + + private String az2; + + private String az3; + + private String internetGatewayCidrBlock; + + private String subnetCidrBlockForAz1; + + private String subnetCidrBlockForAz2; + + private String subnetCidrBlockForAz3; + + private String vpcCidrBlock; + + @JsonUnwrapped + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public String getAz1() { + return az1; + } + + public VpcParameters setAz1(String az1) { + this.az1 = az1; + return this; + } + + public String getAz2() { + return az2; + } + + public VpcParameters setAz2(String az2) { + this.az2 = az2; + return this; + } + + public String getAz3() { + return az3; + } + + public VpcParameters setAz3(String az3) { + this.az3 = az3; + return this; + } + + public String getInternetGatewayCidrBlock() { + return internetGatewayCidrBlock; + } + + public VpcParameters setInternetGatewayCidrBlock(String internetGatewayCidrBlock) { + this.internetGatewayCidrBlock = internetGatewayCidrBlock; + return this; + } + + public String getSubnetCidrBlockForAz1() { + return subnetCidrBlockForAz1; + } + + public VpcParameters setSubnetCidrBlockForAz1(String subnetCidrBlockForAz1) { + this.subnetCidrBlockForAz1 = subnetCidrBlockForAz1; + return this; + } + + public String getSubnetCidrBlockForAz2() { + return subnetCidrBlockForAz2; + } + + public VpcParameters setSubnetCidrBlockForAz2(String subnetCidrBlockForAz2) { + this.subnetCidrBlockForAz2 = subnetCidrBlockForAz2; + return this; + } + + public String getSubnetCidrBlockForAz3() { + return subnetCidrBlockForAz3; + } + + public VpcParameters setSubnetCidrBlockForAz3(String subnetCidrBlockForAz3) { + this.subnetCidrBlockForAz3 = subnetCidrBlockForAz3; + return this; + } + + public String getVpcCidrBlock() { + return vpcCidrBlock; + } + + public VpcParameters setVpcCidrBlock(String vpcCidrBlock) { + this.vpcCidrBlock = vpcCidrBlock; + return this; + } + + @Override + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + + public VpcParameters setTagParameters(TagParametersDelegate tagParameters) { + this.tagParameters = tagParameters; + 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 new file mode 100644 index 00000000..cc5c2730 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/WafOutputs.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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 stack outputs. + */ +public class WafOutputs { + + private Integer autoBlockIPSetID; + + private String manualBlockIPSetID; + + private String whitelistIPSetID; + + public Integer getAutoBlockIPSetID() { + return autoBlockIPSetID; + } + + public WafOutputs setAutoBlockIPSetID(Integer autoBlockIPSetID) { + this.autoBlockIPSetID = autoBlockIPSetID; + return this; + } + + public String getManualBlockIPSetID() { + return manualBlockIPSetID; + } + + public WafOutputs setManualBlockIPSetID(String manualBlockIPSetID) { + this.manualBlockIPSetID = manualBlockIPSetID; + return this; + } + + public String getWhitelistIPSetID() { + return whitelistIPSetID; + } + + public WafOutputs setWhitelistIPSetID(String whitelistIPSetID) { + this.whitelistIPSetID = whitelistIPSetID; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java new file mode 100644 index 00000000..e5d26317 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 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; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +/** + * Represents the waf stack inputs. + */ +public class WafParameters implements TagParameters { + + private String loadBalancerStackName; + + @JsonUnwrapped + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public String getLoadBalancerStackName() { + return loadBalancerStackName; + } + + public WafParameters setLoadBalancerStackName(String loadBalancerStackName) { + this.loadBalancerStackName = loadBalancerStackName; + return this; + } + + @Override + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + + public WafParameters setTagParameters(TagParametersDelegate tagParameters) { + this.tagParameters = tagParameters; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackName.java b/src/main/java/com/nike/cerberus/domain/environment/StackName.java index cc2fa12a..d15d5495 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/StackName.java +++ b/src/main/java/com/nike/cerberus/domain/environment/StackName.java @@ -24,8 +24,12 @@ public enum StackName { VAULT("vault"), CMS("cms"), GATEWAY("gateway"), - @Deprecated - RDSBACKUP("rdsbackup"); // TODO: need to remove but casually deleting will cause JSON parse error + VPC("vpc"), + DATABASE("database"), + SECURITY_GROUPS("security-groups"), + LOAD_BALANCER("load-balancer"), + ROUTE53("route53"), + WAF("web-app-firewall"); private final String name; @@ -37,6 +41,15 @@ public String getName() { return name; } + /** + * Generate the CloudFormation stack name for each Cerberus component + * @param environmentName The name of the environment in which the component lives (e.g. demo, preprod, devel, etc.) + * @return The generated CloudFormation stack name + */ + public String getFullName(String environmentName) { + return String.format("%s-cerberus-%s", environmentName, name); + } + public static StackName fromName(final String name) { for (StackName stackName : StackName.values()) { if (stackName.getName().equals(name)) { diff --git a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java index d4e7e270..b136c3d4 100644 --- a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java +++ b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java @@ -37,6 +37,7 @@ public class EnvironmentConfig { private Vault vault; private ManagementService managementService; private Gateway gateway; + private SecurityGroups securityGroups; public String getVersion() { return version; @@ -157,4 +158,12 @@ public Gateway getGateway() { public void setGateway(Gateway gateway) { this.gateway = gateway; } + + public SecurityGroups getSecurityGroups() { + return securityGroups; + } + + public void setSecurityGroups(SecurityGroups securityGroups) { + this.securityGroups = securityGroups; + } } diff --git a/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java b/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java new file mode 100644 index 00000000..7f8fc5c3 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 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.input; + +/** + * Stores Management Service-specific parameters parsed from YAML + */ +public class SecurityGroups extends CerberusStack { + + private String loadBalancerCidrBlock; + + public String getLoadBalancerCidrBlock() { + return loadBalancerCidrBlock; + } + + public void setLoadBalancerCidrBlock(String loadBalancerCidrBlock) { + this.loadBalancerCidrBlock = loadBalancerCidrBlock; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index 7e341893..da255bb9 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -23,8 +23,9 @@ import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; +import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; import com.nike.cerberus.domain.cloudformation.CmsParameters; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; @@ -58,83 +59,67 @@ public class CreateCmsClusterOperation implements Operation cmsServerCertificateArn = configStore.getServerCertificateArn(StackName.CMS); final Optional pubKey = configStore.getCertPart(StackName.CMS, ConfigConstants.CERT_PART_PUBKEY); - final String internalElbCname = configStore.getInternalElbCname(StackName.CMS); if (!cmsServerCertificateArn.isPresent() || !pubKey.isPresent()) { throw new IllegalStateException("CMS certificate has not been uploaded!"); } // Make sure the given AmiId is for CMS component. Check if it contains required tag - if ( !command.isSkipAmiTagCheck() ) { + if ( ! command.isSkipAmiTagCheck() ) { amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), StackName.CMS); } final CmsParameters cmsParameters = new CmsParameters() - .setInstanceProfileName(baseOutputs.getCmsInstanceProfileName()) - .setCmsElbSgId(baseOutputs.getCmsElbSgId()) - .setCmsSgId(baseOutputs.getCmsSgId()) - .setToolsIngressSgId(baseOutputs.getToolsIngressSgId()) - .setVpcId(baseOutputs.getVpcId()) - .setVpcSubnetIdForAz1(baseOutputs.getVpcSubnetIdForAz1()) - .setVpcSubnetIdForAz2(baseOutputs.getVpcSubnetIdForAz2()) - .setVpcSubnetIdForAz3(baseOutputs.getVpcSubnetIdForAz3()) - .setHostedZoneId(baseOutputs.getVpcHostedZoneId()) - .setCname(internalElbCname); - - cmsParameters.getSslConfigParameters().setCertPublicKey(pubKey.get()); - cmsParameters.getSslConfigParameters().setSslCertificateArn(cmsServerCertificateArn.get()); + .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) + .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) + .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()) + .setBaseStackName(StackName.BASE.getFullName(environmentName)) + .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)) + .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)); cmsParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); cmsParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); cmsParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); cmsParameters.getLaunchConfigParameters().setUserData( ec2UserDataService.getUserData(StackName.CMS, command.getStackDelegate().getOwnerGroup())); - cmsParameters.getLaunchConfigParameters().setDesiredInstances(command.getStackDelegate().getDesiredInstances()); - cmsParameters.getLaunchConfigParameters().setMinimumInstances(command.getStackDelegate().getMinimumInstances()); - cmsParameters.getLaunchConfigParameters().setMaximumInstances(command.getStackDelegate().getMaximumInstances()); cmsParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - cmsParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); + cmsParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); cmsParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); final TypeReference> typeReference = new TypeReference>() {}; - final Map parameters = cloudformationObjectMapper.convertValue(cmsParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters, typeReference); - final String stackId = cloudFormationService.createStack(cloudFormationService.getEnvStackName(uniqueStackName), - parameters, ConfigConstants.CMS_STACK_TEMPLATE_PATH, true); + // allow user to overwrite CloudFormation parameters with -P option + parameters.putAll(command.getStackDelegate().getDynamicParameters()); - logger.info("Uploading data to the configuration bucket."); - configStore.storeStackId(StackName.CMS, stackId); - logger.info("Uploading complete."); + final String stackId = cloudFormationService.createStack(StackName.CMS.getFullName(environmentName), + parameters, ConfigConstants.CMS_STACK_TEMPLATE_PATH, true); final StackStatus endStatus = cloudFormationService.waitForStatus(stackId, @@ -150,6 +135,19 @@ public void run(final CreateCmsClusterCommand command) { @Override public boolean isRunnable(final CreateCmsClusterCommand command) { - return configStore.getCmsEnvConfig().isPresent(); + String environmentName = environmentMetadata.getName(); + + try { + cloudFormationService.getStackId(StackName.LOAD_BALANCER.getFullName(environmentName)); + cloudFormationService.getStackId(StackName.SECURITY_GROUPS.getFullName(environmentName)); + cloudFormationService.getStackId(StackName.BASE.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + logger.error("Could not create the CMS cluster." + + "Make sure the load balancer, security group, and base stacks have all been created.", iae); + return false; + } + + return configStore.getCmsEnvConfig().isPresent() && + ! cloudFormationService.isStackPresent(StackName.CMS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java index 8a68bccf..ce2bd131 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index fcae3aa4..7ba49d40 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,34 +16,22 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.beust.jcommander.internal.Maps; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.BaseParameters; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.Ec2Service; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.util.RandomStringGenerator; -import com.nike.cerberus.util.UuidSupplier; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; -import java.util.List; import java.util.Map; -import static com.nike.cerberus.ConfigConstants.MINIMUM_AZS; import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** @@ -57,127 +45,39 @@ public class CreateBaseOperation implements Operation { private final CloudFormationService cloudFormationService; - private final Ec2Service ec2Service; - - private final UuidSupplier uuidSupplier; - - private final ConfigStore configStore; - - private final ObjectMapper cloudformationObjectMapper; - - private final RandomStringGenerator passwordGenerator = new RandomStringGenerator(); + private final ObjectMapper cloudFormationObjectMapper; @Inject public CreateBaseOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, - final Ec2Service ec2Service, - final UuidSupplier uuidSupplier, - final ConfigStore configStore, @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; - this.ec2Service = ec2Service; - this.uuidSupplier = uuidSupplier; - this.configStore = configStore; - this.cloudformationObjectMapper = cloudformationObjectMapper; + this.cloudFormationObjectMapper = cloudformationObjectMapper; } @Override public void run(final CreateBaseCommand command) { - final String uniqueStackName = String.format("%s-%s", ConfigConstants.BASE_STACK_NAME, uuidSupplier.get()); - final Map azByIdentifier = mapAvailabilityZones(); - final String dbMasterPassword = passwordGenerator.get(); - - final BaseParameters baseParameters = new BaseParameters(); - baseParameters.setAccountAdminArn(command.getAdminRoleArn()) - .setAz1(azByIdentifier.get(1)) - .setAz2(azByIdentifier.get(2)) - .setAz3(azByIdentifier.get(3)) - .setCmsDbMasterUsername(ConfigConstants.DEFAULT_CMS_DB_NAME) - .setCmsDbMasterPassword(dbMasterPassword) - .setCmsDbName(ConfigConstants.DEFAULT_CMS_DB_NAME) - .setVpcHostedZoneName(command.getVpcHostedZoneName()); - - baseParameters.getTagParameters().setTagEmail(command.getOwnerEmail()); - baseParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); - baseParameters.getTagParameters().setTagCostcenter(command.getCostcenter()); - - final TypeReference> typeReference = new TypeReference>() {}; - final Map parameters = cloudformationObjectMapper.convertValue(baseParameters, typeReference); - - final String stackId = cloudFormationService.createStack(cloudFormationService.getEnvStackName(uniqueStackName), - parameters, ConfigConstants.BASE_STACK_TEMPLATE_PATH, true); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (StackStatus.CREATE_COMPLETE == endStatus) { - final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); - final BaseOutputs outputParameters = - cloudformationObjectMapper.convertValue(stackOutputs, BaseOutputs.class); - - logger.info("Stack creation complete, uploading data to the configuration bucket."); - - // ORDER IS IMPORTANT! - - // 1. First update the environment metadata with the new config bucket name. - environmentMetadata.setBucketName(outputParameters.getConfigBucketName()); + final String environmentName = environmentMetadata.getName(); + final BaseParameters baseParameters = new BaseParameters() + .setAccountAdminArn(command.getAdminRoleArn()); - // 2. Initialize the environment config. - configStore.initEnvironmentData(); + baseParameters.getTagParameters().setTagEmail(baseParameters.getTagParameters().getTagEmail()); + baseParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); + baseParameters.getTagParameters().setTagCostcenter(baseParameters.getTagParameters().getTagCostcenter()); - // 3. Write the first configuration values to the config bucket. - configStore.storeAzs(azByIdentifier.get(1), azByIdentifier.get(2), azByIdentifier.get(3)); - configStore.storeStackId(StackName.BASE, stackId); - configStore.storeConfigKeyId(outputParameters.getConfigFileKeyId()); + final TypeReference> typeReference = new TypeReference>() { + }; + final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); - // 4. Initialize the secrets config. - configStore.initSecretsData(); - - // 5. Write the first secret value to the config bucket once the config key id has been saved. - configStore.storeCmsDatabasePassword(dbMasterPassword); - - logger.info("Uploading complete."); - } else { - final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } + cloudFormationService.createStack(StackName.BASE.getFullName(environmentName), + parameters, ConfigConstants.BASE_STACK_TEMPLATE_PATH, true); } @Override public boolean isRunnable(final CreateBaseCommand command) { - if (StringUtils.isNotBlank(environmentMetadata.getBucketName())) { - try { - final String stackId = configStore.getStackId(StackName.BASE); - - if (cloudFormationService.isStackPresent(stackId)) { - logger.warn("Operation has already run for the specified environment."); - return false; - } - } catch (IllegalStateException ise) { //NOPMD - // Don't care, fall through. - } - } - - return true; + String environmentName = environmentMetadata.getName(); + return ! cloudFormationService.isStackPresent(StackName.BASE.getFullName(environmentName)); } - private Map mapAvailabilityZones() { - List zones = ec2Service.getAvailabilityZones(); - - if (zones.size() < MINIMUM_AZS) { - throw new IllegalStateException("Not enough availability zones for the selected region."); - } - - Map azByIdentifier = Maps.newHashMap(); - - for (int i = 1; i <= MINIMUM_AZS; i++) { - azByIdentifier.put(i, zones.get(i - 1)); - } - - return azByIdentifier; - } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java new file mode 100644 index 00000000..55970b80 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 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.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Maps; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.DatabaseParameters; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.cloudformation.VpcParameters; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.Ec2Service; +import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.RandomStringGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.List; +import java.util.Map; + +import static com.nike.cerberus.ConfigConstants.MINIMUM_AZS; +import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class CreateDatabaseOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final ObjectMapper cloudFormationObjectMapper; + + private RandomStringGenerator passwordGenerator = new RandomStringGenerator(); + + @Inject + public CreateDatabaseOperation(final EnvironmentMetadata environmentMetadata, + final CloudFormationService cloudFormationService, + final ConfigStore configStore, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.cloudFormationObjectMapper = cloudformationObjectMapper; + } + + @Override + public void run(final CreateDatabaseCommand command) { + final String environmentName = environmentMetadata.getName(); + final VpcParameters vpcParameters = configStore.getVpcStackParameters(); + final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); + + final DatabaseParameters databaseParameters = new DatabaseParameters() + .setCmsDbMasterPassword(passwordGenerator.get()) + .setCmsDbInstanceAz1(vpcParameters.getAz1()) + .setCmsDbInstanceAz2(vpcParameters.getAz2()) + .setCmsDbInstanceAz3(vpcParameters.getAz3()) + .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) + .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) + .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); + + databaseParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); + databaseParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); + databaseParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); + + final TypeReference> typeReference = new TypeReference>() {}; + final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); + + // allow user to overwrite CloudFormation parameters with -P option + parameters.putAll(command.getTagsDelegate().getDynamicParameters()); + + cloudFormationService.createStack(StackName.DATABASE.getFullName(environmentName), + parameters, ConfigConstants.DATABASE_STACK_TEMPLATE_PATH, true); } + + @Override + public boolean isRunnable(final CreateDatabaseCommand command) { + String environmentName = environmentMetadata.getName(); + return ! cloudFormationService.isStackPresent(StackName.DATABASE.getFullName(environmentName)); + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java new file mode 100644 index 00000000..a6c540b2 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 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.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.core.CreateLoadBalancerCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; +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.CF_OBJECT_MAPPER; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class CreateLoadBalancerOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final ObjectMapper cloudFormationObjectMapper; + + @Inject + public CreateLoadBalancerOperation(final EnvironmentMetadata environmentMetadata, + final CloudFormationService cloudFormationService, + final ConfigStore configStore, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.cloudFormationObjectMapper = cloudformationObjectMapper; + } + + @Override + public void run(final CreateLoadBalancerCommand command) { + final String environmentName = environmentMetadata.getName(); + final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); + + final String sslCertificateArn = configStore.getServerCertificateArn(StackName.CMS) + .orElseThrow(() -> new IllegalStateException("Could not retrieve SSL certificate ARN!")); + + final LoadBalancerParameters loadBalancerParameters = new LoadBalancerParameters() + .setVpcId(vpcOutputs.getVpcId()) + .setSslCertificateArn(sslCertificateArn) + .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)) + .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) + .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) + .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); + + loadBalancerParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); + loadBalancerParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); + loadBalancerParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); + + final TypeReference> typeReference = new TypeReference>() {}; + final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); + + cloudFormationService.createStack(StackName.LOAD_BALANCER.getFullName(environmentName), + parameters, ConfigConstants.LOAD_BALANCER_STACK_TEMPLATE_PATH, true); } + + @Override + public boolean isRunnable(final CreateLoadBalancerCommand command) { + String environmentName = environmentMetadata.getName(); + try { + cloudFormationService.getStackId(StackName.SECURITY_GROUPS.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("The security group stack must exist to create the load balancer!", iae); + } + + return ! cloudFormationService.isStackPresent(StackName.LOAD_BALANCER.getFullName(environmentName)); + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java new file mode 100644 index 00000000..e7df1982 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 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.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.core.CreateRoute53Command; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.Route53Parameters; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; +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.CF_OBJECT_MAPPER; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class CreateRoute53Operation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final ObjectMapper cloudFormationObjectMapper; + + @Inject + public CreateRoute53Operation(final EnvironmentMetadata environmentMetadata, + final CloudFormationService cloudFormationService, + final ConfigStore configStore, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.cloudFormationObjectMapper = cloudformationObjectMapper; + } + + @Override + public void run(final CreateRoute53Command command) { + final String environmentName = environmentMetadata.getName(); + + final Route53Parameters route53Parameters = new Route53Parameters() + .setHostname(command.getCerberusHostname()) + .setHostedZoneId(command.getHostedZoneId()) + .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)); + + final TypeReference> typeReference = new TypeReference>() {}; + final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); + + cloudFormationService.createStack(StackName.ROUTE53.getFullName(environmentName), + parameters, ConfigConstants.ROUTE53_TEMPLATE_PATH, true); + } + + @Override + public boolean isRunnable(final CreateRoute53Command command) { + String environmentName = environmentMetadata.getName(); + try { + cloudFormationService.getStackId(StackName.LOAD_BALANCER.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); + } + + return ! cloudFormationService.isStackPresent(StackName.ROUTE53.getFullName(environmentName)); + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java new file mode 100644 index 00000000..731df035 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 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.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; +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.CF_OBJECT_MAPPER; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class CreateSecurityGroupsOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final ObjectMapper cloudFormationObjectMapper; + + @Inject + public CreateSecurityGroupsOperation(final EnvironmentMetadata environmentMetadata, + final CloudFormationService cloudFormationService, + final ConfigStore configStore, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.cloudFormationObjectMapper = cloudformationObjectMapper; + } + + @Override + public void run(final CreateSecurityGroupsCommand command) { + final String environmentName = environmentMetadata.getName(); + final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); + + final SecurityGroupParameters securityGroupParameters = new SecurityGroupParameters() + .setVpcId(vpcOutputs.getVpcId()) + .setLoadBalancerCidrBlock(command.getLoadBalancerCidr()); + + securityGroupParameters.getTagParameters().setTagEmail(securityGroupParameters.getTagParameters().getTagEmail()); + securityGroupParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); + securityGroupParameters.getTagParameters().setTagCostcenter(securityGroupParameters.getTagParameters().getTagCostcenter()); + + final TypeReference> typeReference = new TypeReference>() {}; + final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); + + cloudFormationService.createStack(StackName.SECURITY_GROUPS.getFullName(environmentName), + parameters, ConfigConstants.SECURITY_GROUPS_STACK_TEMPLATE_PATH, true); + } + + @Override + public boolean isRunnable(final CreateSecurityGroupsCommand command) { + String environmentName = environmentMetadata.getName(); + return ! cloudFormationService.isStackPresent(StackName.SECURITY_GROUPS.getFullName(environmentName)); + } + +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java new file mode 100644 index 00000000..2b4560f1 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017 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.beust.jcommander.internal.Maps; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.core.CreateVpcCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.VpcParameters; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.Ec2Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.List; +import java.util.Map; + +import static com.nike.cerberus.ConfigConstants.MINIMUM_AZS; +import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class CreateVpcOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final Ec2Service ec2Service; + + private final ObjectMapper cloudFormationObjectMapper; + + @Inject + public CreateVpcOperation(final EnvironmentMetadata environmentMetadata, + final CloudFormationService cloudFormationService, + final Ec2Service ec2Service, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.ec2Service = ec2Service; + this.cloudFormationObjectMapper = cloudformationObjectMapper; + } + + @Override + public void run(final CreateVpcCommand command) { + final String environmentName = environmentMetadata.getName(); + final Map azByIdentifier = mapAvailabilityZones(); + + final VpcParameters vpcParameters = new VpcParameters() + .setAz1(azByIdentifier.get(1)) + .setAz2(azByIdentifier.get(2)) + .setAz3(azByIdentifier.get(3)); + + vpcParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); + vpcParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); + vpcParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); + + final TypeReference> typeReference = new TypeReference>() {}; + final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); + + cloudFormationService.createStack(StackName.VPC.getFullName(environmentName), + parameters, ConfigConstants.VPC_STACK_TEMPLATE_PATH, true); } + + @Override + public boolean isRunnable(final CreateVpcCommand command) { + String environmentName = environmentMetadata.getName(); + return ! cloudFormationService.isStackPresent(StackName.VPC.getFullName(environmentName)); + } + + private Map mapAvailabilityZones() { + List zones = ec2Service.getAvailabilityZones(); + + if (zones.size() < MINIMUM_AZS) { + throw new IllegalStateException("Not enough availability zones for the selected region."); + } + + Map azByIdentifier = Maps.newHashMap(); + + for (int i = 1; i <= MINIMUM_AZS; i++) { + azByIdentifier.put(i, zones.get(i - 1)); + } + + return azByIdentifier; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java new file mode 100644 index 00000000..a60a1a87 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 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.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.core.CreateLoadBalancerCommand; +import com.nike.cerberus.command.core.CreateWafCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.cloudformation.WafParameters; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; +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.CF_OBJECT_MAPPER; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class CreateWafOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final ObjectMapper cloudFormationObjectMapper; + + @Inject + public CreateWafOperation(final EnvironmentMetadata environmentMetadata, + final CloudFormationService cloudFormationService, + final ConfigStore configStore, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.cloudFormationObjectMapper = cloudformationObjectMapper; + } + + @Override + public void run(final CreateWafCommand command) { + final String environmentName = environmentMetadata.getName(); + + final WafParameters wafParameters = new WafParameters() + .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)); + + wafParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); + wafParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); + wafParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); + + final TypeReference> typeReference = new TypeReference>() {}; + final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); + + cloudFormationService.createStack(StackName.WAF.getFullName(environmentName), + parameters, ConfigConstants.WAF_STACK_TEMPLATE_PATH, true); } + + @Override + public boolean isRunnable(final CreateWafCommand command) { + String environmentName = environmentMetadata.getName(); + try { + cloudFormationService.getStackId(StackName.LOAD_BALANCER.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("The load balancer stack must exist to create the WAF!", iae); + } + + return ! cloudFormationService.isStackPresent(StackName.ROUTE53.getFullName(environmentName)); + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java index 4e6b1d46..d9eb1b77 100644 --- a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java index 3fc93dbb..95ba1f30 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2017 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.services.ec2.model.Filter; diff --git a/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java b/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java index c80d96b7..162758eb 100644 --- a/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java @@ -34,7 +34,11 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; -import java.util.*; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.Set; import static com.nike.cerberus.module.CerberusModule.getAWSCredentialsProviderChain; @@ -49,6 +53,7 @@ public class SetBackupAdminPrincipalsOperation implements Operation { final List statements = new LinkedList<>(); - principals.forEach( principal -> { + principals.forEach(principal -> { log.debug("Adding principal: {} to the CMK Policy for region {}", principal, region); statements.add(new Statement(Statement.Effect.Allow) .withId("Principal " + principal + " Has All Actions") @@ -109,7 +114,7 @@ public void run(SetBackupAdminPrincipalsCommand command) { @Override public boolean isRunnable(SetBackupAdminPrincipalsCommand command) { Optional adminIamPrincipalArn = configStore.getAccountAdminArn(); - if (! adminIamPrincipalArn.isPresent()) { + if (!adminIamPrincipalArn.isPresent()) { log.error("The admin IAM principal must be set for this environment"); return false; } diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index a1d55635..795785bd 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -24,8 +24,9 @@ import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.domain.cloudformation.BaseParameters; +import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.CmsParameters; +import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.LaunchConfigParameters; import com.nike.cerberus.domain.cloudformation.SslConfigParametersDelegate; import com.nike.cerberus.domain.cloudformation.TagParameters; @@ -84,6 +85,7 @@ public UpdateStackOperation(final ConfigStore configStore, stackParameterMap = new HashMap<>(); stackParameterMap.put(StackName.CMS, CmsParameters.class); + stackParameterMap.put(StackName.GATEWAY, GatewayParameters.class); stackTemplatePathMap = new HashMap<>(); stackTemplatePathMap.put(StackName.BASE, ConfigConstants.BASE_STACK_TEMPLATE_PATH); @@ -194,44 +196,12 @@ private Map getUpdateLaunchConfigParameters(final StackName stac launchConfigParameters.getTagParameters().setTagCostcenter(command.getCostcenter()); } - if (command.getDesiredInstances() != null) { - launchConfigParameters.getLaunchConfigParameters().setDesiredInstances(command.getDesiredInstances()); - } - - if (command.getMinimumInstances() != null) { - launchConfigParameters.getLaunchConfigParameters().setMinimumInstances(command.getMinimumInstances()); - } - - if (command.getMaximumInstances() != null) { - launchConfigParameters.getLaunchConfigParameters().setMaximumInstances(command.getMaximumInstances()); - } - - updateSslConfigParameters(stackName, launchConfigParameters); - final TypeReference> typeReference = new TypeReference>() {}; return cloudformationObjectMapper.convertValue(launchConfigParameters, typeReference); } - private void updateSslConfigParameters(final StackName stackName, - final LaunchConfigParameters launchConfigParameters) { - - final SslConfigParametersDelegate sslConfigParameters = launchConfigParameters.getSslConfigParameters(); - - if (StringUtils.isNotBlank(sslConfigParameters.getCertPublicKey())) { - sslConfigParameters.setCertPublicKey(configStore.getCertPart(stackName, CERT_PART_PUBKEY).get()); - } - - if (StringUtils.isNotBlank(sslConfigParameters.getSslCertificateId())) { - sslConfigParameters.setSslCertificateId(configStore.getServerCertificateId(stackName).get()); - } - - if (StringUtils.isNotBlank(sslConfigParameters.getSslCertificateArn())) { - sslConfigParameters.setSslCertificateArn(configStore.getServerCertificateArn(stackName).get()); - } - } - private Map getUpdatedBaseStackParameters(final UpdateStackCommand command) { - final TagParameters tagParameters = configStore.getStackParameters(command.getStackName(), BaseParameters.class); + final TagParameters tagParameters = configStore.getStackParameters(command.getStackName(), SecurityGroupParameters.class); if (StringUtils.isNotBlank(command.getOwnerEmail())) { tagParameters.getTagParameters().setTagEmail(command.getOwnerEmail()); @@ -244,4 +214,4 @@ private Map getUpdatedBaseStackParameters(final UpdateStackComma final TypeReference> typeReference = new TypeReference>() {}; return cloudformationObjectMapper.convertValue(tagParameters, typeReference); } -} +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index 4a25565c..50800c76 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOpertaion.java b/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java similarity index 87% rename from src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOpertaion.java rename to src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java index 349ce774..74d0346a 100644 --- a/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOpertaion.java +++ b/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import com.google.common.collect.Lists; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.cloudformation.BaseOutputs; +import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; +import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; @@ -39,7 +41,7 @@ /** * Operation */ -public class WhitelistCidrForVpcAccessOpertaion implements Operation { +public class WhitelistCidrForVpcAccessOperation implements Operation { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -50,7 +52,7 @@ public class WhitelistCidrForVpcAccessOpertaion implements Operation { if (!securityGroup.getIpPermissions().isEmpty()) { RevokeSecurityGroupIngressRequest revokeIngressRequest = new RevokeSecurityGroupIngressRequest() - .withGroupId(baseStackOutputs.getToolsIngressSgId()) + .withGroupId(securityGroupOutputs.getWhitelistIngressSgId()) .withIpPermissions(securityGroup.getIpPermissions()); ec2Client.revokeSecurityGroupIngress(revokeIngressRequest); } @@ -88,7 +90,7 @@ public void run(final WhitelistCidrForVpcAccessCommand command) { }); AuthorizeSecurityGroupIngressRequest ingressRequest = new AuthorizeSecurityGroupIngressRequest() - .withGroupId(baseStackOutputs.getToolsIngressSgId()) + .withGroupId(securityGroupOutputs.getWhitelistIngressSgId()) .withIpPermissions(ipPermissionList); ec2Client.authorizeSecurityGroupIngress(ingressRequest); logger.info("Done."); diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index a1c3c1bf..5d0a3d22 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -71,6 +71,7 @@ public class CloudFormationService { public final static String MIN_INSTANCES_STACK_PARAMETER_KEY = "minimumInstances"; private final AmazonCloudFormation cloudFormationClient; + private final EnvironmentMetadata environmentMetadata; @Inject @@ -250,6 +251,30 @@ public List getStackEvents(final String stackId) { return Collections.emptyList(); } + /** + * Get the full ID of the stack + * @param stackName - The stack logical id / full stack name + * @return + */ + public String getStackId(String stackName) { + Preconditions.checkArgument(StringUtils.isNotBlank(stackName), "Stack name cannot be blank"); + + DescribeStacksResult result = cloudFormationClient.describeStacks( + new DescribeStacksRequest() + .withStackName(stackName)); + + List stacks = result.getStacks(); + if (stacks.isEmpty()) { + throw new IllegalArgumentException("No stack found with name: " + stackName); + } else if (stacks.size() > 1) { + logger.warn("Found more than stack with name: {}. Selecting the first one: stack id: {}", + stackName, + stacks.get(0).getStackId()); + } + + return stacks.get(0).getStackId(); + } + /** * Checks if a stack exists with the specified ID. * @@ -257,7 +282,7 @@ public List getStackEvents(final String stackId) { * @return boolean */ public boolean isStackPresent(final String stackId) { - Preconditions.checkArgument(StringUtils.isNotBlank(stackId), "Stack ID can not be blank"); + Preconditions.checkArgument(StringUtils.isNotBlank(stackId), "Stack ID cannot be blank"); return getStackStatus(stackId) != null; } @@ -304,16 +329,6 @@ public String getTemplateText(final String templatePath) { } } - /** - * Prepends the name with the environment name. ([environment]-[name]) - * - * @param name Custom stack name - * @return Formatted name with environment prepended - */ - public String getEnvStackName(final String name) { - return String.format("%s-%s", environmentMetadata.getName(), name); - } - /** * Since there doesn't appear to be a first class way through the SDK at this time to get a CF export. We can * iterate through the stacks for a given output key and return the value. diff --git a/src/main/java/com/nike/cerberus/service/UnexpectedDataEncodingException.java b/src/main/java/com/nike/cerberus/service/UnexpectedDataEncodingException.java index 700b0fc1..ad1e1dde 100644 --- a/src/main/java/com/nike/cerberus/service/UnexpectedDataEncodingException.java +++ b/src/main/java/com/nike/cerberus/service/UnexpectedDataEncodingException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 1e0f7411..769d0b26 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -33,8 +33,16 @@ import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.cloudformation.CmsOutputs; import com.nike.cerberus.domain.cloudformation.CmsParameters; +import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; +import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.cloudformation.GatewayParameters; +import com.nike.cerberus.domain.cloudformation.LoadBalancerOutputs; +import com.nike.cerberus.domain.cloudformation.Route53Parameters; +import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; +import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.VaultOutputs; +import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.cloudformation.VpcParameters; import com.nike.cerberus.domain.environment.BackupRegionInfo; import com.nike.cerberus.domain.environment.Environment; import com.nike.cerberus.domain.environment.Secrets; @@ -52,7 +60,6 @@ import javax.inject.Named; import java.io.IOException; import java.io.StringReader; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; @@ -398,6 +405,7 @@ public Properties getAllExistingCmsEnvProperties() { private Properties generateBaseCmsSystemProperties() { final BaseOutputs baseOutputs = getBaseStackOutputs(); + final DatabaseOutputs databaseOutputs = getDatabaseStackOutputs(); final BaseParameters baseParameters = getBaseStackParameters(); final Optional cmsDatabasePassword = getCmsDatabasePassword(); @@ -409,7 +417,7 @@ private Properties generateBaseCmsSystemProperties() { properties.put(ROOT_USER_ARN_KEY, rootUserArn); properties.put(ADMIN_ROLE_ARN_KEY, baseParameters.getAccountAdminArn()); properties.put(CMS_ROLE_ARN_KEY, baseOutputs.getCmsIamRoleArn()); - properties.put(JDBC_URL_KEY, baseOutputs.getCmsDbJdbcConnectionString()); + properties.put(JDBC_URL_KEY, databaseOutputs.getCmsDbJdbcConnectionString()); properties.put(JDBC_USERNAME_KEY, ConfigConstants.DEFAULT_CMS_DB_NAME); properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword.get()); @@ -422,7 +430,7 @@ public Optional getAccountAdminArn() { } public String getCerberusBaseUrl() { - return String.format("https://%s", getGatewayStackParamters().getHostname()); + return String.format("https://%s", getRoute53Parameters().getHostname()); } /** @@ -478,13 +486,17 @@ public Optional getConfigProperties(String path) { return getEncryptedObject(path); } + public String getStackLogicalId(StackName stackName) { + return stackName.getFullName(environmentMetadata.getName()); + } + /** * Get the base stack parameters. * * @return Base parameters */ public BaseParameters getBaseStackParameters() { - return getStackParameters(StackName.BASE, BaseParameters.class); + return getStackParameters(getStackLogicalId(StackName.BASE), BaseParameters.class); } /** @@ -493,7 +505,79 @@ public BaseParameters getBaseStackParameters() { * @return Base outputs */ public BaseOutputs getBaseStackOutputs() { - return getStackOutputs(StackName.BASE, BaseOutputs.class); + return getStackOutputs(getStackLogicalId(StackName.BASE), BaseOutputs.class); + } + + /** + * Get the base stack parameters. + * + * @return Base parameters + */ + public VpcParameters getVpcStackParameters() { + return getStackParameters(getStackLogicalId(StackName.VPC), VpcParameters.class); + } + + /** + * Get the base stack outputs. + * + * @return Base outputs + */ + public VpcOutputs getVpcStackOutputs() { + return getStackOutputs(getStackLogicalId(StackName.VPC), VpcOutputs.class); + } + + /** + * Get the base stack parameters. + * + * @return Base parameters + */ + public SecurityGroupParameters getSecurityGroupStackParameters() { + return getStackParameters(getStackLogicalId(StackName.SECURITY_GROUPS), SecurityGroupParameters.class); + } + + /** + * Get the base stack outputs. + * + * @return Base outputs + */ + public SecurityGroupOutputs getSecurityGroupStackOutputs() { + return getStackOutputs(getStackLogicalId(StackName.SECURITY_GROUPS), SecurityGroupOutputs.class); + } + + /** + * Get the base stack outputs. + * + * @return Base outputs + */ + public LoadBalancerOutputs getLoadBalancerStackOutputs() { + return getStackOutputs(getStackLogicalId(StackName.LOAD_BALANCER), LoadBalancerOutputs.class); + } + + /** + * Get the base stack parameters. + * + * @return Base parameters + */ + public DatabaseParameters getDatabaseStackParameters() { + return getStackParameters(getStackLogicalId(StackName.DATABASE), DatabaseParameters.class); + } + + /** + * Get the base stack parameters. + * + * @return Base parameters + */ + public Route53Parameters getRoute53Parameters() { + return getStackParameters(getStackLogicalId(StackName.ROUTE53), Route53Parameters.class); + } + + /** + * Get the base stack outputs. + * + * @return Base outputs + */ + public DatabaseOutputs getDatabaseStackOutputs() { + return getStackOutputs(getStackLogicalId(StackName.DATABASE), DatabaseOutputs.class); } /** @@ -511,7 +595,7 @@ public VaultOutputs getVaultStackOutputs() { * @return CMS parameters */ public CmsParameters getCmsStackParamters() { - return getStackParameters(StackName.CMS, CmsParameters.class); + return getStackParameters(getStackLogicalId(StackName.CMS), CmsParameters.class); } /** @@ -520,7 +604,7 @@ public CmsParameters getCmsStackParamters() { * @return CMS outputs */ public CmsOutputs getCmsStackOutputs() { - return getStackOutputs(StackName.CMS, CmsOutputs.class); + return getStackOutputs(getStackLogicalId(StackName.CMS), CmsOutputs.class); } /** @@ -554,6 +638,25 @@ public M getStackOutputs(final StackName stackName, final Class outputCla } } + /** + * Get the stack outputs for a specific stack name. + * + * @param stackName Full stack name + * @param outputClass Outputs class + * @param Outputs type + * @return Outputs + */ + public M getStackOutputs(final String stackName, final Class outputClass) { + final String stackId = cloudFormationService.getStackId(stackName); + + if (!cloudFormationService.isStackPresent(stackId)) { + throw new IllegalStateException("Failed to get CloudFormation output for stack: '" + stackName + "'. Stack does not exist."); + } + + final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); + return cloudFormationObjectMapper.convertValue(stackOutputs, outputClass); + } + /** * Get the stack parameters for a specific stack name. * @@ -577,17 +680,22 @@ public M getStackParameters(final StackName stackName, final Class parame } /** - * Constructs the standard format for CNAMEs used by internal ELBs. + * Get the stack parameters for a specific stack name. * - * @param stackName Stack name the CNAME is for - * @return CNAME + * @param stackName Full stack name + * @param parameterClass Parameters class + * @param Parameters type + * @return Parameters */ - public String getInternalElbCname(final StackName stackName) { - final BaseParameters baseParameters = getBaseStackParameters(); - return String.format("%s.%s.%s.", - stackName.getName(), - environmentMetadata.getRegionName(), - baseParameters.getVpcHostedZoneName()); + public M getStackParameters(final String stackName, final Class parameterClass) { + final String stackId = cloudFormationService.getStackId(stackName); + + if (!cloudFormationService.isStackPresent(stackId)) { + throw new IllegalStateException("Failed to get CloudFormation parameters for stack: '" + stackName + "'. Stack does not exist."); + } + + final Map stackOutputs = cloudFormationService.getStackParameters(stackId); + return cloudFormationObjectMapper.convertValue(stackOutputs, parameterClass); } /** diff --git a/src/main/resources/cloudformation/cms-cluster.yaml b/src/main/resources/cloudformation/cms-cluster.yaml index bf55b8db..2ef607ab 100644 --- a/src/main/resources/cloudformation/cms-cluster.yaml +++ b/src/main/resources/cloudformation/cms-cluster.yaml @@ -8,32 +8,32 @@ Outputs: cmsInstanceProfileName: Value: !Ref 'CmsInstanceProfile' Parameters: - loadBalancerStackName: - Description: The name of the Cerberus ALB CloudFormation stack - Type: String baseStackName: Description: The name of the Cerberus base CloudFormation stack Type: String - cmsAmiId: + amiId: Description: The AMI ID for the CMS instances Type: String desiredInstances: - Default: '3' + Default: 3 Description: Desired Number of Auto Scaling Instances Type: Number instanceSize: - Default: 'm3.medium' + Default: 'm3.medium' Description: The instance size for the CMS instances Type: String keyPairName: Description: The key pair to be associated with the EC2 instances Type: String + loadBalancerStackName: + Description: The name of the Cerberus ALB CloudFormation stack + Type: String maximumInstances: - Default: '4' + Default: 4 Description: Maximum Number of Auto Scaling Instances (must be larger than min) Type: Number minimumInstances: - Default: '3' + Default: 3 Description: Minimum Number of Auto Scaling Instances Type: Number pauseTime: @@ -118,7 +118,7 @@ Resources: Properties: AssociatePublicIpAddress: 'true' IamInstanceProfile: !Ref 'CmsInstanceProfile' - ImageId: !Ref 'cmsAmiId' + ImageId: !Ref 'amiId' InstanceMonitoring: 'true' InstanceType: !Ref 'instanceSize' KeyName: !Ref 'keyPairName' diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml index 5797e644..f0d7a564 100644 --- a/src/main/resources/cloudformation/database.yaml +++ b/src/main/resources/cloudformation/database.yaml @@ -5,13 +5,30 @@ Description: Creates the database for use by the Cerberus Management Service (CM Outputs: cmsDbAddress: Value: !GetAtt 'CmsDatabaseCluster.Endpoint.Address' - cmsDbId: - Value: !Ref 'CmsDbInstance' + cmsDbInstanceId1: + Value: !Ref 'CmsDbInstance1' + cmsDbInstanceId2: + Value: !Ref 'CmsDbInstance2' cmsDbJdbcConnectionString: Description: JDBC connection string for cms database - Value: !Join ['', ['jdbc:mysql://', !GetAtt 'CmsDbInstance.Endpoint.Address', ':', + Value: !Join ['', ['jdbc:mysql://', !GetAtt 'CmsDatabaseCluster.Endpoint.Address', ':', !GetAtt 'CmsDatabaseCluster.Endpoint.Port', /, !Ref 'cmsDbName', '?useUnicode=true&characterEncoding=utf8&useLegacyDatetimeCode=false&serverTimezone=UTC']] Parameters: + cmsDbInstanceAz1: + Default: 'us-west-2a' + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: The first availability zone for the DB Instances + Type: String + cmsDbInstanceAz2: + Default: 'us-west-2b' + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: The second availability zone for the DB Instances + Type: String + cmsDbInstanceAz3: + Default: 'us-west-2b' + AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' + Description: The third availability zone for the DB Instances + Type: String cmsDbInstanceSize: Default: db.r3.large Description: MySQL DB instance class @@ -21,19 +38,13 @@ Parameters: Type: String NoEcho: true cmsDbMasterUsername: + Default: 'cms' Description: Master username for the cms RDS instance Type: String cmsDbName: + Default: 'cms' Description: The name of the database initially create on the RDS instance Type: String - cmsDbPrimaryAz: - AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' - Description: The availability zone for the primary CMS DB Instance - Type: String - cmsDbReplicaAz: - AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' - Description: The availability zone for the standby read replica CMS DB Instance - Type: String sgStackName: Description: The name of the Cerberus Security Groups CloudFormation stack Type: String @@ -64,8 +75,9 @@ Resources: CmsDatabaseCluster: Properties: AvailabilityZones: - - Ref: 'cmsDbPrimaryAz' - - Ref: 'cmsDbReplicaAz' + - Ref: 'cmsDbInstanceAz1' + - Ref: 'cmsDbInstanceAz2' + - Ref: 'cmsDbInstanceAz3' BackupRetentionPeriod: 14 DatabaseName: !Ref 'cmsDbName' DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' @@ -89,7 +101,10 @@ Resources: VpcSecurityGroupIds: - Fn::ImportValue: !Sub "${sgStackName}-cmsDbSgId" Type: AWS::RDS::DBCluster - CmsDbInstance: + # Create two DB instances one primary/write instance and the other a read replica. + # The Aurora CloudFormation does not let you choose which is read or write. + # Aurora decides automatically for you behind the scenes. + CmsDbInstance1: Properties: DBClusterIdentifier: !Ref CmsDatabaseCluster DBInstanceClass: !Ref 'cmsDbInstanceSize' @@ -98,9 +113,7 @@ Resources: Engine: aurora PubliclyAccessible: 'false' Type: AWS::RDS::DBInstance - # There is no guarantee that this instance will be the read replica, since - # RDS decides automatically - CmsReadReplicaDbInstance: + CmsDbInstance2: Properties: DBClusterIdentifier: !Ref CmsDatabaseCluster DBInstanceClass: !Ref 'cmsDbInstanceSize' diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 3ae5bd5e..0f9ba6f3 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -3,20 +3,20 @@ Conditions: RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] Description: Creates the application Load Balancer for the Cerberus environment Outputs: - albAccessLogBucket: - Value: !Ref 'ALBLogBucket' - appLoadBalancerLogicalId: - Value: !Ref 'ApplicationLoadBalancer' - Export: - Name: !Sub "${AWS::StackName}-appLoadBalancerLogicalId" - albDnsName: - Value: !GetAtt 'ApplicationLoadBalancer.DNSName' - Export: - Name: !Sub "${AWS::StackName}-appLoadBalancerDnsName" cmsTargetGroup: Value: !Ref 'CmsTargetGroup' Export: Name: !Sub "${AWS::StackName}-cmsTargetGroupArn" + loadBalancerAccessLogBucket: + Value: !Ref 'ALBLogBucket' + loadBalancerDnsName: + Value: !GetAtt 'ApplicationLoadBalancer.DNSName' + Export: + Name: !Sub "${AWS::StackName}-loadBalancerDnsName" + loadBalancerLogicalId: + Value: !Ref 'ApplicationLoadBalancer' + Export: + Name: !Sub "${AWS::StackName}-loadBalancerLogicalId" Parameters: sgStackName: Description: The name of the stack containing Cerberus IAM roles and SGs stack diff --git a/src/main/resources/cloudformation/route53.yaml b/src/main/resources/cloudformation/route53.yaml index 69354c4a..b7f12b59 100644 --- a/src/main/resources/cloudformation/route53.yaml +++ b/src/main/resources/cloudformation/route53.yaml @@ -22,7 +22,7 @@ Resources: Ref: hostedZoneId Name: !Join [., [!Ref 'hostname', '']] ResourceRecords: - - Fn::ImportValue: !Sub "${loadBalancerStackName}-appLoadBalancerDnsName" + - Fn::ImportValue: !Sub "${loadBalancerStackName}-loadBalancerDnsName" TTL: 30 Type: CNAME Type: AWS::Route53::RecordSet \ No newline at end of file diff --git a/src/main/resources/cloudformation/security-groups.yaml b/src/main/resources/cloudformation/security-groups.yaml index 83ffbf63..c28a3341 100644 --- a/src/main/resources/cloudformation/security-groups.yaml +++ b/src/main/resources/cloudformation/security-groups.yaml @@ -1,10 +1,6 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Launches the Security Groups required for Cerberus Outputs: - appLoadBalancerSgId: - Value: !GetAtt 'AlbSg.GroupId' - Export: - Name: !Sub "${AWS::StackName}-appLoadBalancerSgId" cmsDbPort: Value: !Ref 'cmsDbPort' Export: @@ -17,25 +13,26 @@ Outputs: Value: !GetAtt 'CmsDbSg.GroupId' Export: Name: !Sub "${AWS::StackName}-cmsDbSgId" +loadBalancerSgId: + Value: !GetAtt 'AlbSg.GroupId' + Export: + Name: !Sub "${AWS::StackName}-loadBalancerSgId" whitelistIngressSgId: Value: !GetAtt 'WhitelistIngressSg.GroupId' Export: Name: !Sub "${AWS::StackName}-whitelistIngressSgId" Parameters: - appLoadBalancerCidrBlock: + cmsDbPort: + Default: '3306' + Description: Port for the cms DB instance + Type: Number + loadBalancerCidrBlock: AllowedPattern: '[0-9./]*' Default: 0.0.0.0/0 Description: The application load balancer CIDR block for where traffic is allowed from MaxLength: '20' MinLength: '9' Type: String - cmsDbPort: - Default: '3306' - Description: Port for the cms DB instance - Type: Number - vpcId: - Description: ID of the VPC to associate with the security groups - Type: String tagClassification: Default: Gold Description: Denotes which category of Data Classification the instance is grouped @@ -50,6 +47,9 @@ Parameters: tagName: Description: 'Environment name, e.g. "cerberus-{environment}"' Type: String + vpcId: + Description: ID of the VPC to associate with the security groups + Type: String Resources: AlbSg: Properties: @@ -68,7 +68,7 @@ Resources: Type: AWS::EC2::SecurityGroup AlbIngressFromInternetSg443: Properties: - CidrIp: !Ref 'appLoadBalancerCidrBlock' + CidrIp: !Ref 'loadBalancerCidrBlock' FromPort: 443 GroupId: !Ref 'AlbSg' IpProtocol: tcp diff --git a/src/main/resources/cloudformation/vpc.yaml b/src/main/resources/cloudformation/vpc.yaml index 7dd56fab..2a22608c 100644 --- a/src/main/resources/cloudformation/vpc.yaml +++ b/src/main/resources/cloudformation/vpc.yaml @@ -12,23 +12,19 @@ Outputs: subnetCidrBlockForAz3: Description: Cidr block for subnet in AZ '3' Value: !Ref 'subnetCidrBlockForAz3' - vpcSubnetForAz1: + vpcSubnetIdForAz1: Description: The VPC subnet in AZ '1' Value: !Ref 'CerberusSubnetForAz1' - vpcSubnetForAz2: + vpcSubnetIdForAz2: Description: The VPC subnet in AZ '2' Value: !Ref 'CerberusSubnetForAz2' - vpcSubnetForAz3: + vpcSubnetIdForAz3: Description: The VPC subnet in AZ '3' Value: !Ref 'CerberusSubnetForAz3' + vpcId: + Description: ID of the created VPC + Value: !Ref 'CerberusVpc' Parameters: - internetGatewayCidrBlock: - AllowedPattern: '[0-9./]*' - Default: 0.0.0.0/0 - Description: The application load balancer CIDR block for where traffic is allowed from - MaxLength: '20' - MinLength: '9' - Type: String az1: AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' Description: An availability zone identifier for zone '1' @@ -41,6 +37,13 @@ Parameters: AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' Description: An availability zone identifier for zone '3' Type: String + internetGatewayCidrBlock: + AllowedPattern: '[0-9./]*' + Default: 0.0.0.0/0 + Description: The application load balancer CIDR block for where traffic is allowed from + MaxLength: '20' + MinLength: '9' + Type: String subnetCidrBlockForAz1: AllowedPattern: '[0-9./]*' Default: 172.20.0.0/24 diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml index 29b66680..ea4dbe15 100644 --- a/src/main/resources/cloudformation/web-app-firewall.yaml +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -7,8 +7,8 @@ Outputs: Value: !Ref 'WAFAutoBlockSet' manualBlockIPSetID: Value: !Ref 'WAFManualBlockSet' - whiteListIPSetID: - Value: !Ref 'WAFWhiteListSet' + whitelistIPSetID: + Value: !Ref 'WAFWhitelistSet' Parameters: loadBalancerStackName: Description: The name of the Cerberus load balancer CloudFormation stack @@ -164,16 +164,16 @@ Resources: Name: Manual Block Set Type: AWS::WAFRegional::IPSet WAFWhiteListRule: - DependsOn: WAFWhiteListSet + DependsOn: WAFWhitelistSet Properties: MetricName: WhiteListRule Name: White List Rule Predicates: - - DataId: !Ref 'WAFWhiteListSet' + - DataId: !Ref 'WAFWhitelistSet' Negated: 'false' Type: IPMatch Type: AWS::WAFRegional::Rule - WAFWhiteListSet: + WAFWhitelistSet: Properties: Name: White List Set Type: AWS::WAFRegional::IPSet \ No newline at end of file diff --git a/src/main/resources/example-standup.yaml b/src/main/resources/example-standup.yaml index a6b7805a..5c7341be 100644 --- a/src/main/resources/example-standup.yaml +++ b/src/main/resources/example-standup.yaml @@ -19,7 +19,7 @@ cost-center: 11111 # The e-mail for who owns the provisioned resources. Will be tagged on all resources. owner-email: obvisouly.fake@nike.com # The owning group for the provision resources. Will be tagged on all resources. -owner-group: cloud platform engineering +owner-group: engineering team name # A IAM role ARN that will be given elevated privileges for the KMS CMK created., # If you don't separate root access from admins just use the root role here diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index ad717eeb..0cf426b7 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -92,9 +92,9 @@ public void test_create_base() { "-f", "/path/to/environment.yaml", commandName, "--admin-role-arn", "arn:aws:iam::111111111:role/onelogin-roles-OneLoginAdminRole-2222222222", - "--vpc-hosted-zone-name", "demo.internal.cerberus-oss.io", "--owner-email", "obvisouly.fake@nike.com", - "--costcenter", "11111" + "--costcenter", "11111", + "--owner-group", "engineering team name" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -153,7 +153,7 @@ public void test_create_management_service_cluster() { StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", StackDelegate.COST_CENTER_LONG_ARG, "11111", StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "cloud platform engineering" + StackDelegate.OWNER_GROUP_LONG_ARG, "engineering team name" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -221,7 +221,7 @@ public void test_update_stack_with_no_overwrite_flag_or_dyn_props() { StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", StackDelegate.COST_CENTER_LONG_ARG, "11111", StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "cloud platform engineering", + StackDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -244,7 +244,7 @@ public void test_update_stack_with_overwrite_flag_and_dyn_props() { StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", StackDelegate.COST_CENTER_LONG_ARG, "11111", StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "cloud platform engineering", + StackDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", UpdateStackCommand.OVERWRITE_TEMPLATE_LONG_ARG, UpdateStackCommand.PARAMETER_SHORT_ARG, "k=v", }; diff --git a/src/test/resources/environment.yaml b/src/test/resources/environment.yaml index 7392a44b..ca55fc66 100644 --- a/src/test/resources/environment.yaml +++ b/src/test/resources/environment.yaml @@ -18,7 +18,7 @@ cost-center: 11111 # The e-mail for who owns the provisioned resources. Will be tagged on all resources. owner-email: obvisouly.fake@nike.com # The owning group for the provision resources. Will be tagged on all resources. -owner-group: cloud platform engineering +owner-group: engineering team name # A IAM role ARN that will be given elevated privileges for the KMS CMK created., # If you don't separate root access from admins just use the root role here From f8ca50f658e1d80b3feb9823f392e364cf966e64 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 8 Nov 2017 12:29:14 -0800 Subject: [PATCH 12/69] fix merge conflict --- src/main/java/com/nike/cerberus/module/CerberusModule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index 50582595..d31218a0 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -38,6 +38,8 @@ import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.lambda.AWSLambda; import com.amazonaws.services.lambda.AWSLambdaClient; +import com.amazonaws.services.route53.AmazonRoute53; +import com.amazonaws.services.route53.AmazonRoute53Client; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.Bucket; @@ -113,6 +115,7 @@ protected void configure() { bind(AWSSecurityTokenService.class).toInstance(createAmazonClientInstance(AWSSecurityTokenServiceClient.class, region)); bind(AWSLambda.class).toInstance(createAmazonClientInstance(AWSLambdaClient.class, region)); bind(AmazonSNS.class).toInstance(createAmazonClientInstance(AmazonSNSClient.class, region)); + bind(AmazonRoute53.class).toInstance(createAmazonClientInstance(AmazonRoute53Client.class, region)); } /** From 32e2f03f0fe17dd03fe0124b4d267c057aa248c2 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Wed, 8 Nov 2017 15:08:25 -0800 Subject: [PATCH 13/69] New Command Bug Fixes and Cleanup (#76) * Move tag arg constants to tags delegate * Fix bug in tag delegate + bug in base operation * Remove commented code from database operation * Remove dynamic parameters from tag delegate * Remove unused imports * Use stack delegate in update stack command --- .../com/nike/cerberus/cli/CerberusRunner.java | 16 +++--- .../cli/EnvironmentConfigToArgsMapper.java | 7 +-- .../nike/cerberus/command/StackDelegate.java | 10 ++-- .../command/core/CreateBaseCommand.java | 9 ++++ .../command/core/UpdateStackCommand.java | 49 +++---------------- .../cloudformation/TagParametersDelegate.java | 23 ++------- .../domain/cloudformation/VpcOutputs.java | 6 +-- .../operation/core/CreateBaseOperation.java | 8 +-- .../core/CreateDatabaseOperation.java | 3 -- .../operation/core/UpdateStackOperation.java | 32 ++++++------ .../EnvironmentConfigToArgsMapperTest.java | 19 +++---- 11 files changed, 68 insertions(+), 114 deletions(-) diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 369f6bf0..cddd69ec 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -31,22 +31,22 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; +import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; -import com.nike.cerberus.command.core.GenerateCertsCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; -import com.nike.cerberus.command.core.ViewConfigCommand; -import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.GenerateCertsCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; import com.nike.cerberus.command.core.RestoreCerberusBackupCommand; +import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; +import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; -import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; +import com.nike.cerberus.command.core.ViewConfigCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.logging.LoggingConfigurer; @@ -94,7 +94,7 @@ public void run(String[] args) { String commandName = commander.getParsedCommand(); Command command = commandMap.get(commandName); - if(cerberusCommand.isVersion()) { + if (cerberusCommand.isVersion()) { printCliVersion(); } else if (cerberusCommand.isHelp() || commandName == null) { cerberusHelp.print(); @@ -117,8 +117,7 @@ public void run(String[] args) { } catch (Throwable e) { if (cerberusCommand.isHelp()) { cerberusHelp.print(); - } - else { + } else { System.err.println(Chalk.on("ERROR: " + e.getMessage()).red().bold().toString()); e.printStackTrace(); cerberusHelp.print(); @@ -212,6 +211,7 @@ private void configureLogging(boolean isDebug) { /** * Program entry point. Instantiates and calls run. + * * @param args Command line arguments */ public static void main(String[] args) { diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index fd65ed7c..92cc868a 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -30,6 +30,7 @@ import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.CerberusStack; import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.domain.input.ManagementService; @@ -166,11 +167,11 @@ private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List } private static void addTagArgs(EnvironmentConfig environmentConfig, List args) { - args.add(StackDelegate.COST_CENTER_LONG_ARG); + args.add(TagParametersDelegate.COST_CENTER_LONG_ARG); args.add(environmentConfig.getCostCenter()); - args.add(StackDelegate.OWNER_EMAIL_LONG_ARG); + args.add(TagParametersDelegate.OWNER_EMAIL_LONG_ARG); args.add(environmentConfig.getOwnerEmail()); - args.add(StackDelegate.OWNER_GROUP_LONG_ARG); + args.add(TagParametersDelegate.OWNER_GROUP_LONG_ARG); args.add(environmentConfig.getOwnerGroup()); } diff --git a/src/main/java/com/nike/cerberus/command/StackDelegate.java b/src/main/java/com/nike/cerberus/command/StackDelegate.java index 1e41e03b..b8274762 100644 --- a/src/main/java/com/nike/cerberus/command/StackDelegate.java +++ b/src/main/java/com/nike/cerberus/command/StackDelegate.java @@ -18,6 +18,7 @@ import com.beust.jcommander.DynamicParameter; import com.beust.jcommander.Parameter; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import java.util.HashMap; import java.util.Map; @@ -30,9 +31,6 @@ public class StackDelegate { public static final String AMI_ID_LONG_ARG = "--ami-id"; public static final String INSTANCE_SIZE_LONG_ARG = "--instance-size"; public static final String KEY_PAIR_NAME_LONG_ARG = "--key-pair-name"; - public static final String OWNER_GROUP_LONG_ARG = "--owner-group"; - public static final String OWNER_EMAIL_LONG_ARG = "--owner-email"; - public static final String COST_CENTER_LONG_ARG = "--costcenter"; public static final String PARAMETER_SHORT_ARG = "-P"; @Parameter(names = AMI_ID_LONG_ARG, description = "The AMI ID for the specified stack.", required = true) @@ -44,17 +42,17 @@ public class StackDelegate { @Parameter(names = KEY_PAIR_NAME_LONG_ARG, required = true, description = "SSH key pair name.") private String keyPairName; - @Parameter(names = OWNER_GROUP_LONG_ARG, + @Parameter(names = TagParametersDelegate.OWNER_GROUP_LONG_ARG, description = "The owning group for the provision resources. Will be tagged on all resources.", required = true) private String ownerGroup; - @Parameter(names = OWNER_EMAIL_LONG_ARG, + @Parameter(names = TagParametersDelegate.OWNER_EMAIL_LONG_ARG, description = "The e-mail for who owns the provisioned resources. Will be tagged on all resources.", required = true) private String ownerEmail; - @Parameter(names = COST_CENTER_LONG_ARG, + @Parameter(names = TagParametersDelegate.COST_CENTER_LONG_ARG, description = "Costcenter for where to bill provisioned resources. Will be tagged on all resources.", required = true) private String costcenter; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java index e5ca9fa1..7bf1eb14 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java @@ -24,7 +24,9 @@ import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.Command; import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; +import com.nike.cerberus.domain.cloudformation.TagParameters; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; @@ -54,10 +56,17 @@ public class CreateBaseCommand implements Command { required = true) private String adminRoleArn; + @ParametersDelegate + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + public String getAdminRoleArn() { return adminRoleArn; } + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java index 4bf88a5e..36a02064 100644 --- a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java @@ -19,8 +19,10 @@ import com.beust.jcommander.DynamicParameter; 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.command.StackDelegate; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.UpdateStackOperation; @@ -46,27 +48,8 @@ public class UpdateStackCommand implements Command { @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to update.") private StackName stackName; - @Parameter(names = StackDelegate.OWNER_GROUP_LONG_ARG, - description = "The owning group for the resources to be updated. Will be tagged on all resources.", - required = true) - private String ownerGroup; - - @Parameter(names = StackDelegate.AMI_ID_LONG_ARG, description = "The AMI ID for the specified stack.") - private String amiId; - - @Parameter(names = StackDelegate.INSTANCE_SIZE_LONG_ARG, description = "Specify a custom instance size.") - private String instanceSize; - - @Parameter(names = StackDelegate.KEY_PAIR_NAME_LONG_ARG, description = "SSH key pair name.") - private String keyPairName; - - @Parameter(names = StackDelegate.OWNER_EMAIL_LONG_ARG, - description = "The e-mail for who owns the provisioned resources. Will be tagged on all resources.") - private String ownerEmail; - - @Parameter(names = StackDelegate.COST_CENTER_LONG_ARG, - description = "Costcenter for where to bill provisioned resources. Will be tagged on all resources.") - private String costcenter; + @ParametersDelegate + private StackDelegate stackDelegate = new StackDelegate(); @Parameter(names = OVERWRITE_TEMPLATE_LONG_ARG, description = "Flag for overwriting existing CloudFormation template") @@ -83,28 +66,8 @@ public StackName getStackName() { return stackName; } - public String getOwnerGroup() { - return ownerGroup; - } - - public String getAmiId() { - return amiId; - } - - public String getInstanceSize() { - return instanceSize; - } - - public String getKeyPairName() { - return keyPairName; - } - - public String getOwnerEmail() { - return ownerEmail; - } - - public String getCostcenter() { - return costcenter; + public StackDelegate getStackDelegate() { + return stackDelegate; } public boolean isOverwriteTemplate() { diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java index ce193fa5..ebb51c43 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java @@ -16,21 +16,17 @@ package com.nike.cerberus.domain.cloudformation; -import com.beust.jcommander.DynamicParameter; import com.beust.jcommander.Parameter; -import java.util.HashMap; -import java.util.Map; - -import static com.nike.cerberus.command.StackDelegate.COST_CENTER_LONG_ARG; -import static com.nike.cerberus.command.StackDelegate.OWNER_EMAIL_LONG_ARG; -import static com.nike.cerberus.command.StackDelegate.PARAMETER_SHORT_ARG; - /** * CloudFormation input parameters common to all Cerberus CloudFormation stacks. */ public class TagParametersDelegate { + public static final String OWNER_EMAIL_LONG_ARG = "--owner-email"; + public static final String OWNER_GROUP_LONG_ARG = "--owner-group"; + public static final String COST_CENTER_LONG_ARG = "--costcenter"; + @Parameter(names = "--tag-name", description = "The environment name (e.g. 'cerberus-demo', 'cerberus-preprod')") private String tagName; @@ -45,9 +41,6 @@ public class TagParametersDelegate { required = true) private String tagCostcenter; - @DynamicParameter(names = PARAMETER_SHORT_ARG, description = "Dynamic parameters for overriding the values for specific parameters in the CloudFormation.") - private Map dynamicParameters = new HashMap<>(); - public String getTagName() { return tagName; } @@ -74,12 +67,4 @@ public TagParametersDelegate setTagCostcenter(String tagCostcenter) { this.tagCostcenter = tagCostcenter; return this; } - - public Map getDynamicParameters() { - return dynamicParameters; - } - - public void setDynamicParameters(Map dynamicParameters) { - this.dynamicParameters = dynamicParameters; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java index ba589877..9e331dcd 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java @@ -21,7 +21,7 @@ */ public class VpcOutputs { - private Integer subnetCidrBlockForAz1; + private String subnetCidrBlockForAz1; private String subnetCidrBlockForAz2; @@ -35,11 +35,11 @@ public class VpcOutputs { private String vpcId; - public Integer getSubnetCidrBlockForAz1() { + public String getSubnetCidrBlockForAz1() { return subnetCidrBlockForAz1; } - public VpcOutputs setSubnetCidrBlockForAz1(Integer subnetCidrBlockForAz1) { + public VpcOutputs setSubnetCidrBlockForAz1(String subnetCidrBlockForAz1) { this.subnetCidrBlockForAz1 = subnetCidrBlockForAz1; return this; } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index 7ba49d40..3ed9b4c4 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -62,12 +62,12 @@ public void run(final CreateBaseCommand command) { final BaseParameters baseParameters = new BaseParameters() .setAccountAdminArn(command.getAdminRoleArn()); - baseParameters.getTagParameters().setTagEmail(baseParameters.getTagParameters().getTagEmail()); + baseParameters.getTagParameters().setTagEmail(command.getTagParameters().getTagEmail()); baseParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - baseParameters.getTagParameters().setTagCostcenter(baseParameters.getTagParameters().getTagCostcenter()); + baseParameters.getTagParameters().setTagCostcenter(command.getTagParameters().getTagCostcenter()); + + final TypeReference> typeReference = new TypeReference>() {}; - final TypeReference> typeReference = new TypeReference>() { - }; final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); cloudFormationService.createStack(StackName.BASE.getFullName(environmentName), diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index 55970b80..6bbc29f8 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -92,9 +92,6 @@ public void run(final CreateDatabaseCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); - // allow user to overwrite CloudFormation parameters with -P option - parameters.putAll(command.getTagsDelegate().getDynamicParameters()); - cloudFormationService.createStack(StackName.DATABASE.getFullName(environmentName), parameters, ConfigConstants.DATABASE_STACK_TEMPLATE_PATH, true); } diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 795785bd..000886ae 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -109,7 +109,7 @@ public void run(final UpdateStackCommand command) { // Make sure the given AmiId is for this component. Check if it contains required tag // There is no AMI for Base. if ( !command.isSkipAmiTagCheck() && StackName.BASE != command.getStackName() ) { - amiTagCheckService.validateAmiTagForStack(command.getAmiId(),command.getStackName()); + amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(),command.getStackName()); } parameters.putAll(command.getDynamicParameters()); @@ -174,26 +174,26 @@ private Map getUpdateLaunchConfigParameters(final StackName stac configStore.getStackParameters(stackName, parametersClass); launchConfigParameters.getLaunchConfigParameters().setUserData( - ec2UserDataService.getUserData(stackName, command.getOwnerGroup())); + ec2UserDataService.getUserData(stackName, command.getStackDelegate().getOwnerGroup())); - if (StringUtils.isNotBlank(command.getAmiId())) { - launchConfigParameters.getLaunchConfigParameters().setAmiId(command.getAmiId()); + if (StringUtils.isNotBlank(command.getStackDelegate().getAmiId())) { + launchConfigParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); } - if (StringUtils.isNotBlank(command.getInstanceSize())) { - launchConfigParameters.getLaunchConfigParameters().setInstanceSize(command.getInstanceSize()); + if (StringUtils.isNotBlank(command.getStackDelegate().getInstanceSize())) { + launchConfigParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); } - if (StringUtils.isNotBlank(command.getKeyPairName())) { - launchConfigParameters.getLaunchConfigParameters().setKeyPairName(command.getKeyPairName()); + if (StringUtils.isNotBlank(command.getStackDelegate().getKeyPairName())) { + launchConfigParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); } - if (StringUtils.isNotBlank(command.getOwnerEmail())) { - launchConfigParameters.getTagParameters().setTagEmail(command.getOwnerEmail()); + if (StringUtils.isNotBlank(command.getStackDelegate().getOwnerEmail())) { + launchConfigParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); } - if (StringUtils.isNotBlank(command.getCostcenter())) { - launchConfigParameters.getTagParameters().setTagCostcenter(command.getCostcenter()); + if (StringUtils.isNotBlank(command.getStackDelegate().getCostcenter())) { + launchConfigParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); } final TypeReference> typeReference = new TypeReference>() {}; @@ -203,12 +203,12 @@ private Map getUpdateLaunchConfigParameters(final StackName stac private Map getUpdatedBaseStackParameters(final UpdateStackCommand command) { final TagParameters tagParameters = configStore.getStackParameters(command.getStackName(), SecurityGroupParameters.class); - if (StringUtils.isNotBlank(command.getOwnerEmail())) { - tagParameters.getTagParameters().setTagEmail(command.getOwnerEmail()); + if (StringUtils.isNotBlank(command.getStackDelegate().getOwnerEmail())) { + tagParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); } - if (StringUtils.isNotBlank(command.getCostcenter())) { - tagParameters.getTagParameters().setTagCostcenter(command.getCostcenter()); + if (StringUtils.isNotBlank(command.getStackDelegate().getCostcenter())) { + tagParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); } final TypeReference> typeReference = new TypeReference>() {}; diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index 0cf426b7..0bf429ad 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -26,6 +26,7 @@ import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; import org.junit.Before; @@ -151,9 +152,9 @@ public void test_create_management_service_cluster() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - StackDelegate.COST_CENTER_LONG_ARG, "11111", - StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "engineering team name" + TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", + TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", + TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -219,9 +220,9 @@ public void test_update_stack_with_no_overwrite_flag_or_dyn_props() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - StackDelegate.COST_CENTER_LONG_ARG, "11111", - StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", + TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", + TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", + TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -242,9 +243,9 @@ public void test_update_stack_with_overwrite_flag_and_dyn_props() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - StackDelegate.COST_CENTER_LONG_ARG, "11111", - StackDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - StackDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", + TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", + TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", + TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", UpdateStackCommand.OVERWRITE_TEMPLATE_LONG_ARG, UpdateStackCommand.PARAMETER_SHORT_ARG, "k=v", }; From bcb94e270fd8be20df62afa5561bc8523e5e3987 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Thu, 9 Nov 2017 09:34:17 -0800 Subject: [PATCH 14/69] Feature/composite command pattern (#79) * Add abstract composit command and example command to allow the creation of complex command chains * revert change to EnvironmentConfigToArgsMapper and add logic in CompositeOperation to compensate --- gradle/dependencies.gradle | 2 +- .../com/nike/cerberus/cli/CerberusRunner.java | 5 +- .../cli/EnvironmentConfigToArgsMapper.java | 2 +- .../PrintAllStackInformationCommand.java | 46 ++++++ .../command/core/PrintStackInfoCommand.java | 4 +- .../nike/cerberus/module/CerberusModule.java | 15 +- .../operation/composite/ChainableCommand.java | 85 ++++++++++ .../composite/CompositeOperation.java | 149 ++++++++++++++++++ .../PrintAllStackInformationOperation.java | 53 +++++++ .../EnvironmentConfigToArgsMapperTest.java | 6 +- 10 files changed, 355 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/composite/PrintAllStackInformationCommand.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 939de122..3dadaf9b 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -38,7 +38,7 @@ allprojects { compile 'com.nike:vault-client:1.4.1' compile 'com.squareup.okhttp3:okhttp:3.3.1' - compile 'com.beust:jcommander:1.55' + compile 'com.beust:jcommander:1.72' compile 'com.fasterxml.jackson.core:jackson-core:2.7.+' compile 'com.fasterxml.jackson.core:jackson-databind:2.7.+' diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index cddd69ec..28cc0a39 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -31,6 +31,7 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; +import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; @@ -99,8 +100,7 @@ public void run(String[] args) { } else if (cerberusCommand.isHelp() || commandName == null) { cerberusHelp.print(); } else { - Injector injector = Guice.createInjector(new CerberusModule(cerberusCommand.getProxyDelegate(), - cerberusCommand.getEnvironment(), cerberusCommand.getRegion()), new PropsModule()); + Injector injector = Guice.createInjector(new CerberusModule(cerberusCommand), new PropsModule()); // fail early if there is any problem in local environment LocalEnvironmentValidator validator = injector.getInstance(LocalEnvironmentValidator.class); @@ -169,6 +169,7 @@ private void registerAllCommands() { registerCommand(new CreateCmsCmkCommand()); registerCommand(new UpdateStackCommand()); registerCommand(new PrintStackInfoCommand()); + registerCommand(new PrintAllStackInformationCommand()); registerCommand(new WhitelistCidrForVpcAccessCommand()); registerCommand(new RestoreCerberusBackupCommand()); registerCommand(new ViewConfigCommand()); diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 92cc868a..f040c7ec 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -79,7 +79,7 @@ public static String[] getArgs(EnvironmentConfig environmentConfig, String[] pas return args.toArray(new String[args.size()]); } - private static List getArgsForCommand(EnvironmentConfig environmentConfig, String commandName, String[] passedArgs) { + public static List getArgsForCommand(EnvironmentConfig environmentConfig, String commandName, String[] passedArgs) { switch (commandName) { case CreateBaseCommand.COMMAND_NAME: return getCreateBaseCommandArgs(environmentConfig); diff --git a/src/main/java/com/nike/cerberus/command/composite/PrintAllStackInformationCommand.java b/src/main/java/com/nike/cerberus/command/composite/PrintAllStackInformationCommand.java new file mode 100644 index 00000000..482472c2 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/composite/PrintAllStackInformationCommand.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.composite.PrintAllStackInformationOperation; + +import static com.nike.cerberus.command.composite.PrintAllStackInformationCommand.COMMAND_NAME; + +/** + * Prints information for all the stacks + */ +@Parameters( + commandNames = COMMAND_NAME, + commandDescription = "Iterates through all known stacks for a cerberus environment and prints the stack info" +) +public class PrintAllStackInformationCommand implements Command { + + public static final String COMMAND_NAME = "print-stack-info-for-all-stacks"; + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return PrintAllStackInformationOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java b/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java index a13f0ef0..efaecd7c 100644 --- a/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java @@ -33,7 +33,9 @@ public class PrintStackInfoCommand implements Command { public static final String COMMAND_NAME = "print-stack-info"; - @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to print information about.") + public static final String STACK_NAME_LONG_ARG = "--stack-name"; + + @Parameter(names = {STACK_NAME_LONG_ARG}, required = true, description = "The stack name to print information about.") private StackName stackName; public StackName getStackName() { diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index d31218a0..78fde60a 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -56,9 +56,12 @@ import com.github.mustachejava.MustacheFactory; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.util.Providers; import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.command.CerberusCommand; import com.nike.cerberus.command.ProxyDelegate; import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.util.TokenSupplier; import com.nike.cerberus.util.UuidSupplier; import org.apache.commons.lang3.StringUtils; @@ -94,10 +97,13 @@ public class CerberusModule extends AbstractModule { private final String regionName; - public CerberusModule(ProxyDelegate proxyDelegate, String environmentName, String regionName) { - this.proxyDelegate = proxyDelegate; - this.environmentName = environmentName; - this.regionName = regionName; + private final EnvironmentConfig environmentConfig; + + public CerberusModule(CerberusCommand cerberusCommand) { + proxyDelegate = cerberusCommand.getProxyDelegate(); + environmentName = cerberusCommand.getEnvironment(); + regionName = cerberusCommand.getRegion(); + environmentConfig = cerberusCommand.getEnvironmentConfig(); } /** @@ -105,6 +111,7 @@ public CerberusModule(ProxyDelegate proxyDelegate, String environmentName, Strin */ @Override protected void configure() { + bind(EnvironmentConfig.class).toProvider(Providers.of(environmentConfig)); final Region region = Region.getRegion(Regions.fromName(regionName)); bind(AmazonEC2.class).toInstance(createAmazonClientInstance(AmazonEC2Client.class, region)); bind(AmazonCloudFormation.class).toInstance(createAmazonClientInstance(AmazonCloudFormationClient.class, region)); diff --git a/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java b/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java new file mode 100644 index 00000000..c7942488 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +import java.util.LinkedList; +import java.util.List; + +@SuppressFBWarnings( + value = "EI_EXPOSE_REP", + justification = "We don't care" +) +/** + * Wrapper object to represent a chainable command. + * + * @See CompositeOperation + */ +public class ChainableCommand { + + private Command command; + private Operation operation; + private String[] additionalArgs; + + public Command getCommand() { + return command; + } + + public String[] getAdditionalArgs() { + return additionalArgs; + } + + public Operation getOperation() { + return operation; + } + + public void setOperation(Operation operation) { + this.operation = operation; + } + + public static final class Builder { + private Command command; + private List additionalArgs = new LinkedList<>(); + + private Builder() { + } + + public static Builder create() { + return new Builder(); + } + + public Builder withCommand(Command command) { + this.command = command; + return this; + } + + public Builder withAdditionalArg(String additionalArg) { + additionalArgs.add(additionalArg); + return this; + } + + public ChainableCommand build() { + ChainableCommand chainableCommand = new ChainableCommand(); + chainableCommand.command = this.command; + chainableCommand.additionalArgs = this.additionalArgs.toArray(new String[additionalArgs.size()]); + return chainableCommand; + } + } +} diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java new file mode 100644 index 00000000..ee6fb4ac --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.beust.jcommander.JCommander; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.nike.cerberus.cli.EnvironmentConfigToArgsMapper; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.domain.input.EnvironmentConfig; +import com.nike.cerberus.operation.Operation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.LinkedList; +import java.util.List; + +/** + * This abstract class can be extended by any composite operation to enable chaining of commands + * together to perform complex operations. + * + * @param The command that this operation implements + */ +public abstract class CompositeOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private Injector injector; + + private EnvironmentConfig environmentConfig; + + private List runnableChainedCommands = new LinkedList<>(); + + @Inject + public void setInjector(Injector injector) { + this.injector = injector; + } + + @Inject + public void setEnvironmentConfig(@Nullable EnvironmentConfig environmentConfig) { + this.environmentConfig = environmentConfig; + } + + private Operation getOperationInstance(Command command) { + return injector.getInstance(command.getOperationClass()); + } + + /** + * {@inheritDoc} + * + * Runs all the chained commands in order that are defined in the runnable chained command list, + * which gets populated when isRunnable is executed + */ + @SuppressWarnings("unchecked") + public void run(T compositeCommand) { + runnableChainedCommands.forEach(chainableCommand -> { + Command chainedCommand = chainableCommand.getCommand(); + Operation chainedOperation = chainableCommand.getOperation(); + log.info("Attempting to run command: {}", chainedCommand.getCommandName()); + try { + chainedOperation.run(chainedCommand); + } catch (Throwable e) { + throw new RuntimeException("Failed to execute chained command: " + chainedCommand.getCommandName(), e); + } + log.info("Finished command: {}", chainedCommand.getCommandName()); + + }); + } + + /** + * {@inheritDoc} + * + * Gets the command chain from the implementing class and iterates over every chained command operation to make sure + * they are runnable, if any operation is not runnable this will return false + */ + public boolean isRunnable(T command) { + if (getIsEnvironmentConfigRequired() && environmentConfig == null) { + throw new RuntimeException(String.format("The %s command requires that -f or --file must be supplied as a global option with " + + "a path to a valid environment yaml", command.getCommandName())); + } + + for (ChainableCommand chainableCommand : getCompositeCommandChain()) { + Command chainedCommand = chainableCommand.getCommand(); + String[] additionalArgs = chainableCommand.getAdditionalArgs(); + + // Parse the yaml and additional args to get args to pass to jcommander + List argsList = EnvironmentConfigToArgsMapper.getArgsForCommand( + environmentConfig, chainedCommand.getCommandName(), additionalArgs); + + // If the mapper doesn't have a mapping for a given command it will return an empty list + // in this case we will just use the args manually provided by the chainable command + String[] args; + if (argsList.size() > 0) { + args = argsList.toArray(new String[argsList.size()]); + } else { + args = additionalArgs; + } + + // Use jcommander to bind the resolved args to the command object + JCommander.newBuilder().addObject(chainedCommand).build().parse(args); + + // Get the instance of the operation from guice + Operation operation = getOperationInstance(chainedCommand); + + // If the given command is not runnable return false for the whole chain + //noinspection unchecked + if (! operation.isRunnable(chainedCommand)) { + return false; + } + + // If the given command is runnable add the guice created operation to the object and add the command to the runnable list + log.debug("Command: {} with Args: {} is runnable, adding to runnable list", chainedCommand.getCommandName(), args); + chainableCommand.setOperation(operation); + runnableChainedCommands.add(chainableCommand); + } + return true; + } + + /** + * Implement this method to define the ordered list of chained commands that will get executed + * + * @return An ordered list of ChainableCommand's + */ + protected abstract List getCompositeCommandChain(); + + /** + * If you command doesn't require that the environment yaml be supplied, you can override this to false. + * + * @return boolean of whether or not the environment yaml is required. + */ + public boolean getIsEnvironmentConfigRequired() { + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java new file mode 100644 index 00000000..18b47880 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; +import com.nike.cerberus.command.core.PrintStackInfoCommand; +import com.nike.cerberus.domain.environment.StackName; + +import java.util.LinkedList; +import java.util.List; + +/** + * Composite Command for printing stack info for entire Cerberus Environment + */ +public class PrintAllStackInformationOperation extends CompositeOperation { + + @Override + protected List getCompositeCommandChain() { + List commandList = new LinkedList<>(); + + for (StackName stackName : StackName.values()) { + commandList.add( + ChainableCommand.Builder.create() + .withCommand(new PrintStackInfoCommand()) + .withAdditionalArg(PrintStackInfoCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(stackName.toString()) + .build() + ); + } + + return commandList; + } + + @Override + public boolean getIsEnvironmentConfigRequired() { + return false; + } + +} diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index 0bf429ad..38bb80eb 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -63,7 +63,7 @@ public void test_that_mapper_copies_pre_command_flags_with_flag() { String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - assertArgsAreEqual(expected, actual,commandName); + assertArgsAreEqual(expected, actual, commandName); } @Test @@ -79,7 +79,7 @@ public void test_that_mapper_copies_pre_command_flags_without_flag() { String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - assertArgsAreEqual(expected, actual,commandName); + assertArgsAreEqual(expected, actual, commandName); } @@ -100,7 +100,7 @@ public void test_create_base() { String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - assertArgsAreEqual(expected, actual,commandName); + assertArgsAreEqual(expected, actual, commandName); } @Test From ed160a23f1ec214e3f9287aa695cac942fcbbbce Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Thu, 9 Nov 2017 10:15:05 -0800 Subject: [PATCH 15/69] Fix Bugs in New Stack Commands + Cleanup (#78) * Fix bug in tags for security groups * Initialize environment config when creating base * Pass security group stack name to database * Fix bug in cert file validation * Retrieve config file KMS key id from base stack * Fix database output variable types * Fix create CMS config for new envs * Add environment name to cms env config * Use the right cms cluster cloudformation file * Make sure route53 record params are required * Fix bug with waf creation * Remove min max and desired args from launch config delegate --- .../com/nike/cerberus/ConfigConstants.java | 4 ++- .../command/core/CreateRoute53Command.java | 6 ++-- .../core/CreateSecurityGroupsCommand.java | 9 +++++ .../command/core/CreateWafCommand.java | 3 +- .../UploadCertFilesPathValidator.java | 2 +- .../cloudformation/DatabaseOutputs.java | 6 ++-- .../cloudformation/DatabaseParameters.java | 11 +++++++ .../LaunchConfigParametersDelegate.java | 33 ------------------- .../operation/core/CreateBaseOperation.java | 8 +++++ .../core/CreateDatabaseOperation.java | 7 ++++ .../core/CreateSecurityGroupsOperation.java | 4 +-- .../com/nike/cerberus/store/ConfigStore.java | 10 +++--- src/main/resources/cloudformation/base.yaml | 2 ++ .../cloudformation/security-groups.yaml | 2 +- .../cloudformation/web-app-firewall.yaml | 2 +- 15 files changed, 59 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index a3075337..72fa715c 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -48,7 +48,7 @@ public class ConfigConstants { public static final String BASE_STACK_TEMPLATE_PATH = "/cloudformation/base.yaml"; - public static final String CMS_STACK_TEMPLATE_PATH = "/cloudformation/cms-cluster.json"; + public static final String CMS_STACK_TEMPLATE_PATH = "/cloudformation/cms-cluster.yaml"; public static final String DATABASE_STACK_TEMPLATE_PATH = "/cloudformation/database.yaml"; @@ -84,6 +84,8 @@ public class ConfigConstants { public static final String HASH_SALT = "cms.auth.token.hash.salt"; + public static final String CMS_ENV_NAME = "cms.env.name"; + public static final ImmutableSet SYSTEM_CONFIGURED_CMS_PROPERTIES = ImmutableSet.of( ROOT_USER_ARN_KEY, ADMIN_ROLE_ARN_KEY, diff --git a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java index d02e979e..1a3000d1 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java @@ -38,11 +38,13 @@ public class CreateRoute53Command implements Command { public static final String HOSTED_ZONE_ID = "--hosted-zone-id"; @Parameter(names = HOSTNAME_LONG_ARG, - description = "The hostname of the Route53 record to be created for Cerberus (e.g. .cerberus.example.com)") + description = "The hostname of the Route53 record to be created for Cerberus (e.g. .cerberus.example.com)", + required = true) private String cerberusHostname; @Parameter(names = HOSTED_ZONE_ID, - description = "The Route53 Hosted Zone in which to create the new Cerberus record") + description = "The Route53 Hosted Zone in which to create the new Cerberus record", + required = true) private String hostedZoneId; public String getCerberusHostname() { diff --git a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java index 5752ce53..25f743b2 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java @@ -18,7 +18,9 @@ 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.TagParametersDelegate; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.CreateSecurityGroupsOperation; @@ -39,10 +41,17 @@ public class CreateSecurityGroupsCommand implements Command { description = "The CIDR from which to allow traffic to the load balancer") private String loadBalancerCidr; + @ParametersDelegate + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + public String getLoadBalancerCidr() { return loadBalancerCidr; } + public TagParametersDelegate getTagParameters() { + return tagParameters; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java index 4866c97a..373b9266 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java @@ -22,6 +22,7 @@ import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.CreateLoadBalancerOperation; +import com.nike.cerberus.operation.core.CreateWafOperation; import static com.nike.cerberus.command.core.CreateWafCommand.COMMAND_NAME; @@ -48,7 +49,7 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return CreateLoadBalancerOperation.class; + return CreateWafOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java index c566e5a7..6d3412b5 100644 --- a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java +++ b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java @@ -54,7 +54,7 @@ public void validate(final String name, final Path value) throws ParameterExcept } - final FilenameFilter filter = new RegexFileFilter("^.*\\.pem$"); + final FilenameFilter filter = new RegexFileFilter("^.*\\.(pem|crt)$"); final File[] files = certDirectory.listFiles(filter); Arrays.stream(files).forEach(file -> filenames.add(file.getName())); diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java index e15042c9..144e9076 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseOutputs.java @@ -21,7 +21,7 @@ */ public class DatabaseOutputs { - private Integer cmsDbAddress; + private String cmsDbAddress; private String cmsDbInstanceId1; @@ -29,11 +29,11 @@ public class DatabaseOutputs { private String cmsDbJdbcConnectionString; - public Integer getCmsDbAddress() { + public String getCmsDbAddress() { return cmsDbAddress; } - public DatabaseOutputs setCmsDbAddress(Integer cmsDbAddress) { + public DatabaseOutputs setCmsDbAddress(String cmsDbAddress) { this.cmsDbAddress = cmsDbAddress; return this; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java index 350f2d75..9e957006 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java @@ -37,6 +37,8 @@ public class DatabaseParameters implements TagParameters { private String cmsDbName; + private String sgStackName; + private String vpcSubnetIdForAz1; private String vpcSubnetIdForAz2; @@ -109,6 +111,15 @@ public DatabaseParameters setCmsDbName(String cmsDbName) { return this; } + public String getSgStackName() { + return sgStackName; + } + + public DatabaseParameters setSgStackName(String sgStackName) { + this.sgStackName = sgStackName; + return this; + } + public String getVpcSubnetIdForAz1() { return vpcSubnetIdForAz1; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParametersDelegate.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParametersDelegate.java index 11d51c6c..addb08e6 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParametersDelegate.java @@ -29,12 +29,6 @@ public class LaunchConfigParametersDelegate { private String userData; - private int desiredInstances; - - private int maximumInstances; - - private int minimumInstances; - public String getAmiId() { return amiId; } @@ -70,31 +64,4 @@ public LaunchConfigParametersDelegate setUserData(String userData) { this.userData = userData; return this; } - - public int getDesiredInstances() { - return desiredInstances; - } - - public LaunchConfigParametersDelegate setDesiredInstances(int desiredInstances) { - this.desiredInstances = desiredInstances; - return this; - } - - public int getMaximumInstances() { - return maximumInstances; - } - - public LaunchConfigParametersDelegate setMaximumInstances(int maximumInstances) { - this.maximumInstances = maximumInstances; - return this; - } - - public int getMinimumInstances() { - return minimumInstances; - } - - public LaunchConfigParametersDelegate setMinimumInstances(int minimumInstances) { - this.minimumInstances = minimumInstances; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index 3ed9b4c4..f0c21ca6 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -25,6 +25,7 @@ import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,14 +46,18 @@ public class CreateBaseOperation implements Operation { private final CloudFormationService cloudFormationService; + private final ConfigStore configStore; + private final ObjectMapper cloudFormationObjectMapper; @Inject public CreateBaseOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, + final ConfigStore configStore, @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; + this.configStore = configStore; this.cloudFormationObjectMapper = cloudformationObjectMapper; } @@ -72,6 +77,9 @@ public void run(final CreateBaseCommand command) { cloudFormationService.createStack(StackName.BASE.getFullName(environmentName), parameters, ConfigConstants.BASE_STACK_TEMPLATE_PATH, true); + + configStore.initEnvironmentData(); + configStore.initSecretsData(); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index 6bbc29f8..a5b98e67 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -78,6 +78,7 @@ public void run(final CreateDatabaseCommand command) { final DatabaseParameters databaseParameters = new DatabaseParameters() .setCmsDbMasterPassword(passwordGenerator.get()) + .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)) .setCmsDbInstanceAz1(vpcParameters.getAz1()) .setCmsDbInstanceAz2(vpcParameters.getAz2()) .setCmsDbInstanceAz3(vpcParameters.getAz3()) @@ -98,6 +99,12 @@ public void run(final CreateDatabaseCommand command) { @Override public boolean isRunnable(final CreateDatabaseCommand command) { String environmentName = environmentMetadata.getName(); + try { + cloudFormationService.getStackId(StackName.SECURITY_GROUPS.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); + } + return ! cloudFormationService.isStackPresent(StackName.DATABASE.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 731df035..84ac42f2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -71,9 +71,9 @@ public void run(final CreateSecurityGroupsCommand command) { .setVpcId(vpcOutputs.getVpcId()) .setLoadBalancerCidrBlock(command.getLoadBalancerCidr()); - securityGroupParameters.getTagParameters().setTagEmail(securityGroupParameters.getTagParameters().getTagEmail()); + securityGroupParameters.getTagParameters().setTagEmail(command.getTagParameters().getTagEmail()); securityGroupParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - securityGroupParameters.getTagParameters().setTagCostcenter(securityGroupParameters.getTagParameters().getTagCostcenter()); + securityGroupParameters.getTagParameters().setTagCostcenter(command.getTagParameters().getTagCostcenter()); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 769d0b26..8f07b881 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -71,6 +71,7 @@ import static com.nike.cerberus.ConfigConstants.CERT_PART_KEY; import static com.nike.cerberus.ConfigConstants.CERT_PART_PKCS8_KEY; import static com.nike.cerberus.ConfigConstants.CERT_PART_PUBKEY; +import static com.nike.cerberus.ConfigConstants.CMS_ENV_NAME; import static com.nike.cerberus.ConfigConstants.CMS_ROLE_ARN_KEY; import static com.nike.cerberus.ConfigConstants.HASH_SALT; import static com.nike.cerberus.ConfigConstants.JDBC_PASSWORD_KEY; @@ -407,7 +408,7 @@ private Properties generateBaseCmsSystemProperties() { final BaseOutputs baseOutputs = getBaseStackOutputs(); final DatabaseOutputs databaseOutputs = getDatabaseStackOutputs(); final BaseParameters baseParameters = getBaseStackParameters(); - final Optional cmsDatabasePassword = getCmsDatabasePassword(); + final String cmsDatabasePassword = getDatabaseStackParameters().getCmsDbMasterPassword(); final GetCallerIdentityResult callerIdentity = securityTokenService.getCallerIdentity( new GetCallerIdentityRequest()); @@ -419,7 +420,8 @@ private Properties generateBaseCmsSystemProperties() { properties.put(CMS_ROLE_ARN_KEY, baseOutputs.getCmsIamRoleArn()); properties.put(JDBC_URL_KEY, databaseOutputs.getCmsDbJdbcConnectionString()); properties.put(JDBC_USERNAME_KEY, ConfigConstants.DEFAULT_CMS_DB_NAME); - properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword.get()); + properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword); + properties.put(CMS_ENV_NAME, environmentMetadata.getName()); return properties; } @@ -817,10 +819,8 @@ private Optional getEncryptedObject(final String path) { private void initEncryptedConfigStoreService() { if (encryptedConfigStoreService == null) { - final Environment environment = getEnvironmentData(); - KMSEncryptionMaterialsProvider materialProvider = - new KMSEncryptionMaterialsProvider(environment.getConfigKeyId()); + new KMSEncryptionMaterialsProvider(getBaseStackOutputs().getConfigFileKeyId()); AmazonS3EncryptionClient encryptionClient = new AmazonS3EncryptionClient( diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/base.yaml index c8bbe9b9..8af10c40 100644 --- a/src/main/resources/cloudformation/base.yaml +++ b/src/main/resources/cloudformation/base.yaml @@ -7,6 +7,8 @@ Outputs: Value: !Ref 'CerberusConfigBucket' configFileKeyId: Value: !Ref 'ConfigFileKey' + cmsIamRoleArn: + Value: !GetAtt 'CmsIamRole.Arn' cmsIamRoleName: Value: !Ref 'CmsIamRole' Export: diff --git a/src/main/resources/cloudformation/security-groups.yaml b/src/main/resources/cloudformation/security-groups.yaml index c28a3341..b6543bae 100644 --- a/src/main/resources/cloudformation/security-groups.yaml +++ b/src/main/resources/cloudformation/security-groups.yaml @@ -13,7 +13,7 @@ Outputs: Value: !GetAtt 'CmsDbSg.GroupId' Export: Name: !Sub "${AWS::StackName}-cmsDbSgId" -loadBalancerSgId: + loadBalancerSgId: Value: !GetAtt 'AlbSg.GroupId' Export: Name: !Sub "${AWS::StackName}-loadBalancerSgId" diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml index ea4dbe15..1877ae0d 100644 --- a/src/main/resources/cloudformation/web-app-firewall.yaml +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -67,7 +67,7 @@ Resources: Type: "AWS::WAFRegional::WebACLAssociation" Properties: ResourceArn: - Fn::ImportValue: !Sub "${loadBalancerStackName}-appLoadBalancerLogicalId" + Fn::ImportValue: !Sub "${loadBalancerStackName}-loadBalancerLogicalId" WebACLId: !Ref 'CerberusWAFWebAcl' CerberusWafSizeConstraintRule: From 17bb7322e8e875e82af4a69ed7537a1b7d097e33 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Fri, 10 Nov 2017 09:09:15 -0800 Subject: [PATCH 16/69] Feature/remove nike specific tags and make generic (#82) * Removed Nike specific tags and made tags dynamic and generic * clean up cf templates * ignore mapper test for now, will be fixed in next PR with env yaml v2 * Update CloudFormationService.java --- .../cli/EnvironmentConfigToArgsMapper.java | 12 +-- .../nike/cerberus/command/StackDelegate.java | 29 ++----- .../command/core/CreateBaseCommand.java | 16 +--- .../command/core/CreateRoute53Command.java | 9 +++ .../domain/cloudformation/BaseParameters.java | 16 +--- .../domain/cloudformation/CmsParameters.java | 1 - .../cloudformation/DatabaseParameters.java | 16 +--- .../DeprecatedBaseParameters.java | 17 +--- .../cloudformation/GatewayParameters.java | 1 - .../LaunchConfigParameters.java | 2 +- .../LoadBalancerParameters.java | 3 +- .../SecurityGroupParameters.java | 15 +--- .../domain/cloudformation/TagParameters.java | 25 ------ .../cloudformation/TagParametersDelegate.java | 58 ++++---------- .../domain/cloudformation/VpcParameters.java | 17 +--- .../domain/cloudformation/WafParameters.java | 16 ++-- .../cms/CreateCmsClusterOperation.java | 14 +--- .../operation/core/CreateBaseOperation.java | 7 +- .../core/CreateDatabaseOperation.java | 12 +-- .../core/CreateLoadBalancerOperation.java | 8 +- .../core/CreateRoute53Operation.java | 8 +- .../core/CreateSecurityGroupsOperation.java | 7 +- .../operation/core/CreateVpcOperation.java | 8 +- .../operation/core/CreateWafOperation.java | 14 ++-- .../operation/core/UpdateStackOperation.java | 42 ++++------ .../service/CloudFormationService.java | 39 ++++++++-- .../cerberus/service/Ec2UserDataService.java | 14 ++-- src/main/resources/cloudformation/base.yaml | 23 ------ .../resources/cloudformation/cms-cluster.yaml | 26 ------- .../resources/cloudformation/database.yaml | 23 ------ .../cloudformation/load-balancer.yaml | 42 ---------- .../cloudformation/security-groups.yaml | 46 ----------- src/main/resources/cloudformation/vpc.yaml | 77 ------------------- .../cloudformation/web-app-firewall.yaml | 17 +--- .../EnvironmentConfigToArgsMapperTest.java | 20 ++--- 35 files changed, 140 insertions(+), 560 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/TagParameters.java diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index f040c7ec..c542359e 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -167,12 +167,12 @@ private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List } private static void addTagArgs(EnvironmentConfig environmentConfig, List args) { - args.add(TagParametersDelegate.COST_CENTER_LONG_ARG); - args.add(environmentConfig.getCostCenter()); - args.add(TagParametersDelegate.OWNER_EMAIL_LONG_ARG); - args.add(environmentConfig.getOwnerEmail()); - args.add(TagParametersDelegate.OWNER_GROUP_LONG_ARG); - args.add(environmentConfig.getOwnerGroup()); +// args.add(TagParametersDelegate.COST_CENTER_LONG_ARG); +// args.add(environmentConfig.getCostCenter()); +// args.add(TagParametersDelegate.OWNER_EMAIL_LONG_ARG); +// args.add(environmentConfig.getOwnerEmail()); +// args.add(TagParametersDelegate.OWNER_GROUP_LONG_ARG); +// args.add(environmentConfig.getOwnerGroup()); } private static List getUploadCertFilesCommandArgs(EnvironmentConfig environmentConfig, String[] passedArgs) { diff --git a/src/main/java/com/nike/cerberus/command/StackDelegate.java b/src/main/java/com/nike/cerberus/command/StackDelegate.java index b8274762..b3d5537e 100644 --- a/src/main/java/com/nike/cerberus/command/StackDelegate.java +++ b/src/main/java/com/nike/cerberus/command/StackDelegate.java @@ -18,6 +18,7 @@ import com.beust.jcommander.DynamicParameter; import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import java.util.HashMap; @@ -42,20 +43,8 @@ public class StackDelegate { @Parameter(names = KEY_PAIR_NAME_LONG_ARG, required = true, description = "SSH key pair name.") private String keyPairName; - @Parameter(names = TagParametersDelegate.OWNER_GROUP_LONG_ARG, - description = "The owning group for the provision resources. Will be tagged on all resources.", - required = true) - private String ownerGroup; - - @Parameter(names = TagParametersDelegate.OWNER_EMAIL_LONG_ARG, - description = "The e-mail for who owns the provisioned resources. Will be tagged on all resources.", - required = true) - private String ownerEmail; - - @Parameter(names = TagParametersDelegate.COST_CENTER_LONG_ARG, - description = "Costcenter for where to bill provisioned resources. Will be tagged on all resources.", - required = true) - private String costcenter; + @ParametersDelegate + private TagParametersDelegate tagParameters = new TagParametersDelegate(); @DynamicParameter(names = PARAMETER_SHORT_ARG, description = "Dynamic parameters for overriding the values for specific parameters in the CloudFormation.") private Map dynamicParameters = new HashMap<>(); @@ -72,16 +61,8 @@ public String getKeyPairName() { return keyPairName; } - public String getOwnerGroup() { - return ownerGroup; - } - - public String getOwnerEmail() { - return ownerEmail; - } - - public String getCostcenter() { - return costcenter; + public TagParametersDelegate getTagParameters() { + return tagParameters; } public Map getDynamicParameters() { diff --git a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java index 7bf1eb14..a39c8846 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java @@ -19,26 +19,12 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseParameters; -import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; -import com.nike.cerberus.domain.cloudformation.TagParameters; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; -import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.CreateBaseOperation; -import com.nike.cerberus.service.CloudFormationService; - -import javax.inject.Inject; -import javax.inject.Named; -import java.util.Map; import static com.nike.cerberus.command.core.CreateBaseCommand.COMMAND_NAME; -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** * Command for creating the base components for Cerberus. @@ -63,7 +49,7 @@ public String getAdminRoleArn() { return adminRoleArn; } - public TagParametersDelegate getTagParameters() { + public TagParametersDelegate getTagsDelegate() { return tagParameters; } diff --git a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java index 1a3000d1..cc75f0ad 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java @@ -18,7 +18,9 @@ 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.TagParametersDelegate; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.CreateRoute53Operation; @@ -37,6 +39,13 @@ public class CreateRoute53Command implements Command { public static final String HOSTED_ZONE_ID = "--hosted-zone-id"; + @ParametersDelegate + private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); + + public TagParametersDelegate getTagsDelegate() { + return tagsDelegate; + } + @Parameter(names = HOSTNAME_LONG_ARG, description = "The hostname of the Route53 record to be created for Cerberus (e.g. .cerberus.example.com)", required = true) diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java index 5101b012..e85b3ec1 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java @@ -16,18 +16,13 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - /** * Represents the base stack inputs. */ -public class BaseParameters implements TagParameters { +public class BaseParameters { private String accountAdminArn; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getAccountAdminArn() { return accountAdminArn; } @@ -37,13 +32,4 @@ public BaseParameters setAccountAdminArn(String accountAdminArn) { return this; } - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public BaseParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java index 21f1d42b..12a28e0b 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java @@ -104,7 +104,6 @@ public CmsParameters setLaunchConfigParameters(LaunchConfigParametersDelegate la return this; } - @Override public TagParametersDelegate getTagParameters() { return tagParameters; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java index 9e957006..731240a1 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java @@ -16,12 +16,10 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - /** * Represents the database stack inputs. */ -public class DatabaseParameters implements TagParameters { +public class DatabaseParameters { private String cmsDbInstanceAz1; @@ -45,9 +43,6 @@ public class DatabaseParameters implements TagParameters { private String vpcSubnetIdForAz3; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getCmsDbInstanceAz1() { return cmsDbInstanceAz1; } @@ -147,13 +142,4 @@ public DatabaseParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { return this; } - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public DatabaseParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java index 07356ceb..e92099f1 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java @@ -16,8 +16,6 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - /** * Represents the base stack inputs. * @@ -25,7 +23,7 @@ * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments */ @Deprecated -public class DeprecatedBaseParameters implements TagParameters { +public class DeprecatedBaseParameters { private String accountAdminArn; @@ -35,9 +33,6 @@ public class DeprecatedBaseParameters implements TagParameters { private String az3; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getAz1() { return az1; } @@ -73,14 +68,4 @@ public DeprecatedBaseParameters setAccountAdminArn(String accountAdminArn) { this.accountAdminArn = accountAdminArn; return this; } - - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public DeprecatedBaseParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java index 78ec6d0c..45ecc87e 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/GatewayParameters.java @@ -167,7 +167,6 @@ public GatewayParameters setLaunchConfigParameters(LaunchConfigParametersDelegat return this; } - @Override public TagParametersDelegate getTagParameters() { return tagParameters; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java index 80a22352..a8b0b25f 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LaunchConfigParameters.java @@ -19,7 +19,7 @@ /** * Interface implemented by stack parameter POJOs that have launch configuration parameters. */ -public interface LaunchConfigParameters extends TagParameters { +public interface LaunchConfigParameters { LaunchConfigParametersDelegate getLaunchConfigParameters(); } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java index 775e311e..6cac1ae7 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java @@ -21,7 +21,7 @@ /** * Represents the load balancer stack inputs. */ -public class LoadBalancerParameters implements TagParameters { +public class LoadBalancerParameters { private String sgStackName; @@ -92,7 +92,6 @@ public LoadBalancerParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { return this; } - @Override public TagParametersDelegate getTagParameters() { return tagParameters; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java index 502ebe09..e14ffa45 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java @@ -16,12 +16,10 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - /** * Represents the security group stack inputs. */ -public class SecurityGroupParameters implements TagParameters { +public class SecurityGroupParameters { private Integer cmsDbPort; @@ -29,9 +27,6 @@ public class SecurityGroupParameters implements TagParameters { private String vpcId; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public Integer getCmsDbPort() { return cmsDbPort; } @@ -59,12 +54,4 @@ public SecurityGroupParameters setVpcId(String vpcId) { return this; } - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public SecurityGroupParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParameters.java deleted file mode 100644 index ac28a3f2..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParameters.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016 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; - -/** - * Interface implemented by stack parameter POJOs that have tag parameters. - */ -public interface TagParameters { - - TagParametersDelegate getTagParameters(); -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java index ebb51c43..545cdf70 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/TagParametersDelegate.java @@ -16,55 +16,29 @@ package com.nike.cerberus.domain.cloudformation; -import com.beust.jcommander.Parameter; +import com.beust.jcommander.DynamicParameter; + +import java.util.HashMap; +import java.util.Map; /** * CloudFormation input parameters common to all Cerberus CloudFormation stacks. */ public class TagParametersDelegate { - public static final String OWNER_EMAIL_LONG_ARG = "--owner-email"; - public static final String OWNER_GROUP_LONG_ARG = "--owner-group"; - public static final String COST_CENTER_LONG_ARG = "--costcenter"; - - @Parameter(names = "--tag-name", - description = "The environment name (e.g. 'cerberus-demo', 'cerberus-preprod')") - private String tagName; - - @Parameter(names = OWNER_EMAIL_LONG_ARG, - description = "The e-mail for who owns the provisioned resources. Will be tagged on all resources.", - required = true) - private String tagEmail; - - @Parameter(names = COST_CENTER_LONG_ARG, - description = "Costcenter for where to bill provisioned resources. Will be tagged on all resources.", - required = true) - private String tagCostcenter; + public static final String TAG_LONG_ARG = "--TAG"; + public static final String TAG_SHORT_ARG = "-T"; - public String getTagName() { - return tagName; - } - - public TagParametersDelegate setTagName(String tagName) { - this.tagName = tagName; - return this; - } - - public String getTagEmail() { - return tagEmail; - } - - public TagParametersDelegate setTagEmail(String tagEmail) { - this.tagEmail = tagEmail; - return this; - } - - public String getTagCostcenter() { - return tagCostcenter; - } + @DynamicParameter( + names = { + TAG_LONG_ARG, + TAG_SHORT_ARG + }, + description = "Dynamic parameters for setting tags to be used on generated aws resources." + ) + private Map tags = new HashMap<>(); - public TagParametersDelegate setTagCostcenter(String tagCostcenter) { - this.tagCostcenter = tagCostcenter; - return this; + public Map getTags() { + return tags; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java index d9ac17ee..22ab9055 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java @@ -16,12 +16,10 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - /** * Represents the vpc stack inputs. */ -public class VpcParameters implements TagParameters { +public class VpcParameters { private String az1; @@ -39,9 +37,6 @@ public class VpcParameters implements TagParameters { private String vpcCidrBlock; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getAz1() { return az1; } @@ -113,14 +108,4 @@ public VpcParameters setVpcCidrBlock(String vpcCidrBlock) { this.vpcCidrBlock = vpcCidrBlock; return this; } - - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public VpcParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java index e5d26317..5e0dff50 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/WafParameters.java @@ -16,17 +16,14 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - /** * Represents the waf stack inputs. */ -public class WafParameters implements TagParameters { +public class WafParameters { private String loadBalancerStackName; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); + private String wafName; public String getLoadBalancerStackName() { return loadBalancerStackName; @@ -37,13 +34,12 @@ public WafParameters setLoadBalancerStackName(String loadBalancerStackName) { return this; } - @Override - public TagParametersDelegate getTagParameters() { - return tagParameters; + public String getWafName() { + return wafName; } - public WafParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; + public WafParameters setWafName(String wafName) { + this.wafName = wafName; return this; } } diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index da255bb9..7bb3383f 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -23,17 +23,15 @@ import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; +import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; -import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.util.UuidSupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,12 +103,7 @@ public void run(final CreateCmsClusterCommand command) { cmsParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); cmsParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); cmsParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - cmsParameters.getLaunchConfigParameters().setUserData( - ec2UserDataService.getUserData(StackName.CMS, command.getStackDelegate().getOwnerGroup())); - - cmsParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - cmsParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - cmsParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); + cmsParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(StackName.CMS)); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters, typeReference); @@ -119,7 +112,8 @@ public void run(final CreateCmsClusterCommand command) { parameters.putAll(command.getStackDelegate().getDynamicParameters()); final String stackId = cloudFormationService.createStack(StackName.CMS.getFullName(environmentName), - parameters, ConfigConstants.CMS_STACK_TEMPLATE_PATH, true); + parameters, ConfigConstants.CMS_STACK_TEMPLATE_PATH, true, + command.getStackDelegate().getTagParameters().getTags()); final StackStatus endStatus = cloudFormationService.waitForStatus(stackId, diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index f0c21ca6..bf495574 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -67,16 +67,13 @@ public void run(final CreateBaseCommand command) { final BaseParameters baseParameters = new BaseParameters() .setAccountAdminArn(command.getAdminRoleArn()); - baseParameters.getTagParameters().setTagEmail(command.getTagParameters().getTagEmail()); - baseParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - baseParameters.getTagParameters().setTagCostcenter(command.getTagParameters().getTagCostcenter()); - final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); cloudFormationService.createStack(StackName.BASE.getFullName(environmentName), - parameters, ConfigConstants.BASE_STACK_TEMPLATE_PATH, true); + parameters, ConfigConstants.BASE_STACK_TEMPLATE_PATH, true, + command.getTagsDelegate().getTags()); configStore.initEnvironmentData(); configStore.initSecretsData(); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index a5b98e67..d8f78051 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -18,7 +18,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Maps; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; @@ -28,7 +27,6 @@ import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.Ec2Service; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.RandomStringGenerator; import org.slf4j.Logger; @@ -36,10 +34,8 @@ import javax.inject.Inject; import javax.inject.Named; -import java.util.List; import java.util.Map; -import static com.nike.cerberus.ConfigConstants.MINIMUM_AZS; import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** @@ -86,15 +82,13 @@ public void run(final CreateDatabaseCommand command) { .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); - databaseParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); - databaseParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - databaseParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); - final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); cloudFormationService.createStack(StackName.DATABASE.getFullName(environmentName), - parameters, ConfigConstants.DATABASE_STACK_TEMPLATE_PATH, true); } + parameters, ConfigConstants.DATABASE_STACK_TEMPLATE_PATH, true, + command.getTagsDelegate().getTags()); + } @Override public boolean isRunnable(final CreateDatabaseCommand command) { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index a6c540b2..6a6bcc1a 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -78,15 +78,13 @@ public void run(final CreateLoadBalancerCommand command) { .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); - loadBalancerParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); - loadBalancerParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - loadBalancerParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); - final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); cloudFormationService.createStack(StackName.LOAD_BALANCER.getFullName(environmentName), - parameters, ConfigConstants.LOAD_BALANCER_STACK_TEMPLATE_PATH, true); } + parameters, ConfigConstants.LOAD_BALANCER_STACK_TEMPLATE_PATH, true, + command.getTagsDelegate().getTags()); + } @Override public boolean isRunnable(final CreateLoadBalancerCommand command) { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index e7df1982..8d26ef1e 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -22,7 +22,6 @@ import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; -import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; @@ -47,18 +46,14 @@ public class CreateRoute53Operation implements Operation { private final CloudFormationService cloudFormationService; - private final ConfigStore configStore; - private final ObjectMapper cloudFormationObjectMapper; @Inject public CreateRoute53Operation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, - final ConfigStore configStore, @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; - this.configStore = configStore; this.cloudFormationObjectMapper = cloudformationObjectMapper; } @@ -75,7 +70,8 @@ public void run(final CreateRoute53Command command) { final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); cloudFormationService.createStack(StackName.ROUTE53.getFullName(environmentName), - parameters, ConfigConstants.ROUTE53_TEMPLATE_PATH, true); + parameters, ConfigConstants.ROUTE53_TEMPLATE_PATH, true, + command.getTagsDelegate().getTags()); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 84ac42f2..2fe8395e 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -71,15 +71,12 @@ public void run(final CreateSecurityGroupsCommand command) { .setVpcId(vpcOutputs.getVpcId()) .setLoadBalancerCidrBlock(command.getLoadBalancerCidr()); - securityGroupParameters.getTagParameters().setTagEmail(command.getTagParameters().getTagEmail()); - securityGroupParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - securityGroupParameters.getTagParameters().setTagCostcenter(command.getTagParameters().getTagCostcenter()); - final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); cloudFormationService.createStack(StackName.SECURITY_GROUPS.getFullName(environmentName), - parameters, ConfigConstants.SECURITY_GROUPS_STACK_TEMPLATE_PATH, true); + parameters, ConfigConstants.SECURITY_GROUPS_STACK_TEMPLATE_PATH, true, + command.getTagParameters().getTags()); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index 2b4560f1..832d9e22 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -74,15 +74,13 @@ public void run(final CreateVpcCommand command) { .setAz2(azByIdentifier.get(2)) .setAz3(azByIdentifier.get(3)); - vpcParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); - vpcParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - vpcParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); - final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); cloudFormationService.createStack(StackName.VPC.getFullName(environmentName), - parameters, ConfigConstants.VPC_STACK_TEMPLATE_PATH, true); } + parameters, ConfigConstants.VPC_STACK_TEMPLATE_PATH, true, + command.getTagsDelegate().getTags()); + } @Override public boolean isRunnable(final CreateVpcCommand command) { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index a60a1a87..fbd87f9b 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -19,11 +19,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; -import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.cloudformation.WafParameters; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; @@ -69,17 +66,16 @@ public void run(final CreateWafCommand command) { final String environmentName = environmentMetadata.getName(); final WafParameters wafParameters = new WafParameters() - .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)); - - wafParameters.getTagParameters().setTagEmail(command.getTagsDelegate().getTagEmail()); - wafParameters.getTagParameters().setTagName(ConfigConstants.ENV_PREFIX + environmentName); - wafParameters.getTagParameters().setTagCostcenter(command.getTagsDelegate().getTagCostcenter()); + .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)) + .setWafName("cerberus-" + environmentName + "-waf"); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); cloudFormationService.createStack(StackName.WAF.getFullName(environmentName), - parameters, ConfigConstants.WAF_STACK_TEMPLATE_PATH, true); } + parameters, ConfigConstants.WAF_STACK_TEMPLATE_PATH, true, + command.getTagsDelegate().getTags()); + } @Override public boolean isRunnable(final CreateWafCommand command) { diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 000886ae..9a3e914f 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -21,21 +21,19 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.LaunchConfigParameters; -import com.nike.cerberus.domain.cloudformation.SslConfigParametersDelegate; -import com.nike.cerberus.domain.cloudformation.TagParameters; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; +import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; -import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -46,8 +44,11 @@ import java.util.HashMap; import java.util.Map; -import static com.amazonaws.services.cloudformation.model.StackStatus.*; -import static com.nike.cerberus.ConfigConstants.CERT_PART_PUBKEY; +import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_COMPLETE; +import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_COMPLETE_CLEANUP_IN_PROGRESS; +import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_COMPLETE; +import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS; +import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_FAILED; import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** @@ -118,9 +119,11 @@ public void run(final UpdateStackCommand command) { logger.info("Starting the update for {}.", command.getStackName().getName()); if (command.isOverwriteTemplate()) { - cloudFormationService.updateStack(stackId, parameters, stackTemplatePathMap.get(command.getStackName()), true); + cloudFormationService.updateStack(stackId, parameters, stackTemplatePathMap.get(command.getStackName()), + true, command.getStackDelegate().getTagParameters().getTags()); } else { - cloudFormationService.updateStack(stackId, parameters, true); + cloudFormationService.updateStack(stackId, parameters, true, + command.getStackDelegate().getTagParameters().getTags()); } final StackStatus endStatus = @@ -173,8 +176,7 @@ private Map getUpdateLaunchConfigParameters(final StackName stac final LaunchConfigParameters launchConfigParameters = configStore.getStackParameters(stackName, parametersClass); - launchConfigParameters.getLaunchConfigParameters().setUserData( - ec2UserDataService.getUserData(stackName, command.getStackDelegate().getOwnerGroup())); + launchConfigParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(stackName)); if (StringUtils.isNotBlank(command.getStackDelegate().getAmiId())) { launchConfigParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); @@ -188,30 +190,12 @@ private Map getUpdateLaunchConfigParameters(final StackName stac launchConfigParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); } - if (StringUtils.isNotBlank(command.getStackDelegate().getOwnerEmail())) { - launchConfigParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - } - - if (StringUtils.isNotBlank(command.getStackDelegate().getCostcenter())) { - launchConfigParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); - } final TypeReference> typeReference = new TypeReference>() {}; return cloudformationObjectMapper.convertValue(launchConfigParameters, typeReference); } private Map getUpdatedBaseStackParameters(final UpdateStackCommand command) { - final TagParameters tagParameters = configStore.getStackParameters(command.getStackName(), SecurityGroupParameters.class); - - if (StringUtils.isNotBlank(command.getStackDelegate().getOwnerEmail())) { - tagParameters.getTagParameters().setTagEmail(command.getStackDelegate().getOwnerEmail()); - } - - if (StringUtils.isNotBlank(command.getStackDelegate().getCostcenter())) { - tagParameters.getTagParameters().setTagCostcenter(command.getStackDelegate().getCostcenter()); - } - - final TypeReference> typeReference = new TypeReference>() {}; - return cloudformationObjectMapper.convertValue(tagParameters, typeReference); + return Maps.newHashMap(); } } \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index 5d0a3d22..e7ed8d56 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -30,6 +30,7 @@ import com.amazonaws.services.cloudformation.model.Stack; import com.amazonaws.services.cloudformation.model.StackEvent; import com.amazonaws.services.cloudformation.model.StackStatus; +import com.amazonaws.services.cloudformation.model.Tag; import com.amazonaws.services.cloudformation.model.UpdateStackRequest; import com.beust.jcommander.internal.Maps; import com.github.tomaslanger.chalk.Chalk; @@ -92,13 +93,15 @@ public CloudFormationService(final AmazonCloudFormation cloudFormationClient, public String createStack(final String name, final Map parameters, final String templatePath, - final boolean iamCapabilities) { + final boolean iamCapabilities, + final Map globalTags) { logger.info(String.format("Executing the Cloud Formation: %s, Stack Name: %s", templatePath, name)); final CreateStackRequest request = new CreateStackRequest() .withStackName(name) .withParameters(convertParameters(parameters)) - .withTemplateBody(getTemplateText(templatePath)); + .withTemplateBody(getTemplateText(templatePath)) + .withTags(getTags(globalTags)); if (iamCapabilities) { request.getCapabilities().add("CAPABILITY_IAM"); @@ -117,8 +120,10 @@ public String createStack(final String name, */ public void updateStack(final String stackId, final Map parameters, - final boolean iamCapabilities) { - updateStack(stackId, parameters, null, iamCapabilities); + final boolean iamCapabilities, + final Map globalTags) { + + updateStack(stackId, parameters, null, iamCapabilities, globalTags); } /** @@ -131,7 +136,9 @@ public void updateStack(final String stackId, public void updateStack(final String stackId, final Map parameters, final String templatePath, - final boolean iamCapabilities) { + final boolean iamCapabilities, + final Map globalTags) { + final UpdateStackRequest request = new UpdateStackRequest() .withStackName(stackId) .withParameters(convertParameters(parameters)); @@ -146,6 +153,8 @@ public void updateStack(final String stackId, request.getCapabilities().add("CAPABILITY_IAM"); } + request.setTags(getTags(globalTags)); + cloudFormationClient.updateStack(request); } @@ -409,6 +418,26 @@ public StackStatus waitForStatus(final String stackId, final HashSet getTags(Map globalTags) { + Set tags = new HashSet<>(); + globalTags.forEach((k,v) -> { + tags.add(new Tag().withKey(k).withValue(v)); + }); + + tags.add(new Tag() + .withKey("Name") + .withValue(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()) + ); + + return tags; + } + private String getStatusColor(String status) { if (status.endsWith("PROGRESS")) { return Chalk.on(status).yellow().toString(); diff --git a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java index 0bf0d5ed..6e53420e 100644 --- a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java +++ b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java @@ -21,10 +21,8 @@ import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.io.IOUtils; import javax.inject.Inject; -import java.io.IOException; import java.nio.charset.Charset; import java.util.Base64; import java.util.Map; @@ -44,30 +42,28 @@ public Ec2UserDataService(final EnvironmentMetadata environmentMetadata, this.configStore = configStore; } - public String getUserData(final StackName stackName, final String ownerGroup) { + public String getUserData(final StackName stackName) { switch (stackName) { case CMS: - return getCmsUserData(ownerGroup); + return getCmsUserData(); default: throw new IllegalArgumentException("The stack specified does not support user data. stack: " + stackName.getName()); } } - private String getCmsUserData(final String ownerGroup) { + private String getCmsUserData() { final Map userDataMap = Maps.newHashMap(); - addStandardEnvironmentVariables(userDataMap, StackName.CMS.getName(), ownerGroup); + addStandardEnvironmentVariables(userDataMap, StackName.CMS.getName()); return encodeUserData(writeExportEnvVars(userDataMap)); } private void addStandardEnvironmentVariables(final Map userDataMap, - final String appName, - final String ownerGroup) { + final String appName) { userDataMap.put("CLOUD_ENVIRONMENT", ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); userDataMap.put("CLOUD_MONITOR_BUCKET", appName); userDataMap.put("CLOUD_APP", appName); - userDataMap.put("CLOUD_APP_GROUP", ownerGroup); userDataMap.put("CLOUD_CLUSTER", appName); userDataMap.put("CLASSIFICATION", "Gold"); userDataMap.put("EC2_REGION", environmentMetadata.getRegionName()); diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/base.yaml index 8af10c40..4a984610 100644 --- a/src/main/resources/cloudformation/base.yaml +++ b/src/main/resources/cloudformation/base.yaml @@ -20,33 +20,10 @@ Parameters: accountAdminArn: Description: The ARN for a IAM user, group or role that can create this stack. Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped - under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project. - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Environment name, e.g. "cerberus-{environment}"' - Type: String Resources: CerberusConfigBucket: Properties: AccessControl: Private - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' - - Key: email - Value: !Ref 'tagEmail' VersioningConfiguration: Status: Enabled Type: AWS::S3::Bucket diff --git a/src/main/resources/cloudformation/cms-cluster.yaml b/src/main/resources/cloudformation/cms-cluster.yaml index 2ef607ab..a8ae817a 100644 --- a/src/main/resources/cloudformation/cms-cluster.yaml +++ b/src/main/resources/cloudformation/cms-cluster.yaml @@ -43,19 +43,6 @@ Parameters: sgStackName: Description: The name of the Cerberus Security Groups CloudFormation stack Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project. - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Name assigned to the stack. Format: "cerberus-{environment}"' - Type: String userData: Description: CMS user data Type: String @@ -84,19 +71,6 @@ Resources: - Fn::ImportValue: !Sub "${loadBalancerStackName}-cmsTargetGroupArn" MaxSize: !Ref 'maximumInstances' MinSize: !Ref 'minimumInstances' - Tags: - - Key: Name - PropagateAtLaunch: true - Value: !Ref 'tagName' - - Key: email - PropagateAtLaunch: true - Value: !Ref 'tagEmail' - - Key: classification - PropagateAtLaunch: true - Value: !Ref 'tagClassification' - - Key: costcenter - PropagateAtLaunch: true - Value: !Ref 'tagCostcenter' VPCZoneIdentifier: - Ref: 'vpcSubnetIdForAz1' - Ref: 'vpcSubnetIdForAz2' diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml index f0d7a564..f5e533b3 100644 --- a/src/main/resources/cloudformation/database.yaml +++ b/src/main/resources/cloudformation/database.yaml @@ -48,20 +48,6 @@ Parameters: sgStackName: Description: The name of the Cerberus Security Groups CloudFormation stack Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped - under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project.aaa - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Environment name, e.g. "cerberus-{environment}"' - Type: String vpcSubnetIdForAz1: Description: The subnet for the first availability zone Type: String @@ -89,15 +75,6 @@ Resources: PreferredBackupWindow: 13:14-13:44 PreferredMaintenanceWindow: tue:06:48-tue:07:18 StorageEncrypted: 'true' - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcSecurityGroupIds: - Fn::ImportValue: !Sub "${sgStackName}-cmsDbSgId" Type: AWS::RDS::DBCluster diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 0f9ba6f3..7841dc77 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -24,20 +24,6 @@ Parameters: sslCertificateArn: Description: TLS certificate ARN for CMS Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped - under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project. - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Environment name, e.g. "cerberus-{environment}"' - Type: String vpcId: Description: The VPC in which the EC2 instances will be run Type: String @@ -66,16 +52,6 @@ Resources: ALBLogBucket: Properties: AccessControl: BucketOwnerFullControl - - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' - - Key: email - Value: !Ref 'tagEmail' Type: AWS::S3::Bucket ALBLogBucketPolicy: Properties: @@ -107,15 +83,6 @@ Resources: - Ref: 'vpcSubnetIdForAz1' - Ref: 'vpcSubnetIdForAz2' - Ref: 'vpcSubnetIdForAz3' - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' Type: AWS::ElasticLoadBalancingV2::LoadBalancer CmsTargetGroup: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" @@ -128,15 +95,6 @@ Resources: HealthyThresholdCount: 2 Port: 8443 Protocol: HTTPS - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 10 diff --git a/src/main/resources/cloudformation/security-groups.yaml b/src/main/resources/cloudformation/security-groups.yaml index b6543bae..59bde13d 100644 --- a/src/main/resources/cloudformation/security-groups.yaml +++ b/src/main/resources/cloudformation/security-groups.yaml @@ -33,20 +33,6 @@ Parameters: MaxLength: '20' MinLength: '9' Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped - under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project. - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Environment name, e.g. "cerberus-{environment}"' - Type: String vpcId: Description: ID of the VPC to associate with the security groups Type: String @@ -57,12 +43,6 @@ Resources: Tags: - Key: AutoUpdate Value: 'true' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: Ref: 'vpcId' Type: AWS::EC2::SecurityGroup @@ -86,14 +66,6 @@ Resources: Properties: GroupDescription: Management Server Database Security Group Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: Ref: 'vpcId' Type: AWS::EC2::SecurityGroup @@ -116,30 +88,12 @@ Resources: CmsSg: Properties: GroupDescription: Management Server Instance Security Group - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: Ref: 'vpcId' Type: AWS::EC2::SecurityGroup WhitelistIngressSg: Properties: GroupDescription: Administration ingress from tools NAT boxes - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: Ref: 'vpcId' Type: AWS::EC2::SecurityGroup \ No newline at end of file diff --git a/src/main/resources/cloudformation/vpc.yaml b/src/main/resources/cloudformation/vpc.yaml index 2a22608c..50380ce2 100644 --- a/src/main/resources/cloudformation/vpc.yaml +++ b/src/main/resources/cloudformation/vpc.yaml @@ -65,20 +65,6 @@ Parameters: MaxLength: '20' MinLength: '9' Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped - under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project. - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Environment name, e.g. "cerberus-{environment}"' - Type: String vpcCidrBlock: AllowedPattern: '[0-9./]*' Default: 172.20.0.0/20 @@ -93,27 +79,9 @@ Resources: compute.internal]]] DomainNameServers: - AmazonProvidedDNS - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' Type: AWS::EC2::DHCPOptions CerberusInternetGateway: Properties: - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' Type: AWS::EC2::InternetGateway CerberusRouteInternetGateway: Properties: @@ -123,15 +91,6 @@ Resources: Type: AWS::EC2::Route CerberusRouteTable: Properties: - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: !Ref 'CerberusVpc' Type: AWS::EC2::RouteTable CerberusSubnetForAz1: @@ -139,15 +98,6 @@ Resources: AvailabilityZone: !Ref 'az1' CidrBlock: !Ref 'subnetCidrBlockForAz1' MapPublicIpOnLaunch: 'true' - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: !Ref 'CerberusVpc' Type: AWS::EC2::Subnet CerberusSubnetForAz2: @@ -155,15 +105,6 @@ Resources: AvailabilityZone: !Ref 'az2' CidrBlock: !Ref 'subnetCidrBlockForAz2' MapPublicIpOnLaunch: 'true' - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: !Ref 'CerberusVpc' Type: AWS::EC2::Subnet CerberusSubnetForAz3: @@ -171,15 +112,6 @@ Resources: AvailabilityZone: !Ref 'az3' CidrBlock: !Ref 'subnetCidrBlockForAz3' MapPublicIpOnLaunch: 'true' - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' VpcId: !Ref 'CerberusVpc' Type: AWS::EC2::Subnet CerberusSubnetRouteTableAssociationForAz1: @@ -202,15 +134,6 @@ Resources: CidrBlock: !Ref 'vpcCidrBlock' EnableDnsHostnames: 'true' EnableDnsSupport: 'true' - Tags: - - Key: Name - Value: !Ref 'tagName' - - Key: email - Value: !Ref 'tagEmail' - - Key: classification - Value: !Ref 'tagClassification' - - Key: costcenter - Value: !Ref 'tagCostcenter' Type: AWS::EC2::VPC CerberusVpcDhcpOptionsAssociation: Properties: diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml index 1877ae0d..d9f8b8dd 100644 --- a/src/main/resources/cloudformation/web-app-firewall.yaml +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -13,19 +13,8 @@ Parameters: loadBalancerStackName: Description: The name of the Cerberus load balancer CloudFormation stack Type: String - tagClassification: - Default: Gold - Description: Denotes which category of Data Classification the instance is grouped - under. - Type: String - tagCostcenter: - Description: Represents the Cost Center associated with the team/project. - Type: String - tagEmail: - Description: E-mail address for group or person responsible for the stack. - Type: String - tagName: - Description: 'Environment name, e.g. "cerberus-{environment}"' + wafName: + Description: The name for the Web Application Firewall that will be created Type: String Resources: CerberusWAFWebAcl: @@ -36,7 +25,7 @@ Resources: DefaultAction: Type: ALLOW MetricName: CerberusWAF - Name: !Join [-, [waf, !Ref 'tagName']] + Name: !Ref 'wafName' Rules: - Action: Type: BLOCK diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index 38bb80eb..d32eab23 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -30,6 +30,7 @@ import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.io.InputStream; @@ -37,6 +38,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +@Ignore("This is completely redone in next PR") public class EnvironmentConfigToArgsMapperTest { private EnvironmentConfig environmentConfig; @@ -152,9 +154,9 @@ public void test_create_management_service_cluster() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", - TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name" +// TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", +// TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", +// TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -220,9 +222,9 @@ public void test_update_stack_with_no_overwrite_flag_or_dyn_props() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", - TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", +// TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", +// TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", +// TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -243,9 +245,9 @@ public void test_update_stack_with_overwrite_flag_and_dyn_props() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", - TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", - TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", - TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", +// TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", +// TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", +// TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", UpdateStackCommand.OVERWRITE_TEMPLATE_LONG_ARG, UpdateStackCommand.PARAMETER_SHORT_ARG, "k=v", }; From f132e100bb4e3c5de0d029538ee515400ab32237 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Fri, 10 Nov 2017 09:09:30 -0800 Subject: [PATCH 17/69] Adding tags to the CMS CMKs (#80) * Adding tags to the CMS CMKs --- .../operation/cms/CreateCmsCmkOperation.java | 9 ++++++- .../com/nike/cerberus/service/KmsService.java | 24 ++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java index 23b24ab7..8c10b6f5 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java @@ -19,6 +19,7 @@ import com.amazonaws.regions.Regions; import com.beust.jcommander.ParameterException; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.domain.EnvironmentMetadata; @@ -35,6 +36,7 @@ import javax.inject.Inject; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Properties; import static com.nike.cerberus.ConfigConstants.CMK_ARNS_KEY; @@ -128,8 +130,13 @@ private List createCmks(CreateCmsCmkCommand command, Properties cmsConfi String policyAsJson = generateKeyPolicy(cmsConfigProperties, description); logger.info("Generated the following policy:\n" + policyAsJson); + Map tags = Maps.newHashMap(); + tags.put("created_by", "cerberus_cli"); + tags.put("created_for", "cerberus_cms"); + tags.put("cerberus_env", envName); + List cmkRegions = getTargetRegions(command); - return kmsService.createKeysAndAliases(cmkRegions, alias, policyAsJson, description); + return kmsService.createKeysAndAliases(cmkRegions, alias, policyAsJson, description, tags); } private String generateAliasName(String envName, String primaryRegion) { diff --git a/src/main/java/com/nike/cerberus/service/KmsService.java b/src/main/java/com/nike/cerberus/service/KmsService.java index d89b3002..6b939b31 100644 --- a/src/main/java/com/nike/cerberus/service/KmsService.java +++ b/src/main/java/com/nike/cerberus/service/KmsService.java @@ -23,13 +23,17 @@ import com.amazonaws.services.kms.model.CreateKeyResult; import com.amazonaws.services.kms.model.EnableKeyRotationRequest; import com.amazonaws.services.kms.model.KeyMetadata; +import com.amazonaws.services.kms.model.Tag; import com.beust.jcommander.internal.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Convenience methods for calls to KMS @@ -46,10 +50,19 @@ public KmsService(KmsClientFactory kmsClientFactory) { this.kmsClientFactory = kmsClientFactory; } - public List createKeysAndAliases(List regions, String alias, String policy, String description) { + /** + * Create keys with a shared alias in multiple regions + * @param regions the target regions to create CMKs in + * @param alias the alias for the keys + * @param policy the policy + * @param description the key description + * @param tags tags to apply to the keys + * @return the ARNs of the created CMKs + */ + public List createKeysAndAliases(List regions, String alias, String policy, String description, Map tags) { List keyArns = Lists.newArrayList(); for (Regions region : regions) { - String keyArn = createKey(region, policy, description, alias); + String keyArn = createKey(region, policy, description, alias, tags); keyArns.add(keyArn); } return keyArns; @@ -64,13 +77,18 @@ public List createKeysAndAliases(List regions, String alias, St * @param alias the CMK alias to generate for this key * @return cmkArn */ - private String createKey(Regions region, String policy, String description, String alias) { + private String createKey(Regions region, String policy, String description, String alias, Map tags) { + + Collection kmsTags = tags.entrySet().stream() + .map(entry -> new Tag().withTagKey(entry.getKey()).withTagValue(entry.getValue())) + .collect(Collectors.toList()); AWSKMS client = kmsClientFactory.getClient(region); CreateKeyRequest request = new CreateKeyRequest(); request.setPolicy(policy); request.setDescription(description); + request.setTags(kmsTags); CreateKeyResult result = client.createKey(request); KeyMetadata keyMetadata = result.getKeyMetadata(); From 6fac19389d7a45b4108c8001d32b59dc1ce89cd4 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 10 Nov 2017 09:12:22 -0800 Subject: [PATCH 18/69] Bug Fixes + Clean Up (#81) * Fix whitelist command * Store DB password encrypted in S3 * Clena up security group cloudformation * Clean up database CF * Remove unused import * Fix isRunnable for update CMS config * Add cms env name to list of system configured properties * Fix bug in security groups cloudformation * Make sure the database is multi-az --- .../com/nike/cerberus/ConfigConstants.java | 3 ++- .../command/core/CreateDatabaseCommand.java | 1 - .../cms/UpdateCmsConfigOperation.java | 8 +----- .../core/CreateDatabaseOperation.java | 14 ++++++---- .../WhitelistCidrForVpcAccessOperation.java | 26 +++++++++++-------- .../com/nike/cerberus/store/ConfigStore.java | 2 +- .../resources/cloudformation/database.yaml | 6 +++-- .../cloudformation/security-groups.yaml | 10 +------ 8 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 72fa715c..9c918db7 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -94,7 +94,8 @@ public class ConfigConstants { JDBC_USERNAME_KEY, JDBC_PASSWORD_KEY, CMK_ARNS_KEY, - HASH_SALT); + HASH_SALT, + CMS_ENV_NAME); public static final String CERBERUS_AMI_TAG_NAME = "tag:cerberus_component"; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java index 9306c517..af3534ec 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java @@ -16,7 +16,6 @@ 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; diff --git a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java index 058abe41..aaa7b94a 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java @@ -84,15 +84,9 @@ public void run(final UpdateCmsConfigCommand command) { @Override public boolean isRunnable(final UpdateCmsConfigCommand command) { - boolean isRunnable = true; - final Optional cmsVaultToken = configStore.getCmsVaultToken(); + boolean isRunnable = configStore.getCmsEnvConfig().isPresent(); final Optional cmsDatabasePassword = configStore.getCmsDatabasePassword(); - if (!cmsVaultToken.isPresent()) { - logger.error("CMS Vault token not present for specified environment."); - isRunnable = false; - } - if (!cmsDatabasePassword.isPresent()) { logger.error("CMS database password not present for specified environment."); isRunnable = false; diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index d8f78051..0ff9286b 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -57,9 +57,9 @@ public class CreateDatabaseOperation implements Operation @Inject public CreateDatabaseOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationService cloudFormationService, + final ConfigStore configStore, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.configStore = configStore; @@ -71,9 +71,10 @@ public void run(final CreateDatabaseCommand command) { final String environmentName = environmentMetadata.getName(); final VpcParameters vpcParameters = configStore.getVpcStackParameters(); final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); + final String databasePassword = passwordGenerator.get(); final DatabaseParameters databaseParameters = new DatabaseParameters() - .setCmsDbMasterPassword(passwordGenerator.get()) + .setCmsDbMasterPassword(databasePassword) .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)) .setCmsDbInstanceAz1(vpcParameters.getAz1()) .setCmsDbInstanceAz2(vpcParameters.getAz2()) @@ -88,6 +89,9 @@ public void run(final CreateDatabaseCommand command) { cloudFormationService.createStack(StackName.DATABASE.getFullName(environmentName), parameters, ConfigConstants.DATABASE_STACK_TEMPLATE_PATH, true, command.getTagsDelegate().getTags()); + + configStore.storeCmsDatabasePassword(databasePassword); + } @Override @@ -99,6 +103,6 @@ public boolean isRunnable(final CreateDatabaseCommand command) { throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); } - return ! cloudFormationService.isStackPresent(StackName.DATABASE.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(StackName.DATABASE.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java b/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java index 74d0346a..54906e16 100644 --- a/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java @@ -24,14 +24,12 @@ import com.amazonaws.services.ec2.model.RevokeSecurityGroupIngressRequest; import com.google.common.collect.Lists; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; +import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,13 +49,17 @@ public class WhitelistCidrForVpcAccessOperation implements Operation Date: Fri, 10 Nov 2017 14:05:00 -0800 Subject: [PATCH 19/69] Updating TLS policy on load balancer to ELBSecurityPolicy-2016-08 until we can get all clients moved to TLSv1.2 (#84) --- src/main/resources/cloudformation/load-balancer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 7841dc77..20c2a319 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -48,7 +48,7 @@ Resources: LoadBalancerArn: !Ref 'ApplicationLoadBalancer' Port: 443 Protocol: HTTPS - SslPolicy: ELBSecurityPolicy-TLS-1-2-2017-01 + SslPolicy: ELBSecurityPolicy-2016-08 ALBLogBucket: Properties: AccessControl: BucketOwnerFullControl From 635c465b7af75aacad929020a8f2f827db01048a Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 10 Nov 2017 15:01:58 -0800 Subject: [PATCH 20/69] Create an origin, and load balancer record for Cerberus (#85) * Create an origin, and load balancer record for Cerberus --- .../cli/EnvironmentConfigToArgsMapper.java | 4 +- .../command/core/CreateRoute53Command.java | 40 ++++++--- .../domain/cloudformation/Route53Outputs.java | 21 +++-- .../cloudformation/Route53Parameters.java | 38 +++++---- .../core/CreateRoute53Operation.java | 53 +++++++++--- .../nike/cerberus/service/Route53Service.java | 82 +++++++++++++++++++ .../com/nike/cerberus/store/ConfigStore.java | 2 +- .../resources/cloudformation/route53.yaml | 33 ++++++-- 8 files changed, 220 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/service/Route53Service.java diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index c542359e..12f145e4 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -294,9 +294,9 @@ private static List getCreateLoadBalancerCommandArgs(EnvironmentConfig c private static List getCreateRoute53CommandArgs(EnvironmentConfig config) { List args = new LinkedList<>(); - args.add(CreateRoute53Command.HOSTNAME_LONG_ARG); + args.add(CreateRoute53Command.BASE_DOMAIN_NAME_LONG_ARG); args.add(config.getHostname()); - args.add(CreateRoute53Command.HOSTED_ZONE_ID); + args.add(CreateRoute53Command.HOSTED_ZONE_ID_LONG_ARG); args.add(config.getHostedZoneId()); return args; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java index cc75f0ad..eb68ead8 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java @@ -27,17 +27,21 @@ import static com.nike.cerberus.command.core.CreateRoute53Command.COMMAND_NAME; /** - * Command to create the Route53 record for Cerberus. + * Creates the origin and load balancer Route53 records for Cerberus */ @Parameters(commandNames = COMMAND_NAME, commandDescription = "Create the Route53 record for use by Cerberus") public class CreateRoute53Command implements Command { - public static final String COMMAND_NAME = "create-route53-record"; + public static final String COMMAND_NAME = "create-route53-stack"; - public static final String HOSTNAME_LONG_ARG = "--hostname"; + public static final String BASE_DOMAIN_NAME_LONG_ARG = "--base-domain-name"; - public static final String HOSTED_ZONE_ID = "--hosted-zone-id"; + public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; + + public static final String ORIGIN_DOMAIN_NAME_OVERRIDE = "--origin-domain-name-override"; + + public static final String LOAD_BALANCER_DOMAIN_NAME_OVERRIDE = "--load-balancer-domain-name-override"; @ParametersDelegate private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); @@ -46,24 +50,40 @@ public TagParametersDelegate getTagsDelegate() { return tagsDelegate; } - @Parameter(names = HOSTNAME_LONG_ARG, - description = "The hostname of the Route53 record to be created for Cerberus (e.g. .cerberus.example.com)", + @Parameter(names = BASE_DOMAIN_NAME_LONG_ARG, + description = "The base hostname for Cerberus (e.g. url: https://env.cerberus.example.com => base hostname: cerberus.example.com)", required = true) - private String cerberusHostname; + private String baseDomainName; - @Parameter(names = HOSTED_ZONE_ID, + @Parameter(names = HOSTED_ZONE_ID_LONG_ARG, description = "The Route53 Hosted Zone in which to create the new Cerberus record", required = true) private String hostedZoneId; - public String getCerberusHostname() { - return cerberusHostname; + @Parameter(names = LOAD_BALANCER_DOMAIN_NAME_OVERRIDE, + description = "The full load balancer domain name for Cerberus. Default: env.region.example.domain.com") + private String loadBalancerDomainNameOverride; + + @Parameter(names = ORIGIN_DOMAIN_NAME_OVERRIDE, + description = "The full origin domain name for Cerberus. Default: origin.env.example.domain.com") + private String originDomainNameOverride; + + public String getBaseDomainName() { + return baseDomainName; + } + + public String getOriginDomainNameOverride() { + return originDomainNameOverride; } public String getHostedZoneId() { return hostedZoneId; } + public String getLoadBalancerDomainNameOverride() { + return loadBalancerDomainNameOverride; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java index c3ab1a8c..d845adf6 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Outputs.java @@ -21,14 +21,23 @@ */ public class Route53Outputs { - private String recordSet; + private String loadBalancerDomainName; - public String getRecordSet() { - return recordSet; + private String originDomainName; + + public String getLoadBalancerDomainName() { + return loadBalancerDomainName; + } + + public void setLoadBalancerDomainName(String loadBalancerDomainName) { + this.loadBalancerDomainName = loadBalancerDomainName; + } + + public String getOriginDomainName() { + return originDomainName; } - public Route53Outputs setRecordSet(String recordSet) { - this.recordSet = recordSet; - return this; + public void setOriginDomainName(String originDomainName) { + this.originDomainName = originDomainName; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java index 1d40127e..4fc63940 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/Route53Parameters.java @@ -20,37 +20,47 @@ * Represents the route53 stack inputs. */ public class Route53Parameters { + private String hostedZoneId; + + private String loadBalancerDomainName; private String loadBalancerStackName; - private String hostname; + private String originDomainName; - private String hostedZoneId; + public String getHostedZoneId() { + return hostedZoneId; + } - public String getLoadBalancerStackName() { - return loadBalancerStackName; + public Route53Parameters setHostedZoneId(String hostedZoneId) { + this.hostedZoneId = hostedZoneId; + return this; } - public Route53Parameters setLoadBalancerStackName(String loadBalancerStackName) { - this.loadBalancerStackName = loadBalancerStackName; + public String getLoadBalancerDomainName() { + return loadBalancerDomainName; + } + + public Route53Parameters setLoadBalancerDomainName(String loadBalancerDomainName) { + this.loadBalancerDomainName = loadBalancerDomainName; return this; } - public String getHostname() { - return hostname; + public String getLoadBalancerStackName() { + return loadBalancerStackName; } - public Route53Parameters setHostname(String hostname) { - this.hostname = hostname; + public Route53Parameters setLoadBalancerStackName(String loadBalancerStackName) { + this.loadBalancerStackName = loadBalancerStackName; return this; } - public String getHostedZoneId() { - return hostedZoneId; + public String getOriginDomainName() { + return originDomainName; } - public Route53Parameters setHostedZoneId(String hostedZoneId) { - this.hostedZoneId = hostedZoneId; + public Route53Parameters setOriginDomainName(String originDomainName) { + this.originDomainName = originDomainName; return this; } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index 8d26ef1e..b2a86fb7 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -25,7 +25,9 @@ import com.nike.cerberus.domain.environment.StackName; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.Route53Service; import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,7 @@ import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** - * Creates the base components via CloudFormation used by all of Cerberus. + * Creates the origin and load balancer Route53 records for Cerberus */ public class CreateRoute53Operation implements Operation { @@ -46,14 +48,18 @@ public class CreateRoute53Operation implements Operation { private final CloudFormationService cloudFormationService; + private final Route53Service route53Service; + private final ObjectMapper cloudFormationObjectMapper; @Inject public CreateRoute53Operation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationService cloudFormationService, + final Route53Service route53Service, + @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; + this.route53Service = route53Service; this.cloudFormationObjectMapper = cloudformationObjectMapper; } @@ -62,9 +68,10 @@ public void run(final CreateRoute53Command command) { final String environmentName = environmentMetadata.getName(); final Route53Parameters route53Parameters = new Route53Parameters() - .setHostname(command.getCerberusHostname()) .setHostedZoneId(command.getHostedZoneId()) - .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)); + .setLoadBalancerDomainName(getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride())) + .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)) + .setOriginDomainName(getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride())); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); @@ -76,13 +83,37 @@ public void run(final CreateRoute53Command command) { @Override public boolean isRunnable(final CreateRoute53Command command) { - String environmentName = environmentMetadata.getName(); - try { - cloudFormationService.getStackId(StackName.LOAD_BALANCER.getFullName(environmentName)); - } catch (IllegalArgumentException iae) { - throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); + final String environmentName = environmentMetadata.getName(); + final String loadBalancerDomainName = getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride()); + final String originDomainName = getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride()); + + if (!cloudFormationService.isStackPresent(StackName.LOAD_BALANCER.getFullName(environmentName))) { + throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!"); } + if (cloudFormationService.isStackPresent(StackName.ROUTE53.getFullName(environmentName))) { + throw new IllegalStateException("Route53 stack already exists."); + } + + return !(route53Service.recordSetWithNameAlreadyExists(loadBalancerDomainName, command.getHostedZoneId()) || + route53Service.recordSetWithNameAlreadyExists(originDomainName, command.getHostedZoneId())); + } + + private String getLoadBalancerDomainName(final String baseDomainName, final String loadBalancerDomainNameOverride) { + final String defaultLoadBalancerDomainName = String.format("%s.%s.%s", + environmentMetadata.getName(), + environmentMetadata.getRegionName(), + baseDomainName); + + return StringUtils.isBlank(loadBalancerDomainNameOverride) ? + defaultLoadBalancerDomainName : loadBalancerDomainNameOverride; + } + + private String getOriginDomainName(final String baseDomainName, final String originDomainNameOverride) { + final String defaultOriginDomainName = String.format("origin.%s.%s", + environmentMetadata.getName(), + baseDomainName); - return ! cloudFormationService.isStackPresent(StackName.ROUTE53.getFullName(environmentName)); + return StringUtils.isBlank(originDomainNameOverride) ? + defaultOriginDomainName : originDomainNameOverride; } } diff --git a/src/main/java/com/nike/cerberus/service/Route53Service.java b/src/main/java/com/nike/cerberus/service/Route53Service.java new file mode 100644 index 00000000..02f2bd76 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/Route53Service.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 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.services.route53.AmazonRoute53; +import com.amazonaws.services.route53.model.Change; +import com.amazonaws.services.route53.model.ChangeAction; +import com.amazonaws.services.route53.model.ChangeBatch; +import com.amazonaws.services.route53.model.ChangeResourceRecordSetsRequest; +import com.amazonaws.services.route53.model.ListResourceRecordSetsRequest; +import com.amazonaws.services.route53.model.ListResourceRecordSetsResult; +import com.amazonaws.services.route53.model.RRType; +import com.amazonaws.services.route53.model.ResourceRecord; +import com.amazonaws.services.route53.model.ResourceRecordSet; +import com.nike.cerberus.domain.EnvironmentMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + +/** + * Service wrapper for AWS CloudFormation. + */ +public class Route53Service { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final AmazonRoute53 route53Client; + + @Inject + public Route53Service(final AmazonRoute53 route53Client) { + this.route53Client = route53Client; + } + + public void createRoute53RecordSet(String hostedZoneId, + String recordSetName, + String recordValue, + RRType recordSetType, + String resourceRecordTtl) { + logger.info("Creating Route53 record name: {}, value: {}", recordSetName, recordValue); + + final ResourceRecord record = new ResourceRecord().withValue(recordValue); + final ResourceRecordSet recordSet = new ResourceRecordSet() + .withResourceRecords(record) + .withName(recordSetName) + .withType(recordSetType) + .withTTL(Long.parseLong(resourceRecordTtl)); + + final ChangeBatch recordSetChangeBatch = new ChangeBatch() + .withChanges(new Change() + .withAction(ChangeAction.CREATE) + .withResourceRecordSet(recordSet)); + + route53Client.changeResourceRecordSets(new ChangeResourceRecordSetsRequest() + .withHostedZoneId(hostedZoneId) + .withChangeBatch(recordSetChangeBatch)); + } + + public boolean recordSetWithNameAlreadyExists(final String recordSetName, final String hostedZoneId) { + final ListResourceRecordSetsResult recordSets = route53Client.listResourceRecordSets( + new ListResourceRecordSetsRequest() + .withHostedZoneId(hostedZoneId)); + + return recordSets.getResourceRecordSets() + .stream() + .anyMatch(recordSet -> recordSet.getName().equals(recordSetName + ".")); + } +} diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 41848152..05576f35 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -432,7 +432,7 @@ public Optional getAccountAdminArn() { } public String getCerberusBaseUrl() { - return String.format("https://%s", getRoute53Parameters().getHostname()); + throw new UnsupportedOperationException("Not yet implemented"); // will implement this in next PR } /** diff --git a/src/main/resources/cloudformation/route53.yaml b/src/main/resources/cloudformation/route53.yaml index b7f12b59..8e12783c 100644 --- a/src/main/resources/cloudformation/route53.yaml +++ b/src/main/resources/cloudformation/route53.yaml @@ -3,26 +3,41 @@ Conditions: RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] Description: Creates the Route53 record for Cerberus Outputs: - recordSet: - Value: !Ref 'CerberusPublicRecordSet' + loadBalancerDomainName: + Value: !Ref 'CerberusLoadBalancerRecordSet' + originDomainName: + Value: !Ref 'CerberusOriginRecordSet' Parameters: + hostedZoneId: + Description: The base hosted zone in which the load balancer and origin records will be created + Type: String + loadBalancerDomainName: + Description: The domain name for the Cerberus load balancer (e.g. demo.us-west-2.cerberus.example.com) + Type: String loadBalancerStackName: Description: The name of the Cerberus load balancer CloudFormation stack Type: String - hostname: - Description: The base hostname for the public facing ALB - Type: String - hostedZoneId: - Description: The base hosted zone to add the ALB CNAME to + originDomainName: + Description: The origin domain name of the Cerberus environment (e.g. origin.demo.cerberus.example.com) Type: String Resources: - CerberusPublicRecordSet: + CerberusLoadBalancerRecordSet: Properties: HostedZoneId: Ref: hostedZoneId - Name: !Join [., [!Ref 'hostname', '']] + Name: !Join [., [!Ref 'loadBalancerDomainName', '']] ResourceRecords: - Fn::ImportValue: !Sub "${loadBalancerStackName}-loadBalancerDnsName" TTL: 30 Type: CNAME + Type: AWS::Route53::RecordSet + CerberusOriginRecordSet: + Properties: + HostedZoneId: + Ref: hostedZoneId + Name: !Join [., [!Ref 'originDomainName', '']] + ResourceRecords: + - Ref: CerberusLoadBalancerRecordSet + TTL: 30 + Type: CNAME Type: AWS::Route53::RecordSet \ No newline at end of file From 1d1376cddbefa760a4568d79d58b97a34167705d Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 10 Nov 2017 15:07:18 -0800 Subject: [PATCH 21/69] Add command to create edge domain record --- .../com/nike/cerberus/cli/CerberusRunner.java | 2 + .../core/CreateEdgeDomainRecordCommand.java | 78 +++++++++++++++ .../domain/environment/Environment.java | 10 ++ .../core/CreateEdgeDomainRecordOperation.java | 95 +++++++++++++++++++ .../com/nike/cerberus/store/ConfigStore.java | 23 ++++- 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 28cc0a39..dfbd7216 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -35,6 +35,7 @@ import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; @@ -184,6 +185,7 @@ private void registerAllCommands() { registerCommand(new CreateRoute53Command()); registerCommand(new CreateSecurityGroupsCommand()); registerCommand(new CreateLoadBalancerCommand()); + registerCommand(new CreateEdgeDomainRecordCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java new file mode 100644 index 00000000..4eb126c2 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.CreateEdgeDomainRecordOperation; + +import static com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand.COMMAND_NAME; + +/** + * Command to create the edge domain Route53 record for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the Route53 record for use by Cerberus") +public class CreateEdgeDomainRecordCommand implements Command { + + public static final String COMMAND_NAME = "create-edge-domain-record"; + + public static final String BASE_DOMAIN_NAME_LONG_ARG = "--base-domain-name"; + + public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; + + public static final String EDGE_DOMAIN_NAME_OVERRIDE = "edge-domain-name-override"; + + @Parameter(names = BASE_DOMAIN_NAME_LONG_ARG, + description = "The base domain name for Cerberus (e.g. url: https://env.cerberus.example.com => base hostname: cerberus.example.com)", + required = true) + private String baseDomainName; + + @Parameter(names = EDGE_DOMAIN_NAME_OVERRIDE, + description = "The full edge domain name for Cerberus. Default: env.example.domain.com") + private String edgeDomainNameOverride; + + @Parameter(names = HOSTED_ZONE_ID_LONG_ARG, + description = "The Route53 Hosted Zone in which to create the new Cerberus record", + required = true) + private String hostedZoneId; + + public String getBaseDomainName() { + return baseDomainName; + } + + public String getEdgeDomainNameOverride() { + return edgeDomainNameOverride; + } + + public String getHostedZoneId() { + return hostedZoneId; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateEdgeDomainRecordOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java index dc1d5926..866af7af 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Environment.java @@ -32,6 +32,8 @@ public class Environment { private String az3; + private String domainName; + private Map stackMap; private Map serverCertificateIdMap; @@ -86,6 +88,14 @@ public Environment setAz3(String az3) { return this; } + public String getDomainName() { + return domainName; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + public Map getStackMap() { return stackMap; } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java new file mode 100644 index 00000000..2328157d --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 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.services.route53.model.RRType; +import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.Route53Service; +import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + +/** + * Creates the edge domain Route53 record for Cerberus + */ +public class CreateEdgeDomainRecordOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private static final String RESOURCE_RECORD_TTL = "30"; // in seconds + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final EnvironmentMetadata environmentMetadata; + + private final Route53Service route53Service; + + @Inject + public CreateEdgeDomainRecordOperation(final CloudFormationService cloudFormationService, + final ConfigStore configStore, + final EnvironmentMetadata environmentMetadata, + final Route53Service route53Service) { + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.environmentMetadata = environmentMetadata; + this.route53Service = route53Service; + } + + @Override + public void run(final CreateEdgeDomainRecordCommand command) { + final String recordValue = configStore.getRoute53StackOutputs().getOriginDomainName(); + final String recordSetName = getEdgeDomainName(command.getBaseDomainName(), command.getEdgeDomainNameOverride()); + + route53Service.createRoute53RecordSet(command.getHostedZoneId(), + recordSetName, + recordValue, + RRType.CNAME, + RESOURCE_RECORD_TTL); + } + + @Override + public boolean isRunnable(final CreateEdgeDomainRecordCommand command) { + final String environmentName = environmentMetadata.getName(); + final String recordSetName = getEdgeDomainName(command.getBaseDomainName(), command.getEdgeDomainNameOverride()); + + try { + cloudFormationService.getStackId(StackName.ROUTE53.getFullName(environmentName)); + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); + } + + return !route53Service.recordSetWithNameAlreadyExists(recordSetName, command.getHostedZoneId()); + } + + private String getEdgeDomainName(String baseDomainName, final String edgeDomainNameOverride) { + final String defaultEdgeDomainName = String.format("%s.%s", + environmentMetadata.getName(), + baseDomainName); + + return StringUtils.isBlank(edgeDomainNameOverride) ? + defaultEdgeDomainName : edgeDomainNameOverride; + } +} diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 05576f35..aba557db 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -37,6 +37,7 @@ import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.LoadBalancerOutputs; +import com.nike.cerberus.domain.cloudformation.Route53Outputs; import com.nike.cerberus.domain.cloudformation.Route53Parameters; import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; @@ -284,6 +285,15 @@ public void storeCmsDatabasePassword(final String databasePassword) { } } + public void storeEnvDomainName(final String domainName) { + synchronized (envDataLock) { + final Environment environment = getEnvironmentData(); + + environment.setDomainName(domainName); + saveEnvironmentData(environment); + } + } + /** * Retrieves the CMS Vault token from the config store. * @@ -432,7 +442,7 @@ public Optional getAccountAdminArn() { } public String getCerberusBaseUrl() { - throw new UnsupportedOperationException("Not yet implemented"); // will implement this in next PR + return String.format("https://%s", getEnvironmentData().getDomainName()); } /** @@ -569,10 +579,19 @@ public DatabaseParameters getDatabaseStackParameters() { * * @return Base parameters */ - public Route53Parameters getRoute53Parameters() { + public Route53Parameters getRoute53StackParameters() { return getStackParameters(getStackLogicalId(StackName.ROUTE53), Route53Parameters.class); } + /** + * Get the base stack parameters. + * + * @return Base parameters + */ + public Route53Outputs getRoute53StackOutputs() { + return getStackOutputs(getStackLogicalId(StackName.ROUTE53), Route53Outputs.class); + } + /** * Get the base stack outputs. * From 57d956bc558ea83f967ca5615d5d29a150aaf2fb Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 10 Nov 2017 15:10:38 -0800 Subject: [PATCH 22/69] Clarify parameter description --- .../cerberus/command/core/CreateEdgeDomainRecordCommand.java | 2 +- .../com/nike/cerberus/command/core/CreateRoute53Command.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java index 4eb126c2..60507008 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java @@ -45,7 +45,7 @@ public class CreateEdgeDomainRecordCommand implements Command { private String baseDomainName; @Parameter(names = EDGE_DOMAIN_NAME_OVERRIDE, - description = "The full edge domain name for Cerberus. Default: env.example.domain.com") + description = "Override the edge domain name for Cerberus. Default: env.example.domain.com") private String edgeDomainNameOverride; @Parameter(names = HOSTED_ZONE_ID_LONG_ARG, diff --git a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java index eb68ead8..6845e905 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateRoute53Command.java @@ -61,11 +61,11 @@ public TagParametersDelegate getTagsDelegate() { private String hostedZoneId; @Parameter(names = LOAD_BALANCER_DOMAIN_NAME_OVERRIDE, - description = "The full load balancer domain name for Cerberus. Default: env.region.example.domain.com") + description = "Override the load balancer domain name for Cerberus. Default: env.region.example.domain.com") private String loadBalancerDomainNameOverride; @Parameter(names = ORIGIN_DOMAIN_NAME_OVERRIDE, - description = "The full origin domain name for Cerberus. Default: origin.env.example.domain.com") + description = "Override the origin domain name for Cerberus. Default: origin.env.example.domain.com") private String originDomainNameOverride; public String getBaseDomainName() { From 0a1d51236d0182868eaf5396f7ab8a71f84ca1af Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Fri, 10 Nov 2017 15:12:34 -0800 Subject: [PATCH 23/69] Adding --force option to UpdateCmsConfig (#83) --- .../nike/cerberus/command/cms/UpdateCmsConfigCommand.java | 8 ++++++++ .../cerberus/operation/cms/UpdateCmsConfigOperation.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/nike/cerberus/command/cms/UpdateCmsConfigCommand.java b/src/main/java/com/nike/cerberus/command/cms/UpdateCmsConfigCommand.java index e4b3d46c..6b36e55b 100644 --- a/src/main/java/com/nike/cerberus/command/cms/UpdateCmsConfigCommand.java +++ b/src/main/java/com/nike/cerberus/command/cms/UpdateCmsConfigCommand.java @@ -36,6 +36,7 @@ public class UpdateCmsConfigCommand implements Command { public static final String COMMAND_NAME = "update-cms-config"; public static final String OVERWRITE_LONG_ARG = "--overwrite"; + public static final String FORCE_ARG = "--force"; @Parameter(names = CreateCmsConfigCommand.ADMIN_GROUP_LONG_ARG, description = "Group that has admin privileges in CMS.") private String adminGroup; @@ -46,6 +47,9 @@ public class UpdateCmsConfigCommand implements Command { @DynamicParameter(names = CreateCmsConfigCommand.PROPERTY_SHORT_ARG, description = "Dynamic parameters for setting additional properties in the CMS environment configuration.") private Map additionalProperties = new HashMap<>(); + @Parameter(names = FORCE_ARG, description = "Force allow overwriting of system generated property. This may break your configuration.") + private boolean force = false; + public String getAdminGroup() { return adminGroup; } @@ -58,6 +62,10 @@ public Map getAdditionalProperties() { return additionalProperties; } + public boolean isForce() { + return force; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java index aaa7b94a..a41ea3ec 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java @@ -58,7 +58,7 @@ public void run(final UpdateCmsConfigCommand command) { // update existing custom properties, add new ones command.getAdditionalProperties().forEach((k, v) -> { - if (! SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(k)) { + if (! SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(k) || command.isForce()) { newProperties.put(k, v); } else { logger.warn("Ignoring additional property that would override system configured property, " + k); From 2be788c6156568b49623b1609967b9f121df9a42 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Fri, 10 Nov 2017 15:42:19 -0800 Subject: [PATCH 24/69] Refactoring StackName enum into a Stack class with methods (#87) * Refactoring StackName enum into a Stack class with methods --- .../com/nike/cerberus/ConfigConstants.java | 18 --- .../command/core/PrintStackInfoCommand.java | 11 +- .../RollingRebootWithHealthCheckCommand.java | 8 +- .../command/core/UpdateStackCommand.java | 12 +- .../command/core/UploadCertFilesCommand.java | 12 +- .../UploadCertFilesStackNameValidator.java | 10 +- .../domain/environment/Environment.java | 18 +-- .../cerberus/domain/environment/Stack.java | 109 ++++++++++++++++++ .../domain/environment/StackName.java | 62 ---------- .../cms/CreateCmsClusterOperation.java | 28 ++--- .../PrintAllStackInformationOperation.java | 6 +- .../operation/core/CreateBaseOperation.java | 10 +- .../core/CreateDatabaseOperation.java | 13 +-- .../core/CreateEdgeDomainRecordOperation.java | 4 +- .../core/CreateLoadBalancerOperation.java | 15 +-- .../core/CreateRoute53Operation.java | 12 +- .../core/CreateSecurityGroupsOperation.java | 10 +- .../operation/core/CreateVpcOperation.java | 10 +- .../operation/core/CreateWafOperation.java | 13 +-- .../core/PrintStackInfoOperation.java | 4 +- ...RollingRebootWithHealthCheckOperation.java | 22 ++-- .../operation/core/UpdateStackOperation.java | 51 +++----- .../core/UploadCertFilesOperation.java | 13 +-- .../WhitelistCidrForVpcAccessOperation.java | 4 +- .../cerberus/service/AmiTagCheckService.java | 12 +- .../service/CloudFormationService.java | 71 +++--------- .../cerberus/service/Ec2UserDataService.java | 17 ++- .../com/nike/cerberus/store/ConfigStore.java | 104 ++++++++--------- .../nike/cerberus/util/StackConverter.java | 15 +++ 29 files changed, 328 insertions(+), 366 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/domain/environment/Stack.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/StackName.java create mode 100644 src/main/java/com/nike/cerberus/util/StackConverter.java diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 9c918db7..17c0fb30 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -44,24 +44,6 @@ public class ConfigConstants { public static final String CERT_PART_PUBKEY = "pubkey.pem"; - public static final String BASE_STACK_NAME = "base"; - - public static final String BASE_STACK_TEMPLATE_PATH = "/cloudformation/base.yaml"; - - public static final String CMS_STACK_TEMPLATE_PATH = "/cloudformation/cms-cluster.yaml"; - - public static final String DATABASE_STACK_TEMPLATE_PATH = "/cloudformation/database.yaml"; - - public static final String LOAD_BALANCER_STACK_TEMPLATE_PATH = "/cloudformation/load-balancer.yaml"; - - public static final String ROUTE53_TEMPLATE_PATH = "/cloudformation/route53.yaml"; - - public static final String SECURITY_GROUPS_STACK_TEMPLATE_PATH = "/cloudformation/security-groups.yaml"; - - public static final String VPC_STACK_TEMPLATE_PATH = "/cloudformation/vpc.yaml"; - - public static final String WAF_STACK_TEMPLATE_PATH = "/cloudformation/web-app-firewall.yaml"; - public static final String CMS_ENV_CONFIG_PATH = "data/cms/environment.properties"; public static final String VERSION_PROPERTY = "cli.version"; diff --git a/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java b/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java index efaecd7c..e436aad9 100644 --- a/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/PrintStackInfoCommand.java @@ -19,9 +19,10 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.PrintStackInfoOperation; +import com.nike.cerberus.util.StackConverter; import static com.nike.cerberus.command.core.PrintStackInfoCommand.COMMAND_NAME; @@ -35,11 +36,11 @@ public class PrintStackInfoCommand implements Command { public static final String STACK_NAME_LONG_ARG = "--stack-name"; - @Parameter(names = {STACK_NAME_LONG_ARG}, required = true, description = "The stack name to print information about.") - private StackName stackName; + @Parameter(names = {STACK_NAME_LONG_ARG}, required = true, description = "The stack name to print information about.", converter = StackConverter.class) + private Stack stack; - public StackName getStackName() { - return stackName; + public Stack getStack() { + return stack; } @Override diff --git a/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java b/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java index c34b584d..1d384a72 100644 --- a/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java @@ -19,7 +19,7 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.RollingRebootWithHealthCheckOperation; @@ -38,10 +38,10 @@ public class RollingRebootWithHealthCheckCommand implements Command { public static final String COMMAND_NAME = "rolling-reboot"; @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to reboot.") - private StackName stackName = StackName.CMS; + private Stack stack = Stack.CMS; - public StackName getStackName() { - return stackName; + public Stack getStack() { + return stack; } @Override diff --git a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java index 36a02064..f033e6ad 100644 --- a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java @@ -22,10 +22,10 @@ import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; import com.nike.cerberus.command.StackDelegate; -import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.UpdateStackOperation; +import com.nike.cerberus.util.StackConverter; import java.util.HashMap; import java.util.Map; @@ -45,8 +45,8 @@ public class UpdateStackCommand implements Command { public static final String OVERWRITE_TEMPLATE_LONG_ARG = "--overwrite-template"; public static final String PARAMETER_SHORT_ARG = "-P"; - @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to update.") - private StackName stackName; + @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to update.", converter = StackConverter.class) + private Stack stack; @ParametersDelegate private StackDelegate stackDelegate = new StackDelegate(); @@ -62,8 +62,8 @@ public class UpdateStackCommand implements Command { @DynamicParameter(names = PARAMETER_SHORT_ARG, description = "Dynamic parameters for overriding the values for specific parameters in the CloudFormation.") private Map dynamicParameters = new HashMap<>(); - public StackName getStackName() { - return stackName; + public Stack getStack() { + return stack; } public StackDelegate getStackDelegate() { diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java b/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java index 9a0c3ca8..65065df8 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java @@ -21,9 +21,10 @@ import com.nike.cerberus.command.Command; import com.nike.cerberus.command.validator.UploadCertFilesPathValidator; import com.nike.cerberus.command.validator.UploadCertFilesStackNameValidator; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.UploadCertFilesOperation; +import com.nike.cerberus.util.StackConverter; import java.nio.file.Path; @@ -44,8 +45,9 @@ public class UploadCertFilesCommand implements Command { names = {STACK_NAME_LONG_ARG}, required = true, description = "Stack name the certificate is for.", - validateValueWith = UploadCertFilesStackNameValidator.class) - private StackName stackName; + validateValueWith = UploadCertFilesStackNameValidator.class, + converter = StackConverter.class) + private Stack stack; @Parameter( names = {CERT_PATH_LONG_ARG}, @@ -60,8 +62,8 @@ public class UploadCertFilesCommand implements Command { ) private boolean overwrite; - public StackName getStackName() { - return stackName; + public Stack getStack() { + return stack; } public Path getCertPath() { diff --git a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java index 86b7752c..a8100908 100644 --- a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java +++ b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesStackNameValidator.java @@ -19,20 +19,20 @@ import com.beust.jcommander.IValueValidator; import com.beust.jcommander.ParameterException; import com.google.common.collect.Sets; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import java.util.Set; /** * Validates that the stack name specified actually requires a certificate to be uploaded. */ -public class UploadCertFilesStackNameValidator implements IValueValidator { +public class UploadCertFilesStackNameValidator implements IValueValidator { - private final Set stackNamesWithCerts = Sets.newHashSet(StackName.CMS); + private final Set stackNamesWithCerts = Sets.newHashSet(Stack.CMS); @Override - public void validate(final String name, final StackName stackName) throws ParameterException { - if (!stackNamesWithCerts.contains(stackName)) { + public void validate(final String name, final Stack stack) throws ParameterException { + if (!stackNamesWithCerts.contains(stack)) { throw new ParameterException("Stack specified doesn't require a certificate to be uploaded."); } } diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java index 866af7af..a33430d5 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Environment.java @@ -34,9 +34,9 @@ public class Environment { private String domainName; - private Map stackMap; + private Map stackMap; - private Map serverCertificateIdMap; + private Map serverCertificateIdMap; private String configKeyId; @@ -53,12 +53,12 @@ public class Environment { public Environment() { stackMap = new HashMap<>(); - for (StackName stackName : StackName.values()) { - stackMap.put(stackName, ""); + for (Stack stack : Stack.ALL_STACKS) { + stackMap.put(stack, ""); } serverCertificateIdMap = new HashMap<>(); - serverCertificateIdMap.put(StackName.CMS, ""); + serverCertificateIdMap.put(Stack.CMS, ""); } public String getAz1() { @@ -96,20 +96,20 @@ public void setDomainName(String domainName) { this.domainName = domainName; } - public Map getStackMap() { + public Map getStackMap() { return stackMap; } - public Environment setStackMap(Map stackMap) { + public Environment setStackMap(Map stackMap) { this.stackMap = stackMap; return this; } - public Map getServerCertificateIdMap() { + public Map getServerCertificateIdMap() { return serverCertificateIdMap; } - public Environment setServerCertificateIdMap(Map serverCertificateIdMap) { + public Environment setServerCertificateIdMap(Map serverCertificateIdMap) { this.serverCertificateIdMap = serverCertificateIdMap; 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 new file mode 100644 index 00000000..91f6bf5a --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 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.environment; + +import com.google.common.collect.ImmutableList; +import com.nike.cerberus.ConfigConstants; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Describes the stacks that make up Cerberus. + */ +public class Stack { + + public static final Stack BASE = new Stack("base", "base.yaml"); + public static final Stack VAULT = new Stack("vault", null); + public static final Stack CMS = new Stack("cms", "cms-cluster.yaml"); + public static final Stack GATEWAY = new Stack("gateway", null); + public static final Stack VPC = new Stack("vpc", "vpc.yaml"); + public static final Stack DATABASE = new Stack("database", "database.yaml"); + public static final Stack SECURITY_GROUPS = new Stack("security-groups", "security-groups.yaml"); + public static final Stack LOAD_BALANCER = new Stack("load-balancer", "load-balancer.yaml"); + public static final Stack ROUTE53 = new Stack("route53", "route53.yaml"); + public static final Stack WAF = new Stack("web-app-firewall", "web-app-firewall.yaml"); + + public static final ImmutableList ALL_STACKS = ImmutableList.of(BASE, VAULT, CMS, GATEWAY, VPC, DATABASE, SECURITY_GROUPS, LOAD_BALANCER, ROUTE53, WAF); + + private static final String TEMPLATE_PATH_ROOT = "/cloudformation/"; + + private final Logger logger = LoggerFactory.getLogger(Stack.class); + + + private final String name; + private final String templatePath; + + private Stack(final String name, final String cloudFormationFileName) { + this.name = name; + this.templatePath = TEMPLATE_PATH_ROOT + cloudFormationFileName; + } + + public String getName() { + return name; + } + + public String getTemplatePath() { + return templatePath; + } + /** + * Gets the template contents from the file on the classpath. + * + * @return Template contents + */ + public String getTemplateText() { + final InputStream templateStream = getClass().getResourceAsStream(templatePath); + + if (templateStream == null) { + throw new IllegalStateException( + String.format("The CloudFormation JSON template doesn't exist on the classpath. path: %s", templatePath)); + } + + try { + return IOUtils.toString(templateStream, ConfigConstants.DEFAULT_ENCODING); + } catch (final IOException e) { + final String errorMessage = String.format("Unable to read input stream from %s", templatePath); + logger.error(errorMessage); + throw new RuntimeException(errorMessage, e); + } + } + + /** + * Generate the CloudFormation stack name for each Cerberus component + * + * @param environmentName The name of the environment in which the component lives (e.g. demo, preprod, devel, etc.) + * @return The generated CloudFormation stack name + */ + public String getFullName(String environmentName) { + return String.format("%s-cerberus-%s", environmentName, name); + } + + public static Stack fromName(final String name) { + for (Stack stack : ALL_STACKS) { + if (stack.getName().equalsIgnoreCase(StringUtils.replaceAll(name,"_", "-"))) { + return stack; + } + } + + throw new IllegalArgumentException("Unknown stack name: " + name); + } + +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackName.java b/src/main/java/com/nike/cerberus/domain/environment/StackName.java deleted file mode 100644 index d15d5495..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/StackName.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -/** - * Describes the stacks that make up Cerberus. - */ -public enum StackName { - BASE("base"), - VAULT("vault"), - CMS("cms"), - GATEWAY("gateway"), - VPC("vpc"), - DATABASE("database"), - SECURITY_GROUPS("security-groups"), - LOAD_BALANCER("load-balancer"), - ROUTE53("route53"), - WAF("web-app-firewall"); - - private final String name; - - StackName(final String name) { - this.name = name; - } - - public String getName() { - return name; - } - - /** - * Generate the CloudFormation stack name for each Cerberus component - * @param environmentName The name of the environment in which the component lives (e.g. demo, preprod, devel, etc.) - * @return The generated CloudFormation stack name - */ - public String getFullName(String environmentName) { - return String.format("%s-cerberus-%s", environmentName, name); - } - - public static StackName fromName(final String name) { - for (StackName stackName : StackName.values()) { - if (stackName.getName().equals(name)) { - return stackName; - } - } - - throw new IllegalArgumentException("Unknown stack name: " + name); - } -} diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index 7bb3383f..9d455117 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -25,7 +25,7 @@ import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.AmiTagCheckService; @@ -80,8 +80,8 @@ public CreateCmsClusterOperation(final EnvironmentMetadata environmentMetadata, public void run(final CreateCmsClusterCommand command) { final String environmentName = environmentMetadata.getName(); final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); - final Optional cmsServerCertificateArn = configStore.getServerCertificateArn(StackName.CMS); - final Optional pubKey = configStore.getCertPart(StackName.CMS, ConfigConstants.CERT_PART_PUBKEY); + final Optional cmsServerCertificateArn = configStore.getServerCertificateArn(Stack.CMS); + final Optional pubKey = configStore.getCertPart(Stack.CMS, ConfigConstants.CERT_PART_PUBKEY); if (!cmsServerCertificateArn.isPresent() || !pubKey.isPresent()) { throw new IllegalStateException("CMS certificate has not been uploaded!"); @@ -89,21 +89,21 @@ public void run(final CreateCmsClusterCommand command) { // Make sure the given AmiId is for CMS component. Check if it contains required tag if ( ! command.isSkipAmiTagCheck() ) { - amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), StackName.CMS); + amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), Stack.CMS); } final CmsParameters cmsParameters = new CmsParameters() .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()) - .setBaseStackName(StackName.BASE.getFullName(environmentName)) - .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)) - .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)); + .setBaseStackName(Stack.BASE.getFullName(environmentName)) + .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) + .setSgStackName(Stack.SECURITY_GROUPS.getFullName(environmentName)); cmsParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); cmsParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); cmsParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - cmsParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(StackName.CMS)); + cmsParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(Stack.CMS)); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters, typeReference); @@ -111,8 +111,8 @@ public void run(final CreateCmsClusterCommand command) { // allow user to overwrite CloudFormation parameters with -P option parameters.putAll(command.getStackDelegate().getDynamicParameters()); - final String stackId = cloudFormationService.createStack(StackName.CMS.getFullName(environmentName), - parameters, ConfigConstants.CMS_STACK_TEMPLATE_PATH, true, + final String stackId = cloudFormationService.createStack(Stack.CMS, + parameters, true, command.getStackDelegate().getTagParameters().getTags()); final StackStatus endStatus = @@ -132,9 +132,9 @@ public boolean isRunnable(final CreateCmsClusterCommand command) { String environmentName = environmentMetadata.getName(); try { - cloudFormationService.getStackId(StackName.LOAD_BALANCER.getFullName(environmentName)); - cloudFormationService.getStackId(StackName.SECURITY_GROUPS.getFullName(environmentName)); - cloudFormationService.getStackId(StackName.BASE.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.LOAD_BALANCER.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.BASE.getFullName(environmentName)); } catch (IllegalArgumentException iae) { logger.error("Could not create the CMS cluster." + "Make sure the load balancer, security group, and base stacks have all been created.", iae); @@ -142,6 +142,6 @@ public boolean isRunnable(final CreateCmsClusterCommand command) { } return configStore.getCmsEnvConfig().isPresent() && - ! cloudFormationService.isStackPresent(StackName.CMS.getFullName(environmentName)); + ! cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java index 18b47880..117ad431 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java @@ -18,7 +18,7 @@ import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import java.util.LinkedList; import java.util.List; @@ -32,12 +32,12 @@ public class PrintAllStackInformationOperation extends CompositeOperation getCompositeCommandChain() { List commandList = new LinkedList<>(); - for (StackName stackName : StackName.values()) { + for (Stack stack : Stack.ALL_STACKS) { commandList.add( ChainableCommand.Builder.create() .withCommand(new PrintStackInfoCommand()) .withAdditionalArg(PrintStackInfoCommand.STACK_NAME_LONG_ARG) - .withAdditionalArg(stackName.toString()) + .withAdditionalArg(stack.toString()) .build() ); } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index bf495574..10b6ce50 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -18,11 +18,10 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.BaseParameters; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; @@ -63,7 +62,6 @@ public CreateBaseOperation(final EnvironmentMetadata environmentMetadata, @Override public void run(final CreateBaseCommand command) { - final String environmentName = environmentMetadata.getName(); final BaseParameters baseParameters = new BaseParameters() .setAccountAdminArn(command.getAdminRoleArn()); @@ -71,9 +69,7 @@ public void run(final CreateBaseCommand command) { final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); - cloudFormationService.createStack(StackName.BASE.getFullName(environmentName), - parameters, ConfigConstants.BASE_STACK_TEMPLATE_PATH, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStack(Stack.BASE, parameters, true, command.getTagsDelegate().getTags()); configStore.initEnvironmentData(); configStore.initSecretsData(); @@ -82,7 +78,7 @@ public void run(final CreateBaseCommand command) { @Override public boolean isRunnable(final CreateBaseCommand command) { String environmentName = environmentMetadata.getName(); - return ! cloudFormationService.isStackPresent(StackName.BASE.getFullName(environmentName)); + return ! cloudFormationService.isStackPresent(Stack.BASE.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index 0ff9286b..a87c21f3 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -18,13 +18,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.cloudformation.VpcParameters; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; @@ -75,7 +74,7 @@ public void run(final CreateDatabaseCommand command) { final DatabaseParameters databaseParameters = new DatabaseParameters() .setCmsDbMasterPassword(databasePassword) - .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)) + .setSgStackName(Stack.SECURITY_GROUPS.getFullName(environmentName)) .setCmsDbInstanceAz1(vpcParameters.getAz1()) .setCmsDbInstanceAz2(vpcParameters.getAz2()) .setCmsDbInstanceAz3(vpcParameters.getAz3()) @@ -86,9 +85,7 @@ public void run(final CreateDatabaseCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); - cloudFormationService.createStack(StackName.DATABASE.getFullName(environmentName), - parameters, ConfigConstants.DATABASE_STACK_TEMPLATE_PATH, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStack(Stack.DATABASE, parameters, true, command.getTagsDelegate().getTags()); configStore.storeCmsDatabasePassword(databasePassword); @@ -98,11 +95,11 @@ public void run(final CreateDatabaseCommand command) { public boolean isRunnable(final CreateDatabaseCommand command) { String environmentName = environmentMetadata.getName(); try { - cloudFormationService.getStackId(StackName.SECURITY_GROUPS.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); } catch (IllegalArgumentException iae) { throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); } - return !cloudFormationService.isStackPresent(StackName.DATABASE.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(Stack.DATABASE.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java index 2328157d..7d41cd36 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java @@ -19,7 +19,7 @@ import com.amazonaws.services.route53.model.RRType; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Route53Service; @@ -76,7 +76,7 @@ public boolean isRunnable(final CreateEdgeDomainRecordCommand command) { final String recordSetName = getEdgeDomainName(command.getBaseDomainName(), command.getEdgeDomainNameOverride()); try { - cloudFormationService.getStackId(StackName.ROUTE53.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.ROUTE53.getFullName(environmentName)); } catch (IllegalArgumentException iae) { throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index 6a6bcc1a..a6c99c30 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -18,12 +18,11 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; @@ -67,13 +66,13 @@ public void run(final CreateLoadBalancerCommand command) { final String environmentName = environmentMetadata.getName(); final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); - final String sslCertificateArn = configStore.getServerCertificateArn(StackName.CMS) + final String sslCertificateArn = configStore.getServerCertificateArn(Stack.CMS) .orElseThrow(() -> new IllegalStateException("Could not retrieve SSL certificate ARN!")); final LoadBalancerParameters loadBalancerParameters = new LoadBalancerParameters() .setVpcId(vpcOutputs.getVpcId()) .setSslCertificateArn(sslCertificateArn) - .setSgStackName(StackName.SECURITY_GROUPS.getFullName(environmentName)) + .setSgStackName(Stack.SECURITY_GROUPS.getFullName(environmentName)) .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); @@ -81,20 +80,18 @@ public void run(final CreateLoadBalancerCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); - cloudFormationService.createStack(StackName.LOAD_BALANCER.getFullName(environmentName), - parameters, ConfigConstants.LOAD_BALANCER_STACK_TEMPLATE_PATH, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStack(Stack.LOAD_BALANCER, parameters, true, command.getTagsDelegate().getTags()); } @Override public boolean isRunnable(final CreateLoadBalancerCommand command) { String environmentName = environmentMetadata.getName(); try { - cloudFormationService.getStackId(StackName.SECURITY_GROUPS.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); } catch (IllegalArgumentException iae) { throw new IllegalStateException("The security group stack must exist to create the load balancer!", iae); } - return ! cloudFormationService.isStackPresent(StackName.LOAD_BALANCER.getFullName(environmentName)); + return ! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index b2a86fb7..0ae5dad3 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -22,7 +22,7 @@ import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Route53Service; @@ -70,15 +70,13 @@ public void run(final CreateRoute53Command command) { final Route53Parameters route53Parameters = new Route53Parameters() .setHostedZoneId(command.getHostedZoneId()) .setLoadBalancerDomainName(getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride())) - .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)) + .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setOriginDomainName(getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride())); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); - cloudFormationService.createStack(StackName.ROUTE53.getFullName(environmentName), - parameters, ConfigConstants.ROUTE53_TEMPLATE_PATH, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStack(Stack.ROUTE53, parameters, true, command.getTagsDelegate().getTags()); } @Override @@ -87,10 +85,10 @@ public boolean isRunnable(final CreateRoute53Command command) { final String loadBalancerDomainName = getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride()); final String originDomainName = getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride()); - if (!cloudFormationService.isStackPresent(StackName.LOAD_BALANCER.getFullName(environmentName))) { + if (!cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!"); } - if (cloudFormationService.isStackPresent(StackName.ROUTE53.getFullName(environmentName))) { + if (cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName))) { throw new IllegalStateException("Route53 stack already exists."); } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 2fe8395e..96520378 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -18,12 +18,11 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; @@ -64,7 +63,6 @@ public CreateSecurityGroupsOperation(final EnvironmentMetadata environmentMetada @Override public void run(final CreateSecurityGroupsCommand command) { - final String environmentName = environmentMetadata.getName(); final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); final SecurityGroupParameters securityGroupParameters = new SecurityGroupParameters() @@ -74,15 +72,13 @@ public void run(final CreateSecurityGroupsCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); - cloudFormationService.createStack(StackName.SECURITY_GROUPS.getFullName(environmentName), - parameters, ConfigConstants.SECURITY_GROUPS_STACK_TEMPLATE_PATH, true, - command.getTagParameters().getTags()); + cloudFormationService.createStack(Stack.SECURITY_GROUPS, parameters, true, command.getTagParameters().getTags()); } @Override public boolean isRunnable(final CreateSecurityGroupsCommand command) { String environmentName = environmentMetadata.getName(); - return ! cloudFormationService.isStackPresent(StackName.SECURITY_GROUPS.getFullName(environmentName)); + return ! cloudFormationService.isStackPresent(Stack.SECURITY_GROUPS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index 832d9e22..2b97ac08 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -19,11 +19,10 @@ import com.beust.jcommander.internal.Maps; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.VpcParameters; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2Service; @@ -66,7 +65,6 @@ public CreateVpcOperation(final EnvironmentMetadata environmentMetadata, @Override public void run(final CreateVpcCommand command) { - final String environmentName = environmentMetadata.getName(); final Map azByIdentifier = mapAvailabilityZones(); final VpcParameters vpcParameters = new VpcParameters() @@ -77,15 +75,13 @@ public void run(final CreateVpcCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); - cloudFormationService.createStack(StackName.VPC.getFullName(environmentName), - parameters, ConfigConstants.VPC_STACK_TEMPLATE_PATH, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStack(Stack.VPC, parameters, true, command.getTagsDelegate().getTags()); } @Override public boolean isRunnable(final CreateVpcCommand command) { String environmentName = environmentMetadata.getName(); - return ! cloudFormationService.isStackPresent(StackName.VPC.getFullName(environmentName)); + return ! cloudFormationService.isStackPresent(Stack.VPC.getFullName(environmentName)); } private Map mapAvailabilityZones() { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index fbd87f9b..af504e96 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -18,11 +18,10 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.WafParameters; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; @@ -66,26 +65,24 @@ public void run(final CreateWafCommand command) { final String environmentName = environmentMetadata.getName(); final WafParameters wafParameters = new WafParameters() - .setLoadBalancerStackName(StackName.LOAD_BALANCER.getFullName(environmentName)) + .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setWafName("cerberus-" + environmentName + "-waf"); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); - cloudFormationService.createStack(StackName.WAF.getFullName(environmentName), - parameters, ConfigConstants.WAF_STACK_TEMPLATE_PATH, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStack(Stack.WAF, parameters, true, command.getTagsDelegate().getTags()); } @Override public boolean isRunnable(final CreateWafCommand command) { String environmentName = environmentMetadata.getName(); try { - cloudFormationService.getStackId(StackName.LOAD_BALANCER.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.LOAD_BALANCER.getFullName(environmentName)); } catch (IllegalArgumentException iae) { throw new IllegalStateException("The load balancer stack must exist to create the WAF!", iae); } - return ! cloudFormationService.isStackPresent(StackName.ROUTE53.getFullName(environmentName)); + return ! cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java index d9eb1b77..c9a3fbad 100644 --- a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java @@ -66,10 +66,10 @@ public PrintStackInfoOperation(final ConfigStore configStore, @Override public void run(final PrintStackInfoCommand command) { - final String stackId = configStore.getStackId(command.getStackName()); + final String stackId = configStore.getStackId(command.getStack()); if (StringUtils.isBlank(stackId) || !cloudFormationService.isStackPresent(stackId)) { - logger.error("The specified environment doesn't contain a stack for " + command.getStackName().getName()); + logger.error("The specified environment doesn't contain a stack for " + command.getStack().getName()); return; } diff --git a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java index 95ba1f30..108d8d8e 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java @@ -22,7 +22,7 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AutoScalingService; import com.nike.cerberus.service.CloudFormationService; @@ -56,7 +56,7 @@ public class RollingRebootWithHealthCheckOperation implements Operation HEALTH_CHECK_MAP = ImmutableMap.of( - StackName.CMS.getName(), "http://%s:8080/healthcheck" + Stack.CMS.getName(), "http://%s:8080/healthcheck" ); private final static int DEFAULT_HTTP_TIMEOUT = 15; @@ -103,8 +103,8 @@ public void run(final RollingRebootWithHealthCheckCommand command) { "If this command fails: the minimum instance size may need to be increased and an EC2 instance" + " may need to be set to 'in-service' state on the auto scaling group").yellow().toString()); - final StackName stackName = command.getStackName(); - final String stackId = configStore.getStackId(stackName); + final Stack stack = command.getStack(); + final String stackId = configStore.getStackId(stack); final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); final Map stackParameters = cloudFormationService.getStackParameters(stackId); @@ -121,7 +121,7 @@ public void run(final RollingRebootWithHealthCheckCommand command) { autoScalingService.updateMinInstancesForAutoScalingGroup(autoScalingGroupId, minInstances - 1); instances.forEach(instance -> { - rebootInstance(stackName, autoScalingGroupId, instance); + rebootInstance(stack, autoScalingGroupId, instance); }); logger.info("Increasing min instances for ASG: {}", autoScalingGroupId); @@ -131,9 +131,9 @@ public void run(final RollingRebootWithHealthCheckCommand command) { /** * Reboot an instance and make sure it comes back healthy */ - private void rebootInstance(StackName stackName, String autoScalingGroupId, Instance instance) { + private void rebootInstance(Stack stack, String autoScalingGroupId, Instance instance) { - final String healthCheckUrlTmpl = HEALTH_CHECK_MAP.get(stackName.getName()); + final String healthCheckUrlTmpl = HEALTH_CHECK_MAP.get(stack.getName()); final String healthCheckUrl = String.format(healthCheckUrlTmpl, instance.getPublicDnsName()); logger.info("Checking that instance health check is reachable..."); @@ -229,13 +229,13 @@ private int executeHealthCheck(final String healthCheckUrl) { @Override public boolean isRunnable(final RollingRebootWithHealthCheckCommand command) { - final StackName stackName = command.getStackName(); - final String stackNameStr = stackName.getName(); - final String stackId = configStore.getStackId(stackName); + final Stack stack = command.getStack(); + final String stackNameStr = stack.getName(); + final String stackId = configStore.getStackId(stack); final Map stackParameters = cloudFormationService.getStackParameters(stackId); if (! HEALTH_CHECK_MAP.containsKey(stackNameStr)) { - logger.error("Cannot reboot cluster: {}. Allowed stacks: {}", stackName, HEALTH_CHECK_MAP.keySet()); + logger.error("Cannot reboot cluster: {}. Allowed stacks: {}", stack, HEALTH_CHECK_MAP.keySet()); return false; } else if (! stackParameters.containsKey(MIN_INSTANCES_STACK_PARAMETER_KEY)) { logger.error("Could not find parameter 'minInstances' on stack: {}", stackId); diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 9a3e914f..bd9f2a5a 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -23,12 +23,11 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.GatewayParameters; import com.nike.cerberus.domain.cloudformation.LaunchConfigParameters; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.AmiTagCheckService; @@ -58,9 +57,7 @@ public class UpdateStackOperation implements Operation { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final Map> stackParameterMap; - - private final Map stackTemplatePathMap; + private final Map> stackParameterMap; private final ConfigStore configStore; @@ -85,23 +82,20 @@ public UpdateStackOperation(final ConfigStore configStore, this.amiTagCheckService = amiTagCheckService; stackParameterMap = new HashMap<>(); - stackParameterMap.put(StackName.CMS, CmsParameters.class); - stackParameterMap.put(StackName.GATEWAY, GatewayParameters.class); + stackParameterMap.put(Stack.CMS, CmsParameters.class); + stackParameterMap.put(Stack.GATEWAY, GatewayParameters.class); - stackTemplatePathMap = new HashMap<>(); - stackTemplatePathMap.put(StackName.BASE, ConfigConstants.BASE_STACK_TEMPLATE_PATH); - stackTemplatePathMap.put(StackName.CMS, ConfigConstants.CMS_STACK_TEMPLATE_PATH); } @Override public void run(final UpdateStackCommand command) { - final String stackId = configStore.getStackId(command.getStackName()); - final Class parametersClass = stackParameterMap.get(command.getStackName()); + final String stackId = configStore.getStackId(command.getStack()); + final Class parametersClass = stackParameterMap.get(command.getStack()); final Map parameters; if (parametersClass != null) { - parameters = getUpdateLaunchConfigParameters(command.getStackName(), command, parametersClass); - } else if (StackName.BASE == command.getStackName()) { + parameters = getUpdateLaunchConfigParameters(command.getStack(), command, parametersClass); + } else if (Stack.BASE == command.getStack()) { parameters = getUpdatedBaseStackParameters(command); } else { throw new IllegalArgumentException("The specified stack does not support the update stack command!"); @@ -109,22 +103,17 @@ public void run(final UpdateStackCommand command) { // Make sure the given AmiId is for this component. Check if it contains required tag // There is no AMI for Base. - if ( !command.isSkipAmiTagCheck() && StackName.BASE != command.getStackName() ) { - amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(),command.getStackName()); + if (!command.isSkipAmiTagCheck() && Stack.BASE != command.getStack()) { + amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), command.getStack()); } parameters.putAll(command.getDynamicParameters()); try { - logger.info("Starting the update for {}.", command.getStackName().getName()); + logger.info("Starting the update for {}.", command.getStack().getName()); - if (command.isOverwriteTemplate()) { - cloudFormationService.updateStack(stackId, parameters, stackTemplatePathMap.get(command.getStackName()), - true, command.getStackDelegate().getTagParameters().getTags()); - } else { - cloudFormationService.updateStack(stackId, parameters, true, - command.getStackDelegate().getTagParameters().getTags()); - } + cloudFormationService.updateStack(command.getStack(), parameters, + true, command.isOverwriteTemplate(), command.getStackDelegate().getTagParameters().getTags()); final StackStatus endStatus = cloudFormationService.waitForStatus(stackId, @@ -136,7 +125,7 @@ public void run(final UpdateStackCommand command) { UPDATE_ROLLBACK_FAILED )); - if (! ImmutableList.of(UPDATE_COMPLETE, UPDATE_COMPLETE_CLEANUP_IN_PROGRESS).contains(endStatus)) { + if (!ImmutableList.of(UPDATE_COMPLETE, UPDATE_COMPLETE_CLEANUP_IN_PROGRESS).contains(endStatus)) { final String errorMessage = String.format("CloudFormation reports that updating the stack was not successful. end status: %s", endStatus.name()); logger.error(errorMessage); @@ -157,7 +146,7 @@ public void run(final UpdateStackCommand command) { @Override public boolean isRunnable(final UpdateStackCommand command) { boolean isRunnable = true; - final String stackId = configStore.getStackId(command.getStackName()); + final String stackId = configStore.getStackId(command.getStack()); if (StringUtils.isBlank(stackId)) { logger.error("The stack name specified has not been created for this environment yet!"); @@ -170,13 +159,13 @@ public boolean isRunnable(final UpdateStackCommand command) { return isRunnable; } - private Map getUpdateLaunchConfigParameters(final StackName stackName, + private Map getUpdateLaunchConfigParameters(final Stack stack, final UpdateStackCommand command, final Class parametersClass) { final LaunchConfigParameters launchConfigParameters = - configStore.getStackParameters(stackName, parametersClass); + configStore.getStackParameters(stack, parametersClass); - launchConfigParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(stackName)); + launchConfigParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(stack)); if (StringUtils.isNotBlank(command.getStackDelegate().getAmiId())) { launchConfigParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); @@ -190,9 +179,7 @@ private Map getUpdateLaunchConfigParameters(final StackName stac launchConfigParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); } - - final TypeReference> typeReference = new TypeReference>() {}; - return cloudformationObjectMapper.convertValue(launchConfigParameters, typeReference); + return cloudformationObjectMapper.convertValue(launchConfigParameters, new TypeReference>() {}); } private Map getUpdatedBaseStackParameters(final UpdateStackCommand command) { diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index 50800c76..a4004a12 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -20,9 +20,8 @@ import com.google.common.collect.ImmutableSet; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.CertificateService; import com.nike.cerberus.service.IdentityManagementService; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.UuidSupplier; @@ -80,23 +79,23 @@ public UploadCertFilesOperation(final EnvironmentMetadata environmentMetadata, @Override public void run(final UploadCertFilesCommand command) { - final StackName stackName = command.getStackName(); + final Stack stack = command.getStack(); final Path certPath = command.getCertPath(); final String caContents = getFileContents(certPath, DOMAIN_CERT_CHAIN_FILE); final String certContents = getFileContents(certPath, DOMAIN_CERT_FILE); final String keyContents = getFileContents(certPath, DOMAIN_PKCS1_KEY_FILE); final String pkcs8KeyContents = getFileContents(certPath, DOMAIN_PKCS8_KEY_FILE); final String pubKeyContents = getFileContents(certPath, DOMAIN_PUBLIC_KEY_FILE); - final String certificateName = stackName.getName() + "_" + uuidSupplier.get(); + final String certificateName = stack.getName() + "_" + uuidSupplier.get(); - logger.info("Uploading certificate to IAM for {}, with name of {}.", stackName.getName(), certificateName); + logger.info("Uploading certificate to IAM for {}, with name of {}.", stack.getName(), certificateName); String id = identityManagementService.uploadServerCertificate(certificateName, getPath(), certContents, caContents, keyContents); logger.info("Cert ID: {}", id); logger.info("Uploading certificate parts to the configuration bucket."); - configStore.storeCert(stackName, certificateName, caContents, certContents, keyContents, pkcs8KeyContents, pubKeyContents); + configStore.storeCert(stack, certificateName, caContents, certContents, keyContents, pkcs8KeyContents, pubKeyContents); logger.info("Uploading certificate completed."); } @@ -108,7 +107,7 @@ public boolean isRunnable(final UploadCertFilesCommand command) { return false; } - final String serverCertificateName = configStore.getServerCertificateName(command.getStackName()); + final String serverCertificateName = configStore.getServerCertificateName(command.getStack()); if (StringUtils.isNotBlank(serverCertificateName) && !command.isOverwrite()) { logger.error("Certificate already uploaded for this stack and environment. Use --overwrite flag to force upload."); return false; diff --git a/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java b/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java index 54906e16..24fb414f 100644 --- a/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/WhitelistCidrForVpcAccessOperation.java @@ -26,7 +26,7 @@ import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; @@ -103,7 +103,7 @@ public boolean isRunnable(final WhitelistCidrForVpcAccessCommand command) { String environmentName = environmentMetadata.getName(); try { - cloudFormationService.getStackId(StackName.BASE.getFullName(environmentName)); + cloudFormationService.getStackId(Stack.BASE.getFullName(environmentName)); } catch (IllegalArgumentException iae) { logger.error("Could not create the CMS cluster." + "Make sure the load balancer, security group, and base stacks have all been created.", iae); diff --git a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java index 5e8744e8..694b5800 100644 --- a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java +++ b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java @@ -23,12 +23,10 @@ import com.amazonaws.services.ec2.model.Filter; import javax.inject.Inject; -import java.util.List; import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.ConfigConstants; /** @@ -38,14 +36,14 @@ public class AmiTagCheckService { private final AmazonEC2 ec2Client; - private final Map stackAmiTagValueMap; + private final Map stackAmiTagValueMap; @Inject public AmiTagCheckService(final AmazonEC2 ec2Client) { this.ec2Client = ec2Client; stackAmiTagValueMap = new HashMap<>(); - stackAmiTagValueMap.put(StackName.CMS, ConfigConstants.CMS_AMI_TAG_VALUE); + stackAmiTagValueMap.put(Stack.CMS, ConfigConstants.CMS_AMI_TAG_VALUE); } /** @@ -76,8 +74,8 @@ public boolean isAmiWithTagExist(final String amiId, final String tagName, final * * @return void */ - public void validateAmiTagForStack(final String amiId, final StackName stackName) { - if (!isAmiWithTagExist(amiId, ConfigConstants.CERBERUS_AMI_TAG_NAME, stackAmiTagValueMap.get(stackName) )) { + public void validateAmiTagForStack(final String amiId, final Stack stack) { + if (!isAmiWithTagExist(amiId, ConfigConstants.CERBERUS_AMI_TAG_NAME, stackAmiTagValueMap.get(stack) )) { throw new IllegalStateException(ConfigConstants.AMI_TAG_CHECK_ERROR_MESSAGE); } } diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index e7ed8d56..9880cf0c 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -27,7 +27,6 @@ import com.amazonaws.services.cloudformation.model.DescribeStacksResult; import com.amazonaws.services.cloudformation.model.Output; import com.amazonaws.services.cloudformation.model.Parameter; -import com.amazonaws.services.cloudformation.model.Stack; import com.amazonaws.services.cloudformation.model.StackEvent; import com.amazonaws.services.cloudformation.model.StackStatus; import com.amazonaws.services.cloudformation.model.Tag; @@ -39,7 +38,7 @@ import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.domain.EnvironmentMetadata; -import org.apache.commons.io.IOUtils; +import com.nike.cerberus.domain.environment.Stack; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -48,8 +47,6 @@ import javax.annotation.Nullable; import javax.inject.Inject; -import java.io.IOException; -import java.io.InputStream; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -85,22 +82,19 @@ public CloudFormationService(final AmazonCloudFormation cloudFormationClient, /** * Creates a new stack. * - * @param name Stack name. * @param parameters Input parameters. - * @param templatePath Classpath to the JSON template of the stack. * @return Stack ID */ - public String createStack(final String name, + public String createStack(final Stack stack, final Map parameters, - final String templatePath, final boolean iamCapabilities, final Map globalTags) { - logger.info(String.format("Executing the Cloud Formation: %s, Stack Name: %s", templatePath, name)); + logger.info(String.format("Executing the Cloud Formation: %s, Stack Name: %s", stack.getTemplatePath(), stack.getFullName(environmentMetadata.getName()))); final CreateStackRequest request = new CreateStackRequest() - .withStackName(name) + .withStackName(stack.getFullName(environmentMetadata.getName())) .withParameters(convertParameters(parameters)) - .withTemplateBody(getTemplateText(templatePath)) + .withTemplateBody(stack.getTemplateText()) .withTags(getTags(globalTags)); if (iamCapabilities) { @@ -111,40 +105,22 @@ public String createStack(final String name, return result.getStackId(); } - /** - * Updates an existing stack by name. - * - * @param stackId - * @param parameters - * @param iamCapabilities - */ - public void updateStack(final String stackId, - final Map parameters, - final boolean iamCapabilities, - final Map globalTags) { - - updateStack(stackId, parameters, null, iamCapabilities, globalTags); - } /** - * Updates an existing stack by name. - * - * @param stackId Stack ID. - * @param parameters Input parameters. - * @param templatePath Path to the JSON template of the stack. + * Updates an existing stack */ - public void updateStack(final String stackId, + public void updateStack(final Stack stack, final Map parameters, - final String templatePath, final boolean iamCapabilities, + final boolean overwrite, final Map globalTags) { final UpdateStackRequest request = new UpdateStackRequest() - .withStackName(stackId) + .withStackName(stack.getFullName(environmentMetadata.getName())) .withParameters(convertParameters(parameters)); - if (StringUtils.isNotBlank(templatePath)) { - request.withTemplateBody(getTemplateText(templatePath)); + if (overwrite) { + request.withTemplateBody(stack.getTemplateText()); } else { request.withUsePreviousTemplate(true); } @@ -272,7 +248,7 @@ public String getStackId(String stackName) { new DescribeStacksRequest() .withStackName(stackName)); - List stacks = result.getStacks(); + List stacks = result.getStacks(); if (stacks.isEmpty()) { throw new IllegalArgumentException("No stack found with name: " + stackName); } else if (stacks.size() > 1) { @@ -315,28 +291,7 @@ public Collection convertParameters(final Map paramet return parameterList; } - /** - * Gets the template contents from the file on the classpath. - * - * @param templatePath Classpath for the template to be read - * @return Template contents - */ - public String getTemplateText(final String templatePath) { - final InputStream templateStream = getClass().getResourceAsStream(templatePath); - if (templateStream == null) { - throw new IllegalStateException( - String.format("The CloudFormation JSON template doesn't exist on the classpath. path: %s", templatePath)); - } - - try { - return IOUtils.toString(templateStream, ConfigConstants.DEFAULT_ENCODING); - } catch (final IOException e) { - final String errorMessage = String.format("Unable to read input stream from %s", templatePath); - logger.error(errorMessage); - throw new RuntimeException(errorMessage, e); - } - } /** * Since there doesn't appear to be a first class way through the SDK at this time to get a CF export. We can @@ -352,7 +307,7 @@ public Optional searchStacksForOutput(String outputKey) { request.withNextToken(describeStacksResult.getNextToken()); } describeStacksResult = cloudFormationClient.describeStacks(); - for (Stack stack : describeStacksResult.getStacks()) { + for (com.amazonaws.services.cloudformation.model.Stack stack : describeStacksResult.getStacks()) { for (Output output : stack.getOutputs()) { if (StringUtils.equals(output.getOutputKey(), outputKey)) { return Optional.of(output.getOutputValue()); diff --git a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java index 6e53420e..3796314a 100644 --- a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java +++ b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java @@ -19,7 +19,7 @@ import com.google.common.collect.Maps; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.store.ConfigStore; import javax.inject.Inject; @@ -42,19 +42,18 @@ public Ec2UserDataService(final EnvironmentMetadata environmentMetadata, this.configStore = configStore; } - public String getUserData(final StackName stackName) { - switch (stackName) { - case CMS: - return getCmsUserData(); - default: - throw new IllegalArgumentException("The stack specified does not support user data. stack: " - + stackName.getName()); + public String getUserData(final Stack stack) { + if (stack.equals(Stack.CMS)) { + return getCmsUserData(); + } else { + throw new IllegalArgumentException("The stack specified does not support user data. stack: " + + stack.getName()); } } private String getCmsUserData() { final Map userDataMap = Maps.newHashMap(); - addStandardEnvironmentVariables(userDataMap, StackName.CMS.getName()); + addStandardEnvironmentVariables(userDataMap, Stack.CMS.getName()); return encodeUserData(writeExportEnvVars(userDataMap)); } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index aba557db..716302a2 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -47,7 +47,7 @@ import com.nike.cerberus.domain.environment.BackupRegionInfo; import com.nike.cerberus.domain.environment.Environment; import com.nike.cerberus.domain.environment.Secrets; -import com.nike.cerberus.domain.environment.StackName; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.IdentityManagementService; import com.nike.cerberus.service.S3StoreService; @@ -164,13 +164,13 @@ public void storeAzs(final String az1, final String az2, final String az3) { /** * Stores the stack ID for a specific component. * - * @param stackName Stack component + * @param stack Stack component * @param stackId Stack ID */ - public void storeStackId(final StackName stackName, final String stackId) { + public void storeStackId(final Stack stack, final String stackId) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - environment.getStackMap().put(stackName, stackId); + environment.getStackMap().put(stack, stackId); saveEnvironmentData(environment); } } @@ -178,41 +178,41 @@ public void storeStackId(final StackName stackName, final String stackId) { /** * Get the specific stack ID by component name. * - * @param stackName Stack name + * @param stack Stack name * @return Stack ID */ - public String getStackId(final StackName stackName) { + public String getStackId(final Stack stack) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - return environment.getStackMap().get(stackName); + return environment.getStackMap().get(stack); } } /** * Gets the server certificate name from the config store. * - * @param stackName Stack name + * @param stack Stack name */ - public String getServerCertificateName(final StackName stackName) { + public String getServerCertificateName(final Stack stack) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - return environment.getServerCertificateIdMap().get(stackName); + return environment.getServerCertificateIdMap().get(stack); } } /** * Gets the server certificate ARN for the stack name. * - * @param stackName Stack name + * @param stack Stack name * @return ARN */ - public Optional getServerCertificateArn(final StackName stackName) { - final String certificateName = getServerCertificateName(stackName); + public Optional getServerCertificateArn(final Stack stack) { + final String certificateName = getServerCertificateName(stack); return iamService.getServerCertificateArn(certificateName); } - public Optional getServerCertificateId(final StackName stackName) { - final String certificateName = getServerCertificateName(stackName); + public Optional getServerCertificateId(final Stack stack) { + final String certificateName = getServerCertificateName(stack); return iamService.getServerCertificateId(certificateName); } @@ -335,40 +335,40 @@ public String getVaultRootToken() { /** * Returns the contents of a specific certificate part that's been uploaded for a stack. * - * @param stackName + * @param stack * @param part * @return */ - public Optional getCertPart(final StackName stackName, final String part) { - return getEncryptedObject(buildCertFilePath(stackName, part)); + public Optional getCertPart(final Stack stack, final String part) { + return getEncryptedObject(buildCertFilePath(stack, part)); } /** * Stores the certificate files encrypted and adds the certificate name to the environment data. * - * @param stackName Stack that the cert is for + * @param stack Stack that the cert is for * @param certificateName Certificate name in IAM * @param caContents CA chain * @param certContents Certificate body * @param keyContents Certificate key * @param pubKeyContents Certificate public key */ - public void storeCert(final StackName stackName, + public void storeCert(final Stack stack, final String certificateName, final String caContents, final String certContents, final String keyContents, final String pkcs8KeyContents, final String pubKeyContents) { - saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_CA), caContents); - saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_CERT), certContents); - saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_KEY), keyContents); - saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_PKCS8_KEY), pkcs8KeyContents); - saveEncryptedObject(buildCertFilePath(stackName, CERT_PART_PUBKEY), pubKeyContents); + saveEncryptedObject(buildCertFilePath(stack, CERT_PART_CA), caContents); + saveEncryptedObject(buildCertFilePath(stack, CERT_PART_CERT), certContents); + saveEncryptedObject(buildCertFilePath(stack, CERT_PART_KEY), keyContents); + saveEncryptedObject(buildCertFilePath(stack, CERT_PART_PKCS8_KEY), pkcs8KeyContents); + saveEncryptedObject(buildCertFilePath(stack, CERT_PART_PUBKEY), pubKeyContents); synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - environment.getServerCertificateIdMap().put(stackName, certificateName); + environment.getServerCertificateIdMap().put(stack, certificateName); saveEnvironmentData(environment); } } @@ -498,8 +498,8 @@ public Optional getConfigProperties(String path) { return getEncryptedObject(path); } - public String getStackLogicalId(StackName stackName) { - return stackName.getFullName(environmentMetadata.getName()); + public String getStackLogicalId(Stack stack) { + return stack.getFullName(environmentMetadata.getName()); } /** @@ -508,7 +508,7 @@ public String getStackLogicalId(StackName stackName) { * @return Base parameters */ public BaseParameters getBaseStackParameters() { - return getStackParameters(getStackLogicalId(StackName.BASE), BaseParameters.class); + return getStackParameters(getStackLogicalId(Stack.BASE), BaseParameters.class); } /** @@ -517,7 +517,7 @@ public BaseParameters getBaseStackParameters() { * @return Base outputs */ public BaseOutputs getBaseStackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.BASE), BaseOutputs.class); + return getStackOutputs(getStackLogicalId(Stack.BASE), BaseOutputs.class); } /** @@ -526,7 +526,7 @@ public BaseOutputs getBaseStackOutputs() { * @return Base parameters */ public VpcParameters getVpcStackParameters() { - return getStackParameters(getStackLogicalId(StackName.VPC), VpcParameters.class); + return getStackParameters(getStackLogicalId(Stack.VPC), VpcParameters.class); } /** @@ -535,7 +535,7 @@ public VpcParameters getVpcStackParameters() { * @return Base outputs */ public VpcOutputs getVpcStackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.VPC), VpcOutputs.class); + return getStackOutputs(getStackLogicalId(Stack.VPC), VpcOutputs.class); } /** @@ -544,7 +544,7 @@ public VpcOutputs getVpcStackOutputs() { * @return Base parameters */ public SecurityGroupParameters getSecurityGroupStackParameters() { - return getStackParameters(getStackLogicalId(StackName.SECURITY_GROUPS), SecurityGroupParameters.class); + return getStackParameters(getStackLogicalId(Stack.SECURITY_GROUPS), SecurityGroupParameters.class); } /** @@ -553,7 +553,7 @@ public SecurityGroupParameters getSecurityGroupStackParameters() { * @return Base outputs */ public SecurityGroupOutputs getSecurityGroupStackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.SECURITY_GROUPS), SecurityGroupOutputs.class); + return getStackOutputs(getStackLogicalId(Stack.SECURITY_GROUPS), SecurityGroupOutputs.class); } /** @@ -562,7 +562,7 @@ public SecurityGroupOutputs getSecurityGroupStackOutputs() { * @return Base outputs */ public LoadBalancerOutputs getLoadBalancerStackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.LOAD_BALANCER), LoadBalancerOutputs.class); + return getStackOutputs(getStackLogicalId(Stack.LOAD_BALANCER), LoadBalancerOutputs.class); } /** @@ -571,7 +571,7 @@ public LoadBalancerOutputs getLoadBalancerStackOutputs() { * @return Base parameters */ public DatabaseParameters getDatabaseStackParameters() { - return getStackParameters(getStackLogicalId(StackName.DATABASE), DatabaseParameters.class); + return getStackParameters(getStackLogicalId(Stack.DATABASE), DatabaseParameters.class); } /** @@ -579,8 +579,8 @@ public DatabaseParameters getDatabaseStackParameters() { * * @return Base parameters */ - public Route53Parameters getRoute53StackParameters() { - return getStackParameters(getStackLogicalId(StackName.ROUTE53), Route53Parameters.class); + public Route53Parameters getRoute53Parameters() { + return getStackParameters(getStackLogicalId(Stack.ROUTE53), Route53Parameters.class); } /** @@ -589,7 +589,7 @@ public Route53Parameters getRoute53StackParameters() { * @return Base parameters */ public Route53Outputs getRoute53StackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.ROUTE53), Route53Outputs.class); + return getStackOutputs(getStackLogicalId(Stack.ROUTE53), Route53Outputs.class); } /** @@ -598,7 +598,7 @@ public Route53Outputs getRoute53StackOutputs() { * @return Base outputs */ public DatabaseOutputs getDatabaseStackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.DATABASE), DatabaseOutputs.class); + return getStackOutputs(getStackLogicalId(Stack.DATABASE), DatabaseOutputs.class); } /** @@ -607,7 +607,7 @@ public DatabaseOutputs getDatabaseStackOutputs() { * @return Vault outputs */ public VaultOutputs getVaultStackOutputs() { - return getStackOutputs(StackName.VAULT, VaultOutputs.class); + return getStackOutputs(Stack.VAULT, VaultOutputs.class); } /** @@ -616,7 +616,7 @@ public VaultOutputs getVaultStackOutputs() { * @return CMS parameters */ public CmsParameters getCmsStackParamters() { - return getStackParameters(getStackLogicalId(StackName.CMS), CmsParameters.class); + return getStackParameters(getStackLogicalId(Stack.CMS), CmsParameters.class); } /** @@ -625,7 +625,7 @@ public CmsParameters getCmsStackParamters() { * @return CMS outputs */ public CmsOutputs getCmsStackOutputs() { - return getStackOutputs(getStackLogicalId(StackName.CMS), CmsOutputs.class); + return getStackOutputs(getStackLogicalId(Stack.CMS), CmsOutputs.class); } /** @@ -634,21 +634,21 @@ public CmsOutputs getCmsStackOutputs() { * @return Gateway parameters */ public GatewayParameters getGatewayStackParamters() { - return getStackParameters(StackName.GATEWAY, GatewayParameters.class); + return getStackParameters(Stack.GATEWAY, GatewayParameters.class); } /** * Get the stack outputs for a specific stack name. * - * @param stackName Stack name + * @param stack Stack name * @param outputClass Outputs class * @param Outputs type * @return Outputs */ - public M getStackOutputs(final StackName stackName, final Class outputClass) { + public M getStackOutputs(final Stack stack, final Class outputClass) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - final String stackId = environment.getStackMap().get(stackName); + final String stackId = environment.getStackMap().get(stack); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("The specified stack doesn't exist for specified environment."); @@ -681,15 +681,15 @@ public M getStackOutputs(final String stackName, final Class outputClass) /** * Get the stack parameters for a specific stack name. * - * @param stackName Stack name + * @param stack Stack name * @param parameterClass Parameters class * @param Parameters type * @return Parameters */ - public M getStackParameters(final StackName stackName, final Class parameterClass) { + public M getStackParameters(final Stack stack, final Class parameterClass) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - final String stackId = environment.getStackMap().get(stackName); + final String stackId = environment.getStackMap().get(stack); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("The specified stack doesn't exist for the specified environment"); @@ -859,8 +859,8 @@ private void initConfigStoreService() { } } - private String buildCertFilePath(final StackName stackName, final String suffix) { - return "data/" + stackName.getName() + "/" + stackName.getName() + "-" + suffix; + private String buildCertFilePath(final Stack stack, final String suffix) { + return "data/" + stack.getName() + "/" + stack.getName() + "-" + suffix; } /** diff --git a/src/main/java/com/nike/cerberus/util/StackConverter.java b/src/main/java/com/nike/cerberus/util/StackConverter.java new file mode 100644 index 00000000..1360bd20 --- /dev/null +++ b/src/main/java/com/nike/cerberus/util/StackConverter.java @@ -0,0 +1,15 @@ +package com.nike.cerberus.util; + +import com.beust.jcommander.IStringConverter; +import com.nike.cerberus.domain.environment.Stack; + +/** + * JCommander converter + */ +public class StackConverter implements IStringConverter { + + @Override + public Stack convert(String value) { + return Stack.fromName(value); + } +} \ No newline at end of file From acb2863c0d272bf0e6ab6bee4f4c98418198cbb4 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Fri, 10 Nov 2017 16:16:05 -0800 Subject: [PATCH 25/69] Adding equals and hashcode --- .../cerberus/domain/environment/Stack.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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 91f6bf5a..8c326f35 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -106,4 +106,22 @@ public static Stack fromName(final String name) { throw new IllegalArgumentException("Unknown stack name: " + name); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Stack stack = (Stack) o; + + if (!name.equals(stack.name)) return false; + return templatePath.equals(stack.templatePath); + + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + templatePath.hashCode(); + return result; + } } From 5b91d9cc1540293deda0b838d921c9e3d6fd644a Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Mon, 13 Nov 2017 15:15:05 -0800 Subject: [PATCH 26/69] Fixing update-stack command (#88) --- .../command/core/UpdateStackCommand.java | 23 ++-- .../cerberus/domain/environment/Stack.java | 42 +++++--- .../operation/core/UpdateStackOperation.java | 101 ++++-------------- 3 files changed, 53 insertions(+), 113 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java index f033e6ad..61439324 100644 --- a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java @@ -21,7 +21,7 @@ import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; -import com.nike.cerberus.command.StackDelegate; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.UpdateStackOperation; @@ -31,8 +31,7 @@ import java.util.Map; import static com.nike.cerberus.command.core.UpdateStackCommand.COMMAND_NAME; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_ARG; -import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_DESCRIPTION; +import static com.nike.cerberus.domain.environment.Stack.ALL_STACK_NAMES; /** @@ -49,16 +48,16 @@ public class UpdateStackCommand implements Command { private Stack stack; @ParametersDelegate - private StackDelegate stackDelegate = new StackDelegate(); + private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); + + public TagParametersDelegate getTagsDelegate() { + return tagsDelegate; + } @Parameter(names = OVERWRITE_TEMPLATE_LONG_ARG, description = "Flag for overwriting existing CloudFormation template") private boolean overwriteTemplate; - @Parameter(names = SKIP_AMI_TAG_CHECK_ARG, - description = SKIP_AMI_TAG_CHECK_DESCRIPTION) - private boolean skipAmiTagCheck; - @DynamicParameter(names = PARAMETER_SHORT_ARG, description = "Dynamic parameters for overriding the values for specific parameters in the CloudFormation.") private Map dynamicParameters = new HashMap<>(); @@ -66,10 +65,6 @@ public Stack getStack() { return stack; } - public StackDelegate getStackDelegate() { - return stackDelegate; - } - public boolean isOverwriteTemplate() { return overwriteTemplate; } @@ -78,10 +73,6 @@ public Map getDynamicParameters() { return dynamicParameters; } - public boolean isSkipAmiTagCheck() { - return skipAmiTagCheck; - } - @Override public String getCommandName() { return COMMAND_NAME; 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 8c326f35..adaf1984 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -25,25 +25,28 @@ import java.io.IOException; import java.io.InputStream; +import java.util.stream.Collectors; /** * Describes the stacks that make up Cerberus. */ public class Stack { - public static final Stack BASE = new Stack("base", "base.yaml"); - public static final Stack VAULT = new Stack("vault", null); - public static final Stack CMS = new Stack("cms", "cms-cluster.yaml"); - public static final Stack GATEWAY = new Stack("gateway", null); - public static final Stack VPC = new Stack("vpc", "vpc.yaml"); - public static final Stack DATABASE = new Stack("database", "database.yaml"); - public static final Stack SECURITY_GROUPS = new Stack("security-groups", "security-groups.yaml"); - public static final Stack LOAD_BALANCER = new Stack("load-balancer", "load-balancer.yaml"); - public static final Stack ROUTE53 = new Stack("route53", "route53.yaml"); - public static final Stack WAF = new Stack("web-app-firewall", "web-app-firewall.yaml"); + public static final Stack BASE = new Stack("base", "base.yaml", false); + public static final Stack VAULT = new Stack("vault", null, false); + public static final Stack CMS = new Stack("cms", "cms-cluster.yaml", true); + public static final Stack GATEWAY = new Stack("gateway", null, false); + public static final Stack VPC = new Stack("vpc", "vpc.yaml", false); + public static final Stack DATABASE = new Stack("database", "database.yaml", false); + public static final Stack SECURITY_GROUPS = new Stack("security-groups", "security-groups.yaml", false); + public static final Stack LOAD_BALANCER = new Stack("load-balancer", "load-balancer.yaml", false); + public static final Stack ROUTE53 = new Stack("route53", "route53.yaml", false); + public static final Stack WAF = new Stack("web-app-firewall", "web-app-firewall.yaml", false); public static final ImmutableList ALL_STACKS = ImmutableList.of(BASE, VAULT, CMS, GATEWAY, VPC, DATABASE, SECURITY_GROUPS, LOAD_BALANCER, ROUTE53, WAF); + public static final ImmutableList ALL_STACK_NAMES = ImmutableList.copyOf(ALL_STACKS.stream().map(Stack::getName).collect(Collectors.toList())); + private static final String TEMPLATE_PATH_ROOT = "/cloudformation/"; private final Logger logger = LoggerFactory.getLogger(Stack.class); @@ -51,19 +54,30 @@ public class Stack { private final String name; private final String templatePath; + private final boolean needsUserData; + - private Stack(final String name, final String cloudFormationFileName) { + private Stack(final String name, + final String cloudFormationFileName, + final boolean needsUserData) { this.name = name; this.templatePath = TEMPLATE_PATH_ROOT + cloudFormationFileName; + this.needsUserData = needsUserData; } public String getName() { return name; } + public String getTemplatePath() { return templatePath; } + + public boolean needsUserData() { + return needsUserData; + } + /** * Gets the template contents from the file on the classpath. * @@ -74,7 +88,7 @@ public String getTemplateText() { if (templateStream == null) { throw new IllegalStateException( - String.format("The CloudFormation JSON template doesn't exist on the classpath. path: %s", templatePath)); + String.format("The CloudFormation JSON template doesn't exist on the classpath for stack %s, path: %s", name, templatePath)); } try { @@ -98,12 +112,12 @@ public String getFullName(String environmentName) { public static Stack fromName(final String name) { for (Stack stack : ALL_STACKS) { + // ignore case and all enum style stack names if (stack.getName().equalsIgnoreCase(StringUtils.replaceAll(name,"_", "-"))) { return stack; } } - - throw new IllegalArgumentException("Unknown stack name: " + name); + throw new IllegalArgumentException("Unknown stack name '" + name + "', please choose from " + ALL_STACK_NAMES); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index bd9f2a5a..7bd82d40 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -18,29 +18,19 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.services.cloudformation.model.StackStatus; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.domain.cloudformation.CmsParameters; -import com.nike.cerberus.domain.cloudformation.GatewayParameters; -import com.nike.cerberus.domain.cloudformation.LaunchConfigParameters; -import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; -import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; -import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javax.inject.Named; -import java.util.HashMap; import java.util.Map; import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_COMPLETE; @@ -48,72 +38,47 @@ import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_COMPLETE; import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS; import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_FAILED; -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** * Operation for updating stacks. */ public class UpdateStackOperation implements Operation { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final Map> stackParameterMap; - private final ConfigStore configStore; + private final Logger logger = LoggerFactory.getLogger(getClass()); private final CloudFormationService cloudFormationService; - - private final ObjectMapper cloudformationObjectMapper; - private final Ec2UserDataService ec2UserDataService; - - private final AmiTagCheckService amiTagCheckService; + private final EnvironmentMetadata environmentMetadata; @Inject - public UpdateStackOperation(final ConfigStore configStore, - final CloudFormationService cloudFormationService, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper, + public UpdateStackOperation(final CloudFormationService cloudFormationService, final Ec2UserDataService ec2UserDataService, - final AmiTagCheckService amiTagCheckService) { - this.configStore = configStore; + final EnvironmentMetadata environmentMetadata) { this.cloudFormationService = cloudFormationService; - this.cloudformationObjectMapper = cloudformationObjectMapper; this.ec2UserDataService = ec2UserDataService; - this.amiTagCheckService = amiTagCheckService; + this.environmentMetadata = environmentMetadata; - stackParameterMap = new HashMap<>(); - stackParameterMap.put(Stack.CMS, CmsParameters.class); - stackParameterMap.put(Stack.GATEWAY, GatewayParameters.class); } @Override public void run(final UpdateStackCommand command) { - final String stackId = configStore.getStackId(command.getStack()); - final Class parametersClass = stackParameterMap.get(command.getStack()); - final Map parameters; - - if (parametersClass != null) { - parameters = getUpdateLaunchConfigParameters(command.getStack(), command, parametersClass); - } else if (Stack.BASE == command.getStack()) { - parameters = getUpdatedBaseStackParameters(command); - } else { - throw new IllegalArgumentException("The specified stack does not support the update stack command!"); - } + final String stackId = command.getStack().getFullName(environmentMetadata.getName()); - // Make sure the given AmiId is for this component. Check if it contains required tag - // There is no AMI for Base. - if (!command.isSkipAmiTagCheck() && Stack.BASE != command.getStack()) { - amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), command.getStack()); - } + final Map parameters = cloudFormationService.getStackParameters(stackId); + // only some stacks need user data + if (command.getStack().needsUserData()) { + parameters.put("userData", ec2UserDataService.getUserData(command.getStack())); + } parameters.putAll(command.getDynamicParameters()); try { - logger.info("Starting the update for {}.", command.getStack().getName()); + logger.info("Starting the update for '{}' overwrite:{}.", stackId, command.isOverwriteTemplate()); - cloudFormationService.updateStack(command.getStack(), parameters, - true, command.isOverwriteTemplate(), command.getStackDelegate().getTagParameters().getTags()); + cloudFormationService.updateStack(command.getStack(), parameters, true, command.isOverwriteTemplate(), + command.getTagsDelegate().getTags()); final StackStatus endStatus = cloudFormationService.waitForStatus(stackId, @@ -146,43 +111,13 @@ public void run(final UpdateStackCommand command) { @Override public boolean isRunnable(final UpdateStackCommand command) { boolean isRunnable = true; - final String stackId = configStore.getStackId(command.getStack()); - if (StringUtils.isBlank(stackId)) { - logger.error("The stack name specified has not been created for this environment yet!"); - isRunnable = false; - } else if (!cloudFormationService.isStackPresent(stackId)) { - logger.error("CloudFormation doesn't have the specified stack: {}", stackId); + String fullName = command.getStack().getFullName(environmentMetadata.getName()); + if (!cloudFormationService.isStackPresent(fullName)) { + logger.error("CloudFormation doesn't have the specified stack: {}", fullName); isRunnable = false; } return isRunnable; } - - private Map getUpdateLaunchConfigParameters(final Stack stack, - final UpdateStackCommand command, - final Class parametersClass) { - final LaunchConfigParameters launchConfigParameters = - configStore.getStackParameters(stack, parametersClass); - - launchConfigParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(stack)); - - if (StringUtils.isNotBlank(command.getStackDelegate().getAmiId())) { - launchConfigParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); - } - - if (StringUtils.isNotBlank(command.getStackDelegate().getInstanceSize())) { - launchConfigParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); - } - - if (StringUtils.isNotBlank(command.getStackDelegate().getKeyPairName())) { - launchConfigParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - } - - return cloudformationObjectMapper.convertValue(launchConfigParameters, new TypeReference>() {}); - } - - private Map getUpdatedBaseStackParameters(final UpdateStackCommand command) { - return Maps.newHashMap(); - } } \ No newline at end of file From 06acd572a6f0f088917b99ac0eea7f6f0cbbb857 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Mon, 13 Nov 2017 15:17:27 -0800 Subject: [PATCH 27/69] Adding TODOs --- .../cerberus/operation/core/UpdateStackOperation.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 7bd82d40..3beb9fd8 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -22,6 +22,7 @@ import com.google.common.collect.Sets; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; @@ -72,6 +73,13 @@ public void run(final UpdateStackCommand command) { if (command.getStack().needsUserData()) { parameters.put("userData", ec2UserDataService.getUserData(command.getStack())); } + + if (Stack.CMS.equals(command.getStack())) { + // TODO: tag check + } else if (Stack.DATABASE.equals(command.getStack())) { + // TODO: implement storing password if it was changed + //configStore.storeCmsDatabasePassword(databasePassword); + } parameters.putAll(command.getDynamicParameters()); try { From 755d5f1862afbc9d4ab9f8ef902f273282826471 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Tue, 14 Nov 2017 08:19:19 -0800 Subject: [PATCH 28/69] Removing unused code (#89) --- .../nike/cerberus/module/CerberusModule.java | 7 --- .../com/nike/cerberus/util/TokenSupplier.java | 41 ---------------- .../nike/cerberus/util/TokenSupplierTest.java | 48 ------------------- 3 files changed, 96 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/util/TokenSupplier.java delete mode 100644 src/test/java/com/nike/cerberus/util/TokenSupplierTest.java diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index 78fde60a..40a1412e 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -62,7 +62,6 @@ import com.nike.cerberus.command.ProxyDelegate; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.input.EnvironmentConfig; -import com.nike.cerberus.util.TokenSupplier; import com.nike.cerberus.util.UuidSupplier; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -186,12 +185,6 @@ public MustacheFactory mustacheFactory() { return new DefaultMustacheFactory(); } - @Provides - @Singleton - public TokenSupplier tokenSupplier() { - return new TokenSupplier(); - } - @Provides @Singleton public UuidSupplier uuidSupplier() { diff --git a/src/main/java/com/nike/cerberus/util/TokenSupplier.java b/src/main/java/com/nike/cerberus/util/TokenSupplier.java deleted file mode 100644 index 86fb07ae..00000000 --- a/src/main/java/com/nike/cerberus/util/TokenSupplier.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2016 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.util; - -import java.security.SecureRandom; -import java.util.Base64; -import java.util.function.Supplier; - -/** - * Supplier for a Base64 encoded string of a random 16 bytes. - */ -public class TokenSupplier implements Supplier { - - SecureRandom secureRandom = new SecureRandom(); - - /** - * Supplies a Base64 encoded string of a random 16 bytes. - * - * @return Base64 encoded string - */ - @Override - public String get() { - byte[] bytes = new byte[16]; - secureRandom.nextBytes(bytes); - return Base64.getEncoder().encodeToString(bytes); - } -} diff --git a/src/test/java/com/nike/cerberus/util/TokenSupplierTest.java b/src/test/java/com/nike/cerberus/util/TokenSupplierTest.java deleted file mode 100644 index 1f195d34..00000000 --- a/src/test/java/com/nike/cerberus/util/TokenSupplierTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2016 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.util; - -import com.beust.jcommander.internal.Sets; -import org.junit.Test; - -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -public class TokenSupplierTest { - - @Test - public void sanityTestGet() { - - TokenSupplier supplier = new TokenSupplier(); - - Set previous = Sets.newHashSet(); - for (int i = 0; i < 1000; i++) { - String current = supplier.get(); - - // not the same - assertFalse(previous.contains(current)); - - // check length - assertEquals(24, current.length()); - - previous.add(current); - } - } - -} \ No newline at end of file From e5709b431180668bd64565d9c81d8449806fd337 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 10:21:20 -0800 Subject: [PATCH 29/69] =?UTF-8?q?Make=20every=20CF=20Stack=20base=20comman?= =?UTF-8?q?d=20wait=20for=20cloud=20formation=20to=20finish=20a=E2=80=A6?= =?UTF-8?q?=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make every CF Stack base command wait for cloud formation to finish and exit with bad status code if expected CF status is not achieved, plus some additional clean up from various refactoring efforts. --- .../cli/EnvironmentConfigToArgsMapper.java | 68 +-------- .../core/CreateSecurityGroupsCommand.java | 10 -- .../domain/cloudformation/CmsParameters.java | 12 -- .../LoadBalancerParameters.java | 12 -- .../SecurityGroupParameters.java | 11 -- .../domain/environment/Environment.java | 73 +--------- .../cerberus/domain/input/CerberusStack.java | 87 ----------- .../domain/input/EnvironmentConfig.java | 18 --- .../nike/cerberus/domain/input/Gateway.java | 4 - .../domain/input/ManagementService.java | 74 +++++++++- .../cerberus/domain/input/SecurityGroups.java | 2 +- .../com/nike/cerberus/domain/input/Vault.java | 24 ---- .../operation/core/CreateBaseOperation.java | 23 ++- .../core/CreateDatabaseOperation.java | 15 +- .../core/CreateLoadBalancerOperation.java | 14 +- .../core/CreateRoute53Operation.java | 15 +- .../core/CreateSecurityGroupsOperation.java | 17 ++- .../operation/core/CreateVpcOperation.java | 13 +- .../operation/core/CreateWafOperation.java | 14 +- .../core/PrintStackInfoOperation.java | 11 +- ...RollingRebootWithHealthCheckOperation.java | 10 +- .../com/nike/cerberus/store/ConfigStore.java | 136 +----------------- .../cloudformation/security-groups.yaml | 10 +- src/main/resources/cloudformation/vpc.yaml | 1 - 24 files changed, 200 insertions(+), 474 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/domain/input/CerberusStack.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/Gateway.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/Vault.java diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 12f145e4..332813e9 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -16,7 +16,6 @@ package com.nike.cerberus.cli; -import com.nike.cerberus.command.StackDelegate; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; @@ -27,11 +26,8 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; -import com.nike.cerberus.domain.input.CerberusStack; import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.domain.input.ManagementService; import com.nike.cerberus.domain.input.VpcAccessWhitelist; @@ -91,8 +87,6 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig return getWhitelistCidrForVpcAccessCommandArgs(environmentConfig); case CreateCmsConfigCommand.COMMAND_NAME: return getCreateCmsConfigCommandArgs(environmentConfig); - case UpdateStackCommand.COMMAND_NAME: - return getUpdateStackCommandArgs(environmentConfig, passedArgs); case UpdateCmsConfigCommand.COMMAND_NAME: return getCreateCmsConfigCommandArgs(environmentConfig); case CreateVpcCommand.COMMAND_NAME: @@ -149,20 +143,12 @@ private static List getWhitelistCidrForVpcAccessCommandArgs(EnvironmentC private static List getCreateCmsClusterCommandArgs(EnvironmentConfig environmentConfig) { List args = new LinkedList<>(); - ManagementService component = environmentConfig.getManagementService(); - addCommonStackArgs(environmentConfig, args, component); + addCommonStackArgs(environmentConfig, args); return args; } - private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List args, CerberusStack stack) { - args.add(StackDelegate.AMI_ID_LONG_ARG); - args.add(stack.getAmiId()); - args.add(StackDelegate.INSTANCE_SIZE_LONG_ARG); - args.add(stack.getInstanceSize()); - args.add(StackDelegate.KEY_PAIR_NAME_LONG_ARG); - args.add(stack.getKeyPairName()); - + private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List args) { addTagArgs(environmentConfig, args); } @@ -188,9 +174,6 @@ private static List getUploadCertFilesCommandArgs(EnvironmentConfig envi args.add(UploadCertFilesCommand.CERT_PATH_LONG_ARG); switch (stackName) { - case "vault": - args.add(environmentConfig.getVault().getCertPath()); - break; case "cms": args.add(environmentConfig.getManagementService().getCertPath()); break; @@ -218,47 +201,6 @@ private static List getCreateBaseCommandArgs(EnvironmentConfig config) { return args; } - private static List getUpdateStackCommandArgs(EnvironmentConfig environmentConfig, String[] passedArgs) { - String stackName = getStackName(passedArgs); - List args = new LinkedList<>(); - - if (StringUtils.isBlank(stackName)) { - return args; - } - - args.add(STACK_NAME_KEY); - args.add(stackName); - - CerberusStack cerberusStack; - switch (stackName) { - case "vault": - cerberusStack = environmentConfig.getVault(); - break; - case "cms": - cerberusStack = environmentConfig.getManagementService(); - break; - default: - cerberusStack = null; - } - - if (cerberusStack != null) { - addCommonStackArgs(environmentConfig, args, cerberusStack); - } - - for (int i = 0; i < passedArgs.length; i++) { - String arg = passedArgs[i]; - if (arg.equals(UpdateStackCommand.OVERWRITE_TEMPLATE_LONG_ARG)) { - args.add(UpdateStackCommand.OVERWRITE_TEMPLATE_LONG_ARG); - } - if (arg.equals(UpdateStackCommand.PARAMETER_SHORT_ARG) && i < passedArgs.length -1) { - args.add(UpdateStackCommand.PARAMETER_SHORT_ARG); - args.add(passedArgs[i+1]); - } - } - - return args; - } - private static List getCreateVpcCommandArgs(EnvironmentConfig config) { List args = new LinkedList<>(); addTagArgs(config, args); @@ -268,12 +210,6 @@ private static List getCreateVpcCommandArgs(EnvironmentConfig config) { private static List getCreateSecurityGroupsCommandArgs(EnvironmentConfig config) { List args = new LinkedList<>(); - if (config.getSecurityGroups() != null && - config.getSecurityGroups().getLoadBalancerCidrBlock() != null) { - args.add(CreateSecurityGroupsCommand.LOAD_BALANCER_CIDR_BLOCK_LONG_ARG); - args.add(config.getSecurityGroups().getLoadBalancerCidrBlock()); - } - addTagArgs(config, args); return args; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java index 25f743b2..4fa137cf 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java @@ -35,19 +35,9 @@ public class CreateSecurityGroupsCommand implements Command { public static final String COMMAND_NAME = "create-security-groups"; - public static final String LOAD_BALANCER_CIDR_BLOCK_LONG_ARG = "--load-balancer-cidr"; - - @Parameter(names = LOAD_BALANCER_CIDR_BLOCK_LONG_ARG, - description = "The CIDR from which to allow traffic to the load balancer") - private String loadBalancerCidr; - @ParametersDelegate private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getLoadBalancerCidr() { - return loadBalancerCidr; - } - public TagParametersDelegate getTagParameters() { return tagParameters; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java index 12a28e0b..af527bc0 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/CmsParameters.java @@ -38,9 +38,6 @@ public class CmsParameters implements LaunchConfigParameters { @JsonUnwrapped private LaunchConfigParametersDelegate launchConfigParameters = new LaunchConfigParametersDelegate(); - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getBaseStackName() { return baseStackName; } @@ -103,13 +100,4 @@ public CmsParameters setLaunchConfigParameters(LaunchConfigParametersDelegate la this.launchConfigParameters = launchConfigParameters; return this; } - - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public CmsParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java index 6cac1ae7..25266363 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java @@ -16,7 +16,6 @@ package com.nike.cerberus.domain.cloudformation; -import com.fasterxml.jackson.annotation.JsonUnwrapped; /** * Represents the load balancer stack inputs. @@ -35,9 +34,6 @@ public class LoadBalancerParameters { private String vpcSubnetIdForAz3; - @JsonUnwrapped - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - public String getSgStackName() { return sgStackName; } @@ -92,12 +88,4 @@ public LoadBalancerParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { return this; } - public TagParametersDelegate getTagParameters() { - return tagParameters; - } - - public LoadBalancerParameters setTagParameters(TagParametersDelegate tagParameters) { - this.tagParameters = tagParameters; - return this; - } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java index e14ffa45..943f2712 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/SecurityGroupParameters.java @@ -23,8 +23,6 @@ public class SecurityGroupParameters { private Integer cmsDbPort; - private String loadBalancerCidrBlock; - private String vpcId; public Integer getCmsDbPort() { @@ -36,15 +34,6 @@ public SecurityGroupParameters setCmsDbPort(Integer cmsDbPort) { return this; } - public String getLoadBalancerCidrBlock() { - return loadBalancerCidrBlock; - } - - public SecurityGroupParameters setLoadBalancerCidrBlock(String loadBalancerCidrBlock) { - this.loadBalancerCidrBlock = loadBalancerCidrBlock; - return this; - } - public String getVpcId() { return vpcId; } diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java index a33430d5..2eefc86d 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Environment.java @@ -26,17 +26,11 @@ */ public class Environment { - private String az1; - - private String az2; - - private String az3; - private String domainName; - private Map stackMap; + private Map stackMap; - private Map serverCertificateIdMap; + private Map serverCertificateIdMap; private String configKeyId; @@ -46,46 +40,14 @@ public class Environment { private String metricsTopicArn; - /** - * Is the environment configured for continuous delivery - */ - private boolean isCd; - public Environment() { stackMap = new HashMap<>(); for (Stack stack : Stack.ALL_STACKS) { - stackMap.put(stack, ""); + stackMap.put(stack.getName(), ""); } serverCertificateIdMap = new HashMap<>(); - serverCertificateIdMap.put(Stack.CMS, ""); - } - - public String getAz1() { - return az1; - } - - public Environment setAz1(String az1) { - this.az1 = az1; - return this; - } - - public String getAz2() { - return az2; - } - - public Environment setAz2(String az2) { - this.az2 = az2; - return this; - } - - public String getAz3() { - return az3; - } - - public Environment setAz3(String az3) { - this.az3 = az3; - return this; + serverCertificateIdMap.put(Stack.CMS.getName(), ""); } public String getDomainName() { @@ -96,24 +58,14 @@ public void setDomainName(String domainName) { this.domainName = domainName; } - public Map getStackMap() { + public Map getStackMap() { return stackMap; } - public Environment setStackMap(Map stackMap) { - this.stackMap = stackMap; - return this; - } - - public Map getServerCertificateIdMap() { + public Map getServerCertificateIdMap() { return serverCertificateIdMap; } - public Environment setServerCertificateIdMap(Map serverCertificateIdMap) { - this.serverCertificateIdMap = serverCertificateIdMap; - return this; - } - public String getConfigKeyId() { return configKeyId; } @@ -123,23 +75,10 @@ public Environment setConfigKeyId(String configKeyId) { return this; } - public boolean isCd() { - return isCd; - } - - public Environment setCd(boolean cd) { - isCd = cd; - return this; - } - public Map getRegionBackupBucketMap() { return regionBackupBucketMap == null ? new HashMap<>() : regionBackupBucketMap; } - public void setRegionBackupBucketMap(Map regionBackupBucketMap) { - this.regionBackupBucketMap = regionBackupBucketMap; - } - public Set getBackupAdminIamPrincipals() { return backupAdminIamPrincipals == null ? new HashSet<>() : backupAdminIamPrincipals; } diff --git a/src/main/java/com/nike/cerberus/domain/input/CerberusStack.java b/src/main/java/com/nike/cerberus/domain/input/CerberusStack.java deleted file mode 100644 index ccebad1b..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/CerberusStack.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2016 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.input; - -/** - * Stores CloudFormation stack parameters parsed from YAML - */ -public class CerberusStack { - - private String certPath; - private String amiId; - private String instanceSize; - private String keyPairName; - private String desiredInstances; - private String maxInstances; - private String minInstances; - - public String getCertPath() { - return certPath; - } - - public void setCertPath(String certPath) { - this.certPath = certPath; - } - - public String getAmiId() { - return amiId; - } - - public void setAmiId(String amiId) { - this.amiId = amiId; - } - - public String getInstanceSize() { - return instanceSize; - } - - public void setInstanceSize(String instanceSize) { - this.instanceSize = instanceSize; - } - - public String getKeyPairName() { - return keyPairName; - } - - public void setKeyPairName(String keyPairName) { - this.keyPairName = keyPairName; - } - - public String getDesiredInstances() { - return desiredInstances; - } - - public void setDesiredInstances(String desiredInstances) { - this.desiredInstances = desiredInstances; - } - - public String getMaxInstances() { - return maxInstances; - } - - public void setMaxInstances(String maxInstances) { - this.maxInstances = maxInstances; - } - - public String getMinInstances() { - return minInstances; - } - - public void setMinInstances(String minInstances) { - this.minInstances = minInstances; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java index b136c3d4..fd3b5f8f 100644 --- a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java +++ b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java @@ -34,9 +34,7 @@ public class EnvironmentConfig { private String hostname; private String hostedZoneId; private VpcAccessWhitelist vpcAccessWhitelist; - private Vault vault; private ManagementService managementService; - private Gateway gateway; private SecurityGroups securityGroups; public String getVersion() { @@ -135,14 +133,6 @@ public void setVpcAccessWhitelist(VpcAccessWhitelist vpcAccessWhitelist) { this.vpcAccessWhitelist = vpcAccessWhitelist; } - public Vault getVault() { - return vault; - } - - public void setVault(Vault vault) { - this.vault = vault; - } - public ManagementService getManagementService() { return managementService; } @@ -151,14 +141,6 @@ public void setManagementService(ManagementService managementService) { this.managementService = managementService; } - public Gateway getGateway() { - return gateway; - } - - public void setGateway(Gateway gateway) { - this.gateway = gateway; - } - public SecurityGroups getSecurityGroups() { return securityGroups; } diff --git a/src/main/java/com/nike/cerberus/domain/input/Gateway.java b/src/main/java/com/nike/cerberus/domain/input/Gateway.java deleted file mode 100644 index b8af726b..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/Gateway.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.nike.cerberus.domain.input; - -public class Gateway extends CerberusStack { -} diff --git a/src/main/java/com/nike/cerberus/domain/input/ManagementService.java b/src/main/java/com/nike/cerberus/domain/input/ManagementService.java index fc736102..a1a0d187 100644 --- a/src/main/java/com/nike/cerberus/domain/input/ManagementService.java +++ b/src/main/java/com/nike/cerberus/domain/input/ManagementService.java @@ -22,10 +22,18 @@ /** * Stores Management Service-specific parameters parsed from YAML */ -public class ManagementService extends CerberusStack { +public class ManagementService { private String adminGroup; private List properties; + private List additionalEncryptionCmkRegions; + private String certPath; + private String amiId; + private String instanceSize; + private String keyPairName; + private String desiredInstances; + private String maxInstances; + private String minInstances; public String getAdminGroup() { return adminGroup; @@ -42,4 +50,68 @@ public List getProperties() { public void setProperties(List properties) { this.properties = properties; } + + public List getAdditionalEncryptionCmkRegions() { + return additionalEncryptionCmkRegions; + } + + public void setAdditionalEncryptionCmkRegions(List additionalEncryptionCmkRegions) { + this.additionalEncryptionCmkRegions = additionalEncryptionCmkRegions; + } + + public String getCertPath() { + return certPath; + } + + public void setCertPath(String certPath) { + this.certPath = certPath; + } + + public String getAmiId() { + return amiId; + } + + public void setAmiId(String amiId) { + this.amiId = amiId; + } + + public String getInstanceSize() { + return instanceSize; + } + + public void setInstanceSize(String instanceSize) { + this.instanceSize = instanceSize; + } + + public String getKeyPairName() { + return keyPairName; + } + + public void setKeyPairName(String keyPairName) { + this.keyPairName = keyPairName; + } + + public String getDesiredInstances() { + return desiredInstances; + } + + public void setDesiredInstances(String desiredInstances) { + this.desiredInstances = desiredInstances; + } + + public String getMaxInstances() { + return maxInstances; + } + + public void setMaxInstances(String maxInstances) { + this.maxInstances = maxInstances; + } + + public String getMinInstances() { + return minInstances; + } + + public void setMinInstances(String minInstances) { + this.minInstances = minInstances; + } } diff --git a/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java b/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java index 7f8fc5c3..64f9d1c2 100644 --- a/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java +++ b/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java @@ -19,7 +19,7 @@ /** * Stores Management Service-specific parameters parsed from YAML */ -public class SecurityGroups extends CerberusStack { +public class SecurityGroups { private String loadBalancerCidrBlock; diff --git a/src/main/java/com/nike/cerberus/domain/input/Vault.java b/src/main/java/com/nike/cerberus/domain/input/Vault.java deleted file mode 100644 index 6345b910..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/Vault.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2016 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.input; - - -/** - * Stores Vault-specific parameters parsed from YAML - */ -public class Vault extends CerberusStack { -} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index 10b6ce50..3494d8c2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -16,13 +16,16 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; @@ -69,10 +72,22 @@ public void run(final CreateBaseCommand command) { final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); - cloudFormationService.createStack(Stack.BASE, parameters, true, command.getTagsDelegate().getTags()); - - configStore.initEnvironmentData(); - configStore.initSecretsData(); + final String stackId = cloudFormationService.createStack(Stack.BASE, parameters, true, + command.getTagsDelegate().getTags()); + + final StackStatus endStatus = + cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + + if (StackStatus.CREATE_COMPLETE == endStatus) { + logger.info("Stack creation complete, initializing the configuration bucket."); + environmentMetadata.setBucketName(configStore.getBaseStackOutputs().getConfigBucketName()); + configStore.initEnvironmentData(); + configStore.initSecretsData(); + logger.info("Initialization complete."); + } else { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index a87c21f3..558fb3ae 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -16,8 +16,10 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.DatabaseParameters; @@ -25,6 +27,7 @@ import com.nike.cerberus.domain.cloudformation.VpcParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.RandomStringGenerator; @@ -85,10 +88,18 @@ public void run(final CreateDatabaseCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); - cloudFormationService.createStack(Stack.DATABASE, parameters, true, command.getTagsDelegate().getTags()); + String stackId = cloudFormationService.createStack(Stack.DATABASE, parameters, true, + command.getTagsDelegate().getTags()); - configStore.storeCmsDatabasePassword(databasePassword); + final StackStatus endStatus = + cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + if (StackStatus.CREATE_COMPLETE == endStatus) { + configStore.storeCmsDatabasePassword(databasePassword); + } else { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index a6c99c30..c26c33f1 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -16,16 +16,20 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,7 +84,15 @@ public void run(final CreateLoadBalancerCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); - cloudFormationService.createStack(Stack.LOAD_BALANCER, parameters, true, command.getTagsDelegate().getTags()); + final String stackId = cloudFormationService.createStack(Stack.LOAD_BALANCER, parameters, true, + command.getTagsDelegate().getTags()); + + StackStatus endStatus = cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + + if (endStatus != StackStatus.CREATE_COMPLETE) { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index 0ae5dad3..bf97be4e 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -16,17 +16,19 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Route53Service; -import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,7 +78,16 @@ public void run(final CreateRoute53Command command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); - cloudFormationService.createStack(Stack.ROUTE53, parameters, true, command.getTagsDelegate().getTags()); + final String stackId = cloudFormationService.createStack(Stack.ROUTE53, parameters, true, + command.getTagsDelegate().getTags()); + + final StackStatus endStatus = + cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + + if (StackStatus.CREATE_COMPLETE != endStatus) { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 96520378..0370d551 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -16,14 +16,17 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; @@ -66,13 +69,21 @@ public void run(final CreateSecurityGroupsCommand command) { final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); final SecurityGroupParameters securityGroupParameters = new SecurityGroupParameters() - .setVpcId(vpcOutputs.getVpcId()) - .setLoadBalancerCidrBlock(command.getLoadBalancerCidr()); + .setVpcId(vpcOutputs.getVpcId()); final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); - cloudFormationService.createStack(Stack.SECURITY_GROUPS, parameters, true, command.getTagParameters().getTags()); + String stackId = cloudFormationService.createStack(Stack.SECURITY_GROUPS, parameters, true, + command.getTagParameters().getTags()); + + final StackStatus endStatus = + cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + + if (StackStatus.CREATE_COMPLETE != endStatus) { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index 2b97ac08..16b5cc59 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -16,14 +16,17 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.beust.jcommander.internal.Maps; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.VpcParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2Service; import org.slf4j.Logger; @@ -75,7 +78,15 @@ public void run(final CreateVpcCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); - cloudFormationService.createStack(Stack.VPC, parameters, true, command.getTagsDelegate().getTags()); + final String stackId = cloudFormationService.createStack(Stack.VPC, parameters, true, + command.getTagsDelegate().getTags()); + + StackStatus endStatus = cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + + if (endStatus != StackStatus.CREATE_COMPLETE) { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index af504e96..02f30d73 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -16,13 +16,16 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.WafParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; @@ -71,7 +74,16 @@ public void run(final CreateWafCommand command) { final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); - cloudFormationService.createStack(Stack.WAF, parameters, true, command.getTagsDelegate().getTags()); + String stackId = cloudFormationService.createStack(Stack.WAF, parameters, true, + command.getTagsDelegate().getTags()); + + final StackStatus endStatus = + cloudFormationService.waitForStatus(stackId, + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + + if (StackStatus.CREATE_COMPLETE != endStatus) { + throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); + } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java index c9a3fbad..c2d1f05d 100644 --- a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java @@ -19,11 +19,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.nike.cerberus.command.core.PrintStackInfoCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.StackInfo; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AutoScalingService; import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +45,7 @@ public class PrintStackInfoOperation implements Operation private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ConfigStore configStore; + private final EnvironmentMetadata environmentMetadata; private final CloudFormationService cloudFormationService; @@ -54,11 +54,12 @@ public class PrintStackInfoOperation implements Operation private final ObjectMapper objectMapper; @Inject - public PrintStackInfoOperation(final ConfigStore configStore, + public PrintStackInfoOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, final AutoScalingService autoScalingService, @Named(CONFIG_OBJECT_MAPPER) final ObjectMapper objectMapper) { - this.configStore = configStore; + + this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.autoScalingService = autoScalingService; this.objectMapper = objectMapper; @@ -66,7 +67,7 @@ public PrintStackInfoOperation(final ConfigStore configStore, @Override public void run(final PrintStackInfoCommand command) { - final String stackId = configStore.getStackId(command.getStack()); + final String stackId = command.getStack().getFullName(environmentMetadata.getName()); if (StringUtils.isBlank(stackId) || !cloudFormationService.isStackPresent(stackId)) { logger.error("The specified environment doesn't contain a stack for " + command.getStack().getName()); diff --git a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java index 108d8d8e..9bc9623b 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AutoScalingService; @@ -83,17 +84,20 @@ public class RollingRebootWithHealthCheckOperation implements Operation stackOutputs = cloudFormationService.getStackOutputs(stackId); final Map stackParameters = cloudFormationService.getStackParameters(stackId); @@ -231,7 +235,7 @@ public boolean isRunnable(final RollingRebootWithHealthCheckCommand command) { final Stack stack = command.getStack(); final String stackNameStr = stack.getName(); - final String stackId = configStore.getStackId(stack); + final String stackId = stack.getFullName(environmentMetadata.getName()); final Map stackParameters = cloudFormationService.getStackParameters(stackId); if (! HEALTH_CHECK_MAP.containsKey(stackNameStr)) { diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 716302a2..e9c2e202 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -136,58 +136,6 @@ public ConfigStore(final AmazonS3 s3Client, this.securityTokenService = securityTokenService; } - /** - * Writes the AZs to the configuration store. This is a one time operation. Subsequent calls will result in - * an {@link IllegalStateException} being thrown. - * - * @param az1 AZ 1 - * @param az2 AZ 2 - * @param az3 AZ 3 - */ - public void storeAzs(final String az1, final String az2, final String az3) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - - if (StringUtils.isNotBlank(environment.getAz1()) - || StringUtils.isNotBlank(environment.getAz2()) - || StringUtils.isNotBlank(environment.getAz3())) { - throw new IllegalStateException("AZs are already defined for this environment! Aborting save..."); - } - - environment.setAz1(az1); - environment.setAz2(az2); - environment.setAz3(az3); - saveEnvironmentData(environment); - } - } - - /** - * Stores the stack ID for a specific component. - * - * @param stack Stack component - * @param stackId Stack ID - */ - public void storeStackId(final Stack stack, final String stackId) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - environment.getStackMap().put(stack, stackId); - saveEnvironmentData(environment); - } - } - - /** - * Get the specific stack ID by component name. - * - * @param stack Stack name - * @return Stack ID - */ - public String getStackId(final Stack stack) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - return environment.getStackMap().get(stack); - } - } - /** * Gets the server certificate name from the config store. * @@ -196,7 +144,7 @@ public String getStackId(final Stack stack) { public String getServerCertificateName(final Stack stack) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - return environment.getServerCertificateIdMap().get(stack); + return environment.getServerCertificateIdMap().get(stack.getName()); } } @@ -211,42 +159,6 @@ public Optional getServerCertificateArn(final Stack stack) { return iamService.getServerCertificateArn(certificateName); } - public Optional getServerCertificateId(final Stack stack) { - final String certificateName = getServerCertificateName(stack); - return iamService.getServerCertificateId(certificateName); - } - - /** - * Stores the KMS key ID used to encrypt files in the config bucket. This is a one time operation that will result - * in {@link IllegalStateException} being thrown on subsequent calls. - * - * @param configKeyId The KMS key ID - */ - public void storeConfigKeyId(final String configKeyId) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - - if (StringUtils.isNotBlank(environment.getConfigKeyId())) { - throw new IllegalStateException("Config Key ID is already defined for this environment! Aborting save..."); - } - - environment.setConfigKeyId(configKeyId); - saveEnvironmentData(environment); - } - } - - /** - * Retrieves the CMS adming group from the config store. - * - * @return CMS admin group - */ - public Optional getCmsAdminGroup() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return Optional.ofNullable(secrets.getCms().getAdminGroup()); - } - } - /** * Stores the CMS admin group. * @@ -285,40 +197,6 @@ public void storeCmsDatabasePassword(final String databasePassword) { } } - public void storeEnvDomainName(final String domainName) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - - environment.setDomainName(domainName); - saveEnvironmentData(environment); - } - } - - /** - * Retrieves the CMS Vault token from the config store. - * - * @return CMS Vault token - */ - public Optional getCmsVaultToken() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return Optional.ofNullable(secrets.getCms().getVaultToken()); - } - } - - /** - * Stores the CMS Vault token. - * - * @param cmsVaultToken CMS Vault token - */ - public void storeCmsVaultToken(final String cmsVaultToken) { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getCms().setVaultToken(cmsVaultToken); - saveSecretsData(secrets); - } - } - /** * Gets the Vault root token from the config store. * @@ -360,6 +238,7 @@ public void storeCert(final Stack stack, final String keyContents, final String pkcs8KeyContents, final String pubKeyContents) { + saveEncryptedObject(buildCertFilePath(stack, CERT_PART_CA), caContents); saveEncryptedObject(buildCertFilePath(stack, CERT_PART_CERT), certContents); saveEncryptedObject(buildCertFilePath(stack, CERT_PART_KEY), keyContents); @@ -368,7 +247,7 @@ public void storeCert(final Stack stack, synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - environment.getServerCertificateIdMap().put(stack, certificateName); + environment.getServerCertificateIdMap().put(stack.getName(), certificateName); saveEnvironmentData(environment); } } @@ -648,7 +527,7 @@ public GatewayParameters getGatewayStackParamters() { public M getStackOutputs(final Stack stack, final Class outputClass) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - final String stackId = environment.getStackMap().get(stack); + final String stackId = environment.getStackMap().get(stack.getName()); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("The specified stack doesn't exist for specified environment."); @@ -668,7 +547,7 @@ public M getStackOutputs(final Stack stack, final Class outputClass) { * @return Outputs */ public M getStackOutputs(final String stackName, final Class outputClass) { - final String stackId = cloudFormationService.getStackId(stackName); + final String stackId = Stack.fromName(stackName).getFullName(environmentMetadata.getName()); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("Failed to get CloudFormation output for stack: '" + stackName + "'. Stack does not exist."); @@ -688,8 +567,7 @@ public M getStackOutputs(final String stackName, final Class outputClass) */ public M getStackParameters(final Stack stack, final Class parameterClass) { synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - final String stackId = environment.getStackMap().get(stack); + final String stackId = stack.getFullName(environmentMetadata.getName()); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("The specified stack doesn't exist for the specified environment"); @@ -709,7 +587,7 @@ public M getStackParameters(final Stack stack, final Class parameterClass * @return Parameters */ public M getStackParameters(final String stackName, final Class parameterClass) { - final String stackId = cloudFormationService.getStackId(stackName); + final String stackId = Stack.fromName(stackName).getFullName(environmentMetadata.getName()); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("Failed to get CloudFormation parameters for stack: '" + stackName + "'. Stack does not exist."); diff --git a/src/main/resources/cloudformation/security-groups.yaml b/src/main/resources/cloudformation/security-groups.yaml index 8ccd1b1d..3aab2f55 100644 --- a/src/main/resources/cloudformation/security-groups.yaml +++ b/src/main/resources/cloudformation/security-groups.yaml @@ -26,13 +26,6 @@ Parameters: Default: '3306' Description: Port for the cms DB instance Type: Number - loadBalancerCidrBlock: - AllowedPattern: '[0-9./]*' - Default: 0.0.0.0/0 - Description: The application load balancer CIDR block for where traffic is allowed from - MaxLength: '20' - MinLength: '9' - Type: String vpcId: Description: ID of the VPC to associate with the security groups Type: String @@ -48,7 +41,7 @@ Resources: Type: AWS::EC2::SecurityGroup AlbIngressFromInternetSg443: Properties: - CidrIp: !Ref 'loadBalancerCidrBlock' + CidrIp: 0.0.0.0/0 FromPort: 443 GroupId: !Ref 'AlbSg' IpProtocol: tcp @@ -65,7 +58,6 @@ Resources: CmsDbSg: Properties: GroupDescription: Management Server Database Security Group - Tags: VpcId: Ref: 'vpcId' Type: AWS::EC2::SecurityGroup diff --git a/src/main/resources/cloudformation/vpc.yaml b/src/main/resources/cloudformation/vpc.yaml index 50380ce2..005b2f9b 100644 --- a/src/main/resources/cloudformation/vpc.yaml +++ b/src/main/resources/cloudformation/vpc.yaml @@ -81,7 +81,6 @@ Resources: - AmazonProvidedDNS Type: AWS::EC2::DHCPOptions CerberusInternetGateway: - Properties: Type: AWS::EC2::InternetGateway CerberusRouteInternetGateway: Properties: From ffd2c6ef50f9200c5c0245f191924234c2b26449 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 11:01:41 -0800 Subject: [PATCH 30/69] add custom serializer and deserializer for Stack (#91) --- .../domain/environment/Environment.java | 12 ++--- .../cerberus/domain/environment/Stack.java | 4 ++ .../domain/environment/StackDeserializer.java | 41 ++++++++++++++++ .../domain/environment/StackSerializer.java | 39 +++++++++++++++ .../com/nike/cerberus/store/ConfigStore.java | 6 +-- .../com/nike/cerberus/domain/StackTest.java | 49 +++++++++++++++++++ 6 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java create mode 100644 src/test/java/com/nike/cerberus/domain/StackTest.java diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java index 2eefc86d..0ebbc2db 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Environment.java @@ -28,9 +28,9 @@ public class Environment { private String domainName; - private Map stackMap; + private Map stackMap; - private Map serverCertificateIdMap; + private Map serverCertificateIdMap; private String configKeyId; @@ -43,11 +43,11 @@ public class Environment { public Environment() { stackMap = new HashMap<>(); for (Stack stack : Stack.ALL_STACKS) { - stackMap.put(stack.getName(), ""); + stackMap.put(stack, ""); } serverCertificateIdMap = new HashMap<>(); - serverCertificateIdMap.put(Stack.CMS.getName(), ""); + serverCertificateIdMap.put(Stack.CMS, ""); } public String getDomainName() { @@ -58,11 +58,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } - public Map getStackMap() { + public Map getStackMap() { return stackMap; } - public Map getServerCertificateIdMap() { + public Map getServerCertificateIdMap() { return serverCertificateIdMap; } 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 adaf1984..62647cd7 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -16,6 +16,8 @@ package com.nike.cerberus.domain.environment; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableList; import com.nike.cerberus.ConfigConstants; import org.apache.commons.io.IOUtils; @@ -30,6 +32,8 @@ /** * Describes the stacks that make up Cerberus. */ +@JsonSerialize(using = StackSerializer.class) +@JsonDeserialize(using = StackDeserializer.class) public class Stack { public static final Stack BASE = new Stack("base", "base.yaml", false); diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java b/src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java new file mode 100644 index 00000000..0dbfea1d --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 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.environment; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +public class StackDeserializer extends StdDeserializer { + + public StackDeserializer() { + this(null); + } + + public StackDeserializer(Class t) { + super(t); + } + + @Override + public Stack deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String stackName = p.getCodec().readValue(p, String.class); + return Stack.fromName(stackName); + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java b/src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java new file mode 100644 index 00000000..d58979b1 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 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.environment; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +public class StackSerializer extends StdSerializer { + + public StackSerializer() { + this(null); + } + + public StackSerializer(Class t) { + super(t); + } + + @Override + public void serialize(Stack value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(value.getName()); + } +} diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index e9c2e202..eeb647ed 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -144,7 +144,7 @@ public ConfigStore(final AmazonS3 s3Client, public String getServerCertificateName(final Stack stack) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - return environment.getServerCertificateIdMap().get(stack.getName()); + return environment.getServerCertificateIdMap().get(stack); } } @@ -247,7 +247,7 @@ public void storeCert(final Stack stack, synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - environment.getServerCertificateIdMap().put(stack.getName(), certificateName); + environment.getServerCertificateIdMap().put(stack, certificateName); saveEnvironmentData(environment); } } @@ -527,7 +527,7 @@ public GatewayParameters getGatewayStackParamters() { public M getStackOutputs(final Stack stack, final Class outputClass) { synchronized (envDataLock) { final Environment environment = getEnvironmentData(); - final String stackId = environment.getStackMap().get(stack.getName()); + final String stackId = environment.getStackMap().get(stack); if (!cloudFormationService.isStackPresent(stackId)) { throw new IllegalStateException("The specified stack doesn't exist for specified environment."); diff --git a/src/test/java/com/nike/cerberus/domain/StackTest.java b/src/test/java/com/nike/cerberus/domain/StackTest.java new file mode 100644 index 00000000..3c9ce5db --- /dev/null +++ b/src/test/java/com/nike/cerberus/domain/StackTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 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; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.domain.environment.Stack; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class StackTest { + + @Test + public void test_stack_serializes_as_expected() throws JsonProcessingException { + Stack stack = Stack.CMS; + + ObjectMapper objectMapper = new ObjectMapper(); + String json = objectMapper.writeValueAsString(stack); + + assertEquals("\"cms\"", json); + } + + @Test + public void test_stack_deserializes_as_expected() throws IOException { + Stack expected = Stack.CMS; + + ObjectMapper objectMapper = new ObjectMapper(); + Stack actual = objectMapper.readValue("\"cms\"", Stack.class); + + assertEquals(expected, actual); + } +} From 595cab92ad91e632109c4ea248a0b53443f76928 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 13:48:48 -0800 Subject: [PATCH 31/69] Parameterize SSL option for alb (#92) --- .../command/core/CreateLoadBalancerCommand.java | 17 +++++++++++++++++ .../cloudformation/LoadBalancerParameters.java | 10 ++++++++++ .../core/CreateLoadBalancerOperation.java | 7 ++++++- .../resources/cloudformation/load-balancer.yaml | 9 ++++++++- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java index 3da54d77..30a5a8cd 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateLoadBalancerCommand.java @@ -16,6 +16,7 @@ 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; @@ -34,6 +35,8 @@ public class CreateLoadBalancerCommand implements Command { public static final String COMMAND_NAME = "create-load-balancer"; + public static final String LOAD_BALANCER_SSL_POLICY_OVERRIDE_LONG_ARG = "--ssl-policy-override"; + @ParametersDelegate private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); @@ -41,6 +44,20 @@ public TagParametersDelegate getTagsDelegate() { return tagsDelegate; } + @Parameter( + names = { + LOAD_BALANCER_SSL_POLICY_OVERRIDE_LONG_ARG + }, + description = "The SSL Policy that will get applied to the application load balancer, " + + "see http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html" + + " for more information." + ) + private String loadBalancerSslPolicyOverride; + + public String getLoadBalancerSslPolicyOverride() { + return loadBalancerSslPolicyOverride; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java index 25266363..85028665 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java @@ -34,6 +34,8 @@ public class LoadBalancerParameters { private String vpcSubnetIdForAz3; + private String sslPolicy; + public String getSgStackName() { return sgStackName; } @@ -88,4 +90,12 @@ public LoadBalancerParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { return this; } + public String getSslPolicy() { + return sslPolicy; + } + + public LoadBalancerParameters setSslPolicy(String sslPolicy) { + this.sslPolicy = sslPolicy; + return this; + } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index c26c33f1..1e1b9b99 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -81,6 +81,10 @@ public void run(final CreateLoadBalancerCommand command) { .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); + if (StringUtils.isNotBlank(command.getLoadBalancerSslPolicyOverride())) { + loadBalancerParameters.setSslPolicy(command.getLoadBalancerSslPolicyOverride()); + } + final TypeReference> typeReference = new TypeReference>() {}; final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); @@ -101,7 +105,8 @@ public boolean isRunnable(final CreateLoadBalancerCommand command) { try { cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); } catch (IllegalArgumentException iae) { - throw new IllegalStateException("The security group stack must exist to create the load balancer!", iae); + logger.error("The security group stack must exist to create the load balancer!"); + return false; } return ! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName)); diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 20c2a319..8d869d8b 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -36,6 +36,13 @@ Parameters: vpcSubnetIdForAz3: Description: The subnet for the third availability zone Type: String + sslPolicy: + Description: > + The SSL Policy that will get applied to the application load balancer, + see http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html + for more information + Type: String + Default: TLS-1-2-2017-01 Resources: ALBListener: Type: AWS::ElasticLoadBalancingV2::Listener @@ -48,7 +55,7 @@ Resources: LoadBalancerArn: !Ref 'ApplicationLoadBalancer' Port: 443 Protocol: HTTPS - SslPolicy: ELBSecurityPolicy-2016-08 + SslPolicy: !Ref 'sslPolicy' ALBLogBucket: Properties: AccessControl: BucketOwnerFullControl From d2fc87d32ee4a3959c667ed9089b7be844846403 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 14:01:46 -0800 Subject: [PATCH 32/69] tokenize the environment name in order to support _ chars in env name, which the cli allows when creating environments (#93) --- .../java/com/nike/cerberus/domain/environment/Stack.java | 5 +++-- src/main/java/com/nike/cerberus/module/CerberusModule.java | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) 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 62647cd7..ea498c08 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,8 @@ public String getTemplateText() { * @return The generated CloudFormation stack name */ public String getFullName(String environmentName) { - return String.format("%s-cerberus-%s", environmentName, name); + String tokenizedEnvName = StringUtils.replaceAll(environmentName, "_", "-"); + return String.format("%s-cerberus-%s", tokenizedEnvName, name); } public static Stack fromName(final String name) { diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index 40a1412e..3b5203ff 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -217,10 +217,11 @@ private Optional findBucket(final String environmentName) { String envBucket = null; for (final Bucket bucket : buckets) { + String bucketName = bucket.getName(); if (StringUtils.contains(bucket.getName(), ConfigConstants.CONFIG_BUCKET_KEY)) { - String[] parts = bucket.getName().split("-"); - if (StringUtils.equalsIgnoreCase(environmentName, parts[0])) { - envBucket = bucket.getName(); + String tokenizedEnvName = StringUtils.replaceAll(environmentName, "_", "-"); + if (StringUtils.startsWith(bucketName, tokenizedEnvName)) { + envBucket = bucketName; break; } } From 3d4ee1e827b6129789f14ee82b71283e5d630d5a Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 14:11:13 -0800 Subject: [PATCH 33/69] Make the operation validate file existance at runtime rather than when you create the command so that it can be used in a chained command (#94) --- .../UploadCertFilesPathValidator.java | 20 --------------- .../core/UploadCertFilesOperation.java | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java index 6d3412b5..c226e106 100644 --- a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java +++ b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java @@ -18,17 +18,9 @@ import com.beust.jcommander.IValueValidator; import com.beust.jcommander.ParameterException; -import com.beust.jcommander.internal.Sets; -import org.apache.commons.io.filefilter.RegexFileFilter; import java.io.File; -import java.io.FilenameFilter; import java.nio.file.Path; -import java.util.Arrays; -import java.util.Set; -import java.util.StringJoiner; - -import static com.nike.cerberus.operation.core.UploadCertFilesOperation.EXPECTED_FILE_NAMES; /** * Validates that the specified directory contains all the correct files for cert upload. @@ -43,7 +35,6 @@ public void validate(final String name, final Path value) throws ParameterExcept } final File certDirectory = value.toFile(); - final Set filenames = Sets.newHashSet(); if (!certDirectory.canRead()) { throw new ParameterException("Specified path is not readable."); @@ -52,16 +43,5 @@ public void validate(final String name, final Path value) throws ParameterExcept if (!certDirectory.isDirectory()) { throw new ParameterException("Specified path is not a directory."); } - - - final FilenameFilter filter = new RegexFileFilter("^.*\\.(pem|crt)$"); - final File[] files = certDirectory.listFiles(filter); - Arrays.stream(files).forEach(file -> filenames.add(file.getName())); - - if (!filenames.containsAll(EXPECTED_FILE_NAMES)) { - final StringJoiner sj = new StringJoiner(", ", "[", "]"); - EXPECTED_FILE_NAMES.stream().forEach(sj::add); - throw new ParameterException("Not all expected files are present! Expected: " + sj.toString()); - } } } diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index a4004a12..6c6c3a74 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -16,6 +16,8 @@ package com.nike.cerberus.operation.core; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.internal.Sets; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.nike.cerberus.command.core.UploadCertFilesCommand; @@ -25,17 +27,21 @@ import com.nike.cerberus.service.IdentityManagementService; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.UuidSupplier; +import org.apache.commons.io.filefilter.RegexFileFilter; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Set; +import java.util.StringJoiner; import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_CHAIN_FILE; import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_FILE; @@ -79,6 +85,9 @@ public UploadCertFilesOperation(final EnvironmentMetadata environmentMetadata, @Override public void run(final UploadCertFilesCommand command) { + + checkForRequiredFiles(command.getCertPath()); + final Stack stack = command.getStack(); final Path certPath = command.getCertPath(); final String caContents = getFileContents(certPath, DOMAIN_CERT_CHAIN_FILE); @@ -100,6 +109,22 @@ public void run(final UploadCertFilesCommand command) { logger.info("Uploading certificate completed."); } + public void checkForRequiredFiles(final Path certDir) { + + final File certDirectory = certDir.toFile(); + final Set filenames = Sets.newHashSet(); + + final FilenameFilter filter = new RegexFileFilter("^.*\\.(pem|crt)$"); + final File[] files = certDirectory.listFiles(filter); + Arrays.stream(files).forEach(file -> filenames.add(file.getName())); + + if (!filenames.containsAll(EXPECTED_FILE_NAMES)) { + final StringJoiner sj = new StringJoiner(", ", "[", "]"); + EXPECTED_FILE_NAMES.stream().forEach(sj::add); + throw new RuntimeException("Not all expected files are present! Expected: " + sj.toString()); + } + } + @Override public boolean isRunnable(final UploadCertFilesCommand command) { if (StringUtils.isBlank(environmentMetadata.getBucketName())) { From 6ffd36c81fa1d2f7a4734d707f82b6d000c822d2 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 14:32:08 -0800 Subject: [PATCH 34/69] Allow overriding all cnames (#96) --- .../command/core/GenerateCertsCommand.java | 48 ++++++++++++++++++- .../core/GenerateCertsOperation.java | 25 +++++++--- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java index c301bd3f..95c0cb0a 100644 --- a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java @@ -40,9 +40,15 @@ public class GenerateCertsCommand implements Command { public static final String BASE_DOMAIN_LONG_ARG = "--base-domain"; + public static final String EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--edge-domain-name-override"; + + public static final String ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--origin-domain-name-override"; + + public static final String LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--load-balancer-domain-name-override"; + public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; - public static final String ADDITIONAL_SUBJECT_ALT_NAME_LONG_ARG = "--additional-subject-alternative-name"; + public static final String SUBJECT_ALT_NAME_LONG_ARG = "--subject-alternative-name"; public static final String ENABLE_LE_CERTFIX_LONG_ARG = "--enable-letsecrypt-certfix"; @@ -73,6 +79,44 @@ public String getBaseDomainName() { return baseDomainName; } + @Parameter( + names = { + EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG + }, + description = "This command uses {environment}.{base-domain} as the common name, override it with this option" + ) + private String edgeDomainNameOverride; + + public String getEdgeDomainNameOverride() { + return edgeDomainNameOverride; + } + + @Parameter( + names = { + ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG + }, + description = "origin domain name defaults to origin.{environment-name}.{base-domain}, " + + "this command automatically creates a subject alternate name for this, override it with this option" + ) + private String originDomainNameOverride; + + public String getOriginDomainNameOverride() { + return originDomainNameOverride; + } + + @Parameter( + names = { + LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG + }, + description = "the load balancer domain name defaults to {environment-name}.{primary-primaryRegion}.{base-domain}, " + + "this command automatically creates a subject alternate name for this, override it with this option" + ) + private String loadBalancerDomainNameOverride; + + public String getLoadBalancerDomainNameOverride() { + return loadBalancerDomainNameOverride; + } + @Parameter( names = { HOSTED_ZONE_ID_LONG_ARG @@ -88,7 +132,7 @@ public String getHostedZoneId() { @Parameter( names = { - ADDITIONAL_SUBJECT_ALT_NAME_LONG_ARG + SUBJECT_ALT_NAME_LONG_ARG }, description = "Alternative subject names, this should be any additional cnames that need to be secured. such as " ) diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java index 7aac6cf5..98edb015 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java @@ -23,6 +23,7 @@ import com.nike.cerberus.service.ConsoleService; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,15 +61,25 @@ public GenerateCertsOperation(CertificateService certService, ) @Override public void run(GenerateCertsCommand command) { - // The common name ex: demo.cerberus.io - String commonName = String.format("%s.%s", environmentMetadata.getName(), command.getBaseDomainName()); - // The region specific subject alternate name. EX demo.us-west-2.ceberus.io - String regionSpecificSAN = String.format("%s.%s.%s", - environmentMetadata.getName(), environmentMetadata.getRegionName(), command.getBaseDomainName()); + // The common name ex: demo.example.com + String commonName = StringUtils.isNotBlank(command.getEdgeDomainNameOverride()) ? + command.getEdgeDomainNameOverride() : + String.format("%s.%s", environmentMetadata.getName(), command.getBaseDomainName()); + + // origin name san ex: origin.demo.example.com + String originName = StringUtils.isNotBlank(command.getOriginDomainNameOverride()) ? + command.getOriginDomainNameOverride() : + String.format("origin.%s.%s", environmentMetadata.getName(), command.getBaseDomainName()); + + // The region specific subject alternate name for the load balancer. EX demo.us-west-2.example.com + String loadBalancerName = StringUtils.isNotBlank(command.getLoadBalancerDomainNameOverride()) ? + command.getLoadBalancerDomainNameOverride() : + String.format("%s.%s.%s", environmentMetadata.getName(), environmentMetadata.getRegionName(), command.getBaseDomainName()); Set subjectAlternativeNames = new HashSet<>(); - subjectAlternativeNames.addAll(command.getSubjectAlternativeNames()); // add any additional SANs to the SAN set - subjectAlternativeNames.add(regionSpecificSAN); // add the default region specific san + subjectAlternativeNames.addAll(command.getSubjectAlternativeNames()); + subjectAlternativeNames.add(originName); + subjectAlternativeNames.add(loadBalancerName); // confirm with user consoleService.askUserToProceed(String.format("Preparing to generate certs with Common Name: %s and Subject Alternative Names: %s", From 1ac9cc6ee5568f9c671f3098a8bb9c0a06445429 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 14:32:31 -0800 Subject: [PATCH 35/69] do validation in isRunnbable rather than run, so it plays nicer with composite commands (#95) --- .../operation/cms/CreateCmsCmkOperation.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java index 8c10b6f5..a2702146 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java @@ -69,10 +69,6 @@ public void run(final CreateCmsCmkCommand command) { logger.info("Retrieving configuration data from the configuration bucket."); final Properties cmsConfigProperties = configStore.getAllExistingCmsEnvProperties(); - // additional argument validation - validateRegionsArg(command); - validateRotateArg(command, cmsConfigProperties); - // create the CMKs List cmkArns = createCmks(command, cmsConfigProperties); @@ -91,6 +87,16 @@ public boolean isRunnable(final CreateCmsCmkCommand command) { logger.warn("CMS config does not exist, please use 'create-cms-config' command first."); } + try { + final Properties cmsConfigProperties = configStore.getAllExistingCmsEnvProperties(); + // additional argument validation + validateRegionsArg(command); + validateRotateArg(command, cmsConfigProperties); + } catch (ParameterException e) { + return false; + } + + return isRunnable; } From 66766c8415577b5867decddaf55b202fcb2223d8 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 14 Nov 2017 15:58:39 -0800 Subject: [PATCH 36/69] Create env composite command with V2 yaml (#97) * Create env composite comman with V2 yaml, also making sure isRunnable on the required commands returns false rather than throwing exceptions * Misc clean up from PRs and merge conflicts * remove proxy information from env yaml as it is not needed for new arch env creation and address pr feedback * fix test and remove uneeded List wrapping class * update error message --- .../com/nike/cerberus/cli/ArgsBuilder.java | 58 ++++++ .../com/nike/cerberus/cli/CerberusRunner.java | 2 + .../cli/EnvironmentConfigToArgsMapper.java | 184 +++++++++++------- .../cerberus/command/CerberusCommand.java | 20 +- .../CreateCerberusEnvironmentCommand.java | 45 +++++ .../domain/input/EnvironmentConfig.java | 152 ++++++++++----- .../cerberus/domain/input/ProxyConfig.java | 61 ------ .../composite/CompositeOperation.java | 65 +++---- .../CreateCerberusEnvironmentOperation.java | 116 +++++++++++ .../PrintAllStackInformationOperation.java | 6 +- .../core/CreateDatabaseOperation.java | 3 +- .../core/CreateEdgeDomainRecordOperation.java | 6 +- .../core/CreateRoute53Operation.java | 6 +- .../operation/core/CreateWafOperation.java | 3 +- .../cerberus/command/CerberusCommandTest.java | 48 ----- src/test/resources/environment.yaml | 161 +++++++++++---- 16 files changed, 599 insertions(+), 337 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/cli/ArgsBuilder.java create mode 100644 src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/ProxyConfig.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java diff --git a/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java b/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java new file mode 100644 index 00000000..96f062f5 --- /dev/null +++ b/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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.cli; + +import java.util.LinkedList; +import java.util.List; + +public class ArgsBuilder { + + private List args; + + private ArgsBuilder() { + args = new LinkedList<>(); + } + + public static ArgsBuilder create() { + return new ArgsBuilder(); + } + + public ArgsBuilder addOption(String optionKey, String optionValue) { + args.add(optionKey); + args.add(optionValue); + return this; + } + + public ArgsBuilder addDynamicProperty(String dynamicOptionFlag, String key, String value) { + args.add(String.format("%s%s=%s", dynamicOptionFlag, key, value)); + return this; + } + + public ArgsBuilder addFlag(String flag) { + args.add(flag); + return this; + } + + public ArgsBuilder addAll(List argsToAdd) { + this.args.addAll(argsToAdd); + return this; + } + + public List build() { + return args; + } +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index dfbd7216..b2499525 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -31,6 +31,7 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; +import com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; @@ -186,6 +187,7 @@ private void registerAllCommands() { registerCommand(new CreateSecurityGroupsCommand()); registerCommand(new CreateLoadBalancerCommand()); registerCommand(new CreateEdgeDomainRecordCommand()); + registerCommand(new CreateCerberusEnvironmentCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 332813e9..78da6caa 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -16,11 +16,14 @@ package com.nike.cerberus.cli; +import com.nike.cerberus.command.StackDelegate; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; +import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; @@ -28,6 +31,8 @@ import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; +import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.domain.input.ManagementService; import com.nike.cerberus.domain.input.VpcAccessWhitelist; @@ -101,147 +106,178 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig return getCreateRoute53CommandArgs(environmentConfig); case CreateWafCommand.COMMAND_NAME: return getCreateWafCommandArgs(environmentConfig); + case GenerateCertsCommand.COMMAND_NAME: + return getGenerateCertificatesCommandArgs(environmentConfig); + case CreateCmsCmkCommand.COMMAND_NAME: + return getCreateCmsCmkCommandArgs(environmentConfig); + case CreateEdgeDomainRecordCommand.COMMAND_NAME: + return getCreateEdgeDomainRecordCommandArgs(environmentConfig); default: return new LinkedList<>(); } } - private static List getCreateCmsConfigCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); + private static List getCreateEdgeDomainRecordCommandArgs(EnvironmentConfig environmentConfig) { + return ArgsBuilder.create() + .addOption(CreateEdgeDomainRecordCommand.BASE_DOMAIN_NAME_LONG_ARG, environmentConfig.getBaseDomainName()) + .addOption(CreateEdgeDomainRecordCommand.HOSTED_ZONE_ID_LONG_ARG, environmentConfig.getHostedZoneId()) + .addOption(CreateEdgeDomainRecordCommand.EDGE_DOMAIN_NAME_OVERRIDE, environmentConfig.getEdgeDomainNameOverride()) + .build(); + } - ManagementService managementService = environmentConfig.getManagementService(); + private static List getCreateCmsCmkCommandArgs(EnvironmentConfig environmentConfig) { + ArgsBuilder args = ArgsBuilder.create(); + + if (environmentConfig.getManagementService().getAdditionalEncryptionCmkRegions() != null + && environmentConfig.getManagementService().getAdditionalEncryptionCmkRegions().size() >= 1) { + environmentConfig.getManagementService().getAdditionalEncryptionCmkRegions().forEach(region -> { + args.addOption(CreateCmsCmkCommand.ADDITIONAL_REGIONS_ARG, region); + }); + } else { + throw new RuntimeException(String.format("%s requires at least 1 additional region be specified for high " + + "availability, add at least 1 region to 'encryption-cmk-regions'", CreateCmsCmkCommand.COMMAND_NAME)); + } - args.add(CreateCmsConfigCommand.ADMIN_GROUP_LONG_ARG); - args.add(managementService.getAdminGroup()); + return args.build(); + } + private static List getCreateCmsConfigCommandArgs(EnvironmentConfig environmentConfig) { + ArgsBuilder args = ArgsBuilder.create(); + ManagementService managementService = environmentConfig.getManagementService(); + args.addOption(CreateCmsConfigCommand.ADMIN_GROUP_LONG_ARG, managementService.getAdminGroup()); managementService.getProperties().forEach(property -> { - args.add(CreateCmsConfigCommand.PROPERTY_SHORT_ARG); - args.add(property); + args.addOption(CreateCmsConfigCommand.PROPERTY_SHORT_ARG, property); }); - - return args; + return args.build(); } private static List getWhitelistCidrForVpcAccessCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); + ArgsBuilder args = ArgsBuilder.create(); VpcAccessWhitelist vpcAccessWhitelist = environmentConfig.getVpcAccessWhitelist(); vpcAccessWhitelist.getCidrs().forEach(cidr -> { - args.add(WhitelistCidrForVpcAccessCommand.CIDR_LONG_ARG); - args.add(cidr); + args.addOption(WhitelistCidrForVpcAccessCommand.CIDR_LONG_ARG, cidr); }); vpcAccessWhitelist.getPorts().forEach(port -> { - args.add(WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG); - args.add(port); + args.addOption(WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, port); }); - return args; + return args.build(); } private static List getCreateCmsClusterCommandArgs(EnvironmentConfig environmentConfig) { - List args = new LinkedList<>(); - - addCommonStackArgs(environmentConfig, args); - - return args; - } - - private static void addCommonStackArgs(EnvironmentConfig environmentConfig, List args) { - addTagArgs(environmentConfig, args); + return ArgsBuilder.create() + .addOption(StackDelegate.AMI_ID_LONG_ARG, environmentConfig.getManagementService().getAmiId()) + .addOption(StackDelegate.INSTANCE_SIZE_LONG_ARG, environmentConfig.getManagementService().getInstanceSize()) + .addOption(StackDelegate.KEY_PAIR_NAME_LONG_ARG, environmentConfig.getManagementService().getKeyPairName()) + .addAll(getGlobalTags(environmentConfig)) + .build(); } - private static void addTagArgs(EnvironmentConfig environmentConfig, List args) { -// args.add(TagParametersDelegate.COST_CENTER_LONG_ARG); -// args.add(environmentConfig.getCostCenter()); -// args.add(TagParametersDelegate.OWNER_EMAIL_LONG_ARG); -// args.add(environmentConfig.getOwnerEmail()); -// args.add(TagParametersDelegate.OWNER_GROUP_LONG_ARG); -// args.add(environmentConfig.getOwnerGroup()); + private static List getGlobalTags(EnvironmentConfig environmentConfig) { + ArgsBuilder args = ArgsBuilder.create(); + environmentConfig.getGlobalTags().forEach( (key, value) -> + args.addDynamicProperty(TagParametersDelegate.TAG_SHORT_ARG, key, value) + ); + return args.build(); } private static List getUploadCertFilesCommandArgs(EnvironmentConfig environmentConfig, String[] passedArgs) { String stackName = getStackName(passedArgs); - List args = new LinkedList<>(); + ArgsBuilder args = ArgsBuilder.create(); if (stackName == null) { - return args; + return args.build(); } - args.add(UploadCertFilesCommand.STACK_NAME_LONG_ARG); - args.add(stackName); - - args.add(UploadCertFilesCommand.CERT_PATH_LONG_ARG); + args.addOption(UploadCertFilesCommand.STACK_NAME_LONG_ARG, stackName); switch (stackName) { case "cms": - args.add(environmentConfig.getManagementService().getCertPath()); + args.addOption(UploadCertFilesCommand.CERT_PATH_LONG_ARG, + environmentConfig.getManagementService().getCertPath()); break; default: - args.add(""); + break; } Arrays.stream(passedArgs).forEach(arg -> { if (arg.equals("--overwrite")) { - args.add(UploadCertFilesCommand.OVERWRITE_LONG_ARG); + args.addFlag(UploadCertFilesCommand.OVERWRITE_LONG_ARG); } }); - return args; + return args.build(); } private static List getCreateBaseCommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - - addTagArgs(config, args); - - args.add(CreateBaseCommand.ADMIN_ROLE_ARN_LONG_ARG); - args.add(config.getAdminRoleArn()); - - return args; + return ArgsBuilder.create() + .addAll(getGlobalTags(config)) + .addOption(CreateBaseCommand.ADMIN_ROLE_ARN_LONG_ARG, config.getAdminRoleArn()) + .build(); } private static List getCreateVpcCommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - addTagArgs(config, args); - return args; + return getGlobalTags(config); } private static List getCreateSecurityGroupsCommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - - addTagArgs(config, args); - - return args; + return getGlobalTags(config); } private static List getCreateDatabaseCommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - addTagArgs(config, args); - return args; + return getGlobalTags(config); } private static List getCreateLoadBalancerCommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - addTagArgs(config, args); - return args; + ArgsBuilder args = ArgsBuilder.create(); + if (StringUtils.isNotBlank(config.getLoadBalancerSslPolicyOverride())) { + args.addOption(CreateLoadBalancerCommand.LOAD_BALANCER_SSL_POLICY_OVERRIDE_LONG_ARG, + config.getLoadBalancerSslPolicyOverride()); + } + + args.addAll(getGlobalTags(config)); + return args.build(); } private static List getCreateRoute53CommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - - args.add(CreateRoute53Command.BASE_DOMAIN_NAME_LONG_ARG); - args.add(config.getHostname()); - args.add(CreateRoute53Command.HOSTED_ZONE_ID_LONG_ARG); - args.add(config.getHostedZoneId()); - - return args; + return ArgsBuilder.create() + .addOption(CreateRoute53Command.BASE_DOMAIN_NAME_LONG_ARG, config.getBaseDomainName()) + .addOption(CreateRoute53Command.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) + .addOption(CreateRoute53Command.ORIGIN_DOMAIN_NAME_OVERRIDE, config.getOriginDomainNameOverride()) + .addOption(CreateRoute53Command.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE, config.getLoadBalancerDomainNameOverride()) + .build(); } private static List getCreateWafCommandArgs(EnvironmentConfig config) { - List args = new LinkedList<>(); - addTagArgs(config, args); - return args; + return ArgsBuilder.create() + .addAll(getGlobalTags(config)) + .build(); + } + + private static List getGenerateCertificatesCommandArgs(EnvironmentConfig config) { + ArgsBuilder argsBuilder = ArgsBuilder.create() + .addOption(GenerateCertsCommand.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) + .addOption(GenerateCertsCommand.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()) + .addOption(GenerateCertsCommand.ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getOriginDomainNameOverride()) + .addOption(GenerateCertsCommand.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getLoadBalancerDomainNameOverride()) + .addOption(GenerateCertsCommand.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) + .addOption(GenerateCertsCommand.ACME_API_LONG_ARG, config.getAcmeApiUrl()) + .addOption(GenerateCertsCommand.CONTACT_EMAIL_LONG_ARG, config.getAcmeContactEmail()) + .addOption(GenerateCertsCommand.CERT_FOLDER_LONG_ARG, config.getLocalFolderToStoreCerts()); + + if (config.isEnableLeCertFix()) { + argsBuilder.addFlag(GenerateCertsCommand.ENABLE_LE_CERTFIX_LONG_ARG); + } + + if (config.getAdditionalSubjectNames() != null) { + config.getAdditionalSubjectNames().forEach(sn -> { + argsBuilder.addOption(GenerateCertsCommand.SUBJECT_ALT_NAME_LONG_ARG, sn); + }); + } + + return argsBuilder.build(); } private static String getStackName(String[] passedArgs) { diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index 51429c15..a0cc9ed7 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.nike.cerberus.command.validator.EnvironmentNameValidator; import com.nike.cerberus.domain.input.EnvironmentConfig; -import com.nike.cerberus.domain.input.ProxyConfig; import org.apache.commons.lang3.StringUtils; import java.io.File; @@ -129,7 +128,7 @@ public String getEnvironment() { */ public String getRegion() { String commandLinePassedRegion = region; - String environmentConfigFileRegion = getEnvironmentConfig() == null ? null : getEnvironmentConfig().getRegion(); + String environmentConfigFileRegion = getEnvironmentConfig() == null ? null : getEnvironmentConfig().getPrimaryRegion(); String EnvironmentalVarRegion = System.getenv("CERBERUS_CLI_REGION"); String calculatedRegion = StringUtils.isNotBlank(commandLinePassedRegion) ? commandLinePassedRegion : @@ -155,24 +154,7 @@ public boolean isVersion() { return version; } - /** - * Load poxy config from args first then look to replace them with yaml. - */ public ProxyDelegate getProxyDelegate() { - - if (getEnvironmentConfig() != null && getEnvironmentConfig().getProxyConfig() != null) { - ProxyConfig environmentYamlProxyConfig = getEnvironmentConfig().getProxyConfig(); - if (StringUtils.isBlank(proxyDelegate.getProxyHost()) && StringUtils.isNotBlank(environmentYamlProxyConfig.getHost())) { - proxyDelegate.setProxyHost(environmentYamlProxyConfig.getHost()); - } - if (proxyDelegate.getProxyPort() == null && environmentYamlProxyConfig.getPort() != null) { - proxyDelegate.setProxyPort(environmentYamlProxyConfig.getPort()); - } - if (proxyDelegate.getProxyType() == null && environmentYamlProxyConfig.getType() != null) { - proxyDelegate.setProxyType(environmentYamlProxyConfig.getType()); - } - } - return proxyDelegate; } } diff --git a/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java b/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java new file mode 100644 index 00000000..16b383f4 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.composite.CreateCerberusEnvironmentOperation; + +import static com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand.COMMAND_NAME; + +@Parameters( + commandNames = { + COMMAND_NAME + }, + commandDescription = "Creates a complete Cerberus environment from scratch using an environment yaml" +) +public class CreateCerberusEnvironmentCommand implements Command { + + public static final String COMMAND_NAME = "create-cerberus-environment"; + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CreateCerberusEnvironmentOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java index fd3b5f8f..c35e5f75 100644 --- a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java +++ b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java @@ -17,25 +17,33 @@ package com.nike.cerberus.domain.input; +import java.util.List; +import java.util.Map; + /** * Stores all YAML data for a given Cerberus environment */ public class EnvironmentConfig { private String version; - private ProxyConfig proxyConfig; private String environmentName; - private String region; - private String costCenter; - private String ownerEmail; - private String ownerGroup; + private String primaryRegion; + private Map globalTags; private String adminRoleArn; - private String vpcHostedZoneName; - private String hostname; + private String baseDomainName; + private String edgeDomainNameOverride; + private String originDomainNameOverride; + private String loadBalancerDomainNameOverride; + private String loadBalancerSslPolicyOverride; + private List additionalSubjectNames; private String hostedZoneId; private VpcAccessWhitelist vpcAccessWhitelist; + private boolean generateKeysAndCerts; + private String acmeApiUrl; + private boolean enableLeCertFix; + private String acmeContactEmail; + private String localFolderToStoreCerts; private ManagementService managementService; - private SecurityGroups securityGroups; public String getVersion() { return version; @@ -45,14 +53,6 @@ public void setVersion(String version) { this.version = version; } - public ProxyConfig getProxyConfig() { - return proxyConfig; - } - - public void setProxyConfig(ProxyConfig proxyConfig) { - this.proxyConfig = proxyConfig; - } - public String getEnvironmentName() { return environmentName; } @@ -61,60 +61,76 @@ public void setEnvironmentName(String environmentName) { this.environmentName = environmentName; } - public String getRegion() { - return region; + public String getPrimaryRegion() { + return primaryRegion; } - public void setRegion(String region) { - this.region = region; + public void setPrimaryRegion(String primaryRegion) { + this.primaryRegion = primaryRegion; } - public String getCostCenter() { - return costCenter; + public Map getGlobalTags() { + return globalTags; } - public void setCostCenter(String costCenter) { - this.costCenter = costCenter; + public void setGlobalTags(Map globalTags) { + this.globalTags = globalTags; } - public String getOwnerEmail() { - return ownerEmail; + public String getAdminRoleArn() { + return adminRoleArn; } - public void setOwnerEmail(String ownerEmail) { - this.ownerEmail = ownerEmail; + public void setAdminRoleArn(String adminRoleArn) { + this.adminRoleArn = adminRoleArn; } - public String getOwnerGroup() { - return ownerGroup; + public String getBaseDomainName() { + return baseDomainName; } - public void setOwnerGroup(String ownerGroup) { - this.ownerGroup = ownerGroup; + public void setBaseDomainName(String baseDomainName) { + this.baseDomainName = baseDomainName; } - public String getAdminRoleArn() { - return adminRoleArn; + public String getEdgeDomainNameOverride() { + return edgeDomainNameOverride; } - public void setAdminRoleArn(String adminRoleArn) { - this.adminRoleArn = adminRoleArn; + public void setEdgeDomainNameOverride(String edgeDomainNameOverride) { + this.edgeDomainNameOverride = edgeDomainNameOverride; + } + + public String getOriginDomainNameOverride() { + return originDomainNameOverride; } - public String getVpcHostedZoneName() { - return vpcHostedZoneName; + public void setOriginDomainNameOverride(String originDomainNameOverride) { + this.originDomainNameOverride = originDomainNameOverride; } - public void setVpcHostedZoneName(String vpcHostedZoneName) { - this.vpcHostedZoneName = vpcHostedZoneName; + public String getLoadBalancerDomainNameOverride() { + return loadBalancerDomainNameOverride; } - public String getHostname() { - return hostname; + public void setLoadBalancerDomainNameOverride(String loadBalancerDomainNameOverride) { + this.loadBalancerDomainNameOverride = loadBalancerDomainNameOverride; } - public void setHostname(String hostname) { - this.hostname = hostname; + public String getLoadBalancerSslPolicyOverride() { + return loadBalancerSslPolicyOverride; + } + + public void setLoadBalancerSslPolicyOverride(String loadBalancerSslPolicyOverride) { + this.loadBalancerSslPolicyOverride = loadBalancerSslPolicyOverride; + } + + public List getAdditionalSubjectNames() { + return additionalSubjectNames; + } + + public void setAdditionalSubjectNames(List additionalSubjectNames) { + this.additionalSubjectNames = additionalSubjectNames; } public String getHostedZoneId() { @@ -133,19 +149,51 @@ public void setVpcAccessWhitelist(VpcAccessWhitelist vpcAccessWhitelist) { this.vpcAccessWhitelist = vpcAccessWhitelist; } - public ManagementService getManagementService() { - return managementService; + public boolean isGenerateKeysAndCerts() { + return generateKeysAndCerts; } - public void setManagementService(ManagementService managementService) { - this.managementService = managementService; + public void setGenerateKeysAndCerts(boolean generateKeysAndCerts) { + this.generateKeysAndCerts = generateKeysAndCerts; + } + + public String getAcmeApiUrl() { + return acmeApiUrl; + } + + public void setAcmeApiUrl(String acmeApiUrl) { + this.acmeApiUrl = acmeApiUrl; + } + + public boolean isEnableLeCertFix() { + return enableLeCertFix; + } + + public void setEnableLeCertFix(boolean enableLeCertFix) { + this.enableLeCertFix = enableLeCertFix; + } + + public String getAcmeContactEmail() { + return acmeContactEmail; + } + + public void setAcmeContactEmail(String acmeContactEmail) { + this.acmeContactEmail = acmeContactEmail; } - public SecurityGroups getSecurityGroups() { - return securityGroups; + public String getLocalFolderToStoreCerts() { + return localFolderToStoreCerts; } - public void setSecurityGroups(SecurityGroups securityGroups) { - this.securityGroups = securityGroups; + public void setLocalFolderToStoreCerts(String localFolderToStoreCerts) { + this.localFolderToStoreCerts = localFolderToStoreCerts; + } + + public ManagementService getManagementService() { + return managementService; + } + + public void setManagementService(ManagementService managementService) { + this.managementService = managementService; } } diff --git a/src/main/java/com/nike/cerberus/domain/input/ProxyConfig.java b/src/main/java/com/nike/cerberus/domain/input/ProxyConfig.java deleted file mode 100644 index c343c25c..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/ProxyConfig.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2016 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.input; - -import java.net.Proxy; - -/** - * Stores parameters for a Squid Proxy or other proxy parsed from YAML - */ -public class ProxyConfig { - private boolean enabled = false; - private String host; - private Integer port; - private Proxy.Type type; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public Proxy.Type getType() { - return type; - } - - public void setType(Proxy.Type type) { - this.type = type; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java index ee6fb4ac..d4ce6ac0 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -44,8 +44,6 @@ public abstract class CompositeOperation implements Operation private EnvironmentConfig environmentConfig; - private List runnableChainedCommands = new LinkedList<>(); - @Inject public void setInjector(Injector injector) { this.injector = injector; @@ -68,30 +66,9 @@ private Operation getOperationInstance(Command command) { */ @SuppressWarnings("unchecked") public void run(T compositeCommand) { - runnableChainedCommands.forEach(chainableCommand -> { - Command chainedCommand = chainableCommand.getCommand(); - Operation chainedOperation = chainableCommand.getOperation(); - log.info("Attempting to run command: {}", chainedCommand.getCommandName()); - try { - chainedOperation.run(chainedCommand); - } catch (Throwable e) { - throw new RuntimeException("Failed to execute chained command: " + chainedCommand.getCommandName(), e); - } - log.info("Finished command: {}", chainedCommand.getCommandName()); - - }); - } - - /** - * {@inheritDoc} - * - * Gets the command chain from the implementing class and iterates over every chained command operation to make sure - * they are runnable, if any operation is not runnable this will return false - */ - public boolean isRunnable(T command) { - if (getIsEnvironmentConfigRequired() && environmentConfig == null) { + if (isEnvironmentConfigRequired() && environmentConfig == null) { throw new RuntimeException(String.format("The %s command requires that -f or --file must be supplied as a global option with " + - "a path to a valid environment yaml", command.getCommandName())); + "a path to a valid environment yaml", compositeCommand.getCommandName())); } for (ChainableCommand chainableCommand : getCompositeCommandChain()) { @@ -119,16 +96,24 @@ public boolean isRunnable(T command) { // If the given command is not runnable return false for the whole chain //noinspection unchecked - if (! operation.isRunnable(chainedCommand)) { - return false; + boolean isRunnable = operation.isRunnable(chainedCommand); + if (! isRunnable) { + if (! skipOnNotRunnable()) { + throw new RuntimeException("The command: " + chainedCommand.getCommandName() + " is not runnable, stopping..."); + } else { + log.info("The command {} reports that it is not runnable, skipping...", chainedCommand.getCommandName()); + continue; + } } - // If the given command is runnable add the guice created operation to the object and add the command to the runnable list - log.debug("Command: {} with Args: {} is runnable, adding to runnable list", chainedCommand.getCommandName(), args); - chainableCommand.setOperation(operation); - runnableChainedCommands.add(chainableCommand); + log.info("Attempting to run command: {}, with args: {}", chainedCommand.getCommandName(), args); + try { + operation.run(chainedCommand); + } catch (Throwable e) { + throw new RuntimeException("Failed to execute chained command: " + chainedCommand.getCommandName(), e); + } + log.info("Finished command: {}\n", chainedCommand.getCommandName()); } - return true; } /** @@ -143,7 +128,21 @@ public boolean isRunnable(T command) { * * @return boolean of whether or not the environment yaml is required. */ - public boolean getIsEnvironmentConfigRequired() { + public boolean isEnvironmentConfigRequired() { return true; } + + /** + * If your chain of commands doesn't need every command to run to succeed you can override this to tru. + * + * For example you could have a chain of commands that is long and complicated and commands that have completed will + * return isRunnable: false on future runs, setting this to return true would just mean that the command that is + * already run will be skipped moving on to commands that are still needing to be ran. Allowing you to run a + * complicated composite command as many times as needed to succeed + * + * @return boolean of whether or not to fail the command if a command in the chain returns false for isRunnable + */ + public boolean skipOnNotRunnable() { + return false; + } } diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java new file mode 100644 index 00000000..1f4462c4 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.google.common.collect.Lists; +import com.nike.cerberus.command.cms.CreateCmsClusterCommand; +import com.nike.cerberus.command.cms.CreateCmsCmkCommand; +import com.nike.cerberus.command.cms.CreateCmsConfigCommand; +import com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand; +import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; +import com.nike.cerberus.command.core.CreateLoadBalancerCommand; +import com.nike.cerberus.command.core.CreateRoute53Command; +import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; +import com.nike.cerberus.command.core.CreateVpcCommand; +import com.nike.cerberus.command.core.CreateWafCommand; +import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.command.core.UploadCertFilesCommand; +import com.nike.cerberus.domain.environment.Stack; + +import java.util.List; + +/** + * Operation class for CreateCerberusEnvironmentCommand + */ +public class CreateCerberusEnvironmentOperation extends CompositeOperation { + + /** + * {@inheritDoc} + */ + @Override + protected List getCompositeCommandChain() { + return Lists.newArrayList( + // Step 1 Create the Base Cloud Formation Stack that creates S3 Buckets, Iam Roles and KMS keys needed for config + ChainableCommand.Builder.create().withCommand(new CreateBaseCommand()).build(), + + // Step 2 Create the VPC Cloud Formation Stack that Cerberus will use + ChainableCommand.Builder.create().withCommand(new CreateVpcCommand()).build(), + + // Step 3 Create the Security Group Cloud Formation Stack + ChainableCommand.Builder.create().withCommand(new CreateSecurityGroupsCommand()).build(), + + // Step 4 Create the RDS Database Cloud Formation Stack + ChainableCommand.Builder.create().withCommand(new CreateDatabaseCommand()).build(), + + // Step 5 Generate the PKCS private and public keys as well as the x509 certificates needed to enable https + ChainableCommand.Builder.create().withCommand(new GenerateCertsCommand()).build(), + + // Step 6 Upload the certs and keys to S3 and the IAM Cert Management service so that the ALB and CMS can use the certs + ChainableCommand.Builder.create().withCommand(new UploadCertFilesCommand()) + .withAdditionalArg(UploadCertFilesCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(Stack.CMS.getName()) + .build(), + + // Step 7 Create the Application Load Balancer Cloud Formation Stack + ChainableCommand.Builder.create().withCommand(new CreateLoadBalancerCommand()).build(), + + // Step 8 Generate the CMS config with org specific setting and first secrets encrypt, + // Upload to S3 for CMS to download at service start + ChainableCommand.Builder.create().withCommand(new CreateCmsConfigCommand()).build(), + + // Step 9 Create CMS CMK, create KMS master keys in the regions specified for CMS to use with the AWS Encryption + // client to encrypt secure data in a manor that is decryptable in multiple regions and store in cms props + ChainableCommand.Builder.create().withCommand(new CreateCmsCmkCommand()).build(), + + // Step 10 Create the CMS Cluster Stack + ChainableCommand.Builder.create().withCommand(new CreateCmsClusterCommand()).build(), + + // Step 11 Create the Web Application Fire wall stack + ChainableCommand.Builder.create().withCommand(new CreateWafCommand()).build(), + + // Step 12 Create the Route 53 DNS Record Stack for origin and the load balancer + ChainableCommand.Builder.create().withCommand(new CreateRoute53Command()).build(), + + // Step 13 Create the outer most domain name record that will point to the origin record + ChainableCommand.Builder.create().withCommand(new CreateEdgeDomainRecordCommand()).build() + ); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunnable(CreateCerberusEnvironmentCommand command) { + // Always return true and depend on isRunnable of the chained commands to skip commands that cannot be re-ran + // we want this command to be able to be run more than once to complete an environment for example say temporary + // aws creds expire half way through environment creation + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean skipOnNotRunnable() { + // Since we always return true for isRunnable on this command sub of the sub commands may have already been run + // we want this command to be able to be run more than once to complete an environment for example say temporary + // aws creds expire half way through environment creation + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java index 117ad431..db64b52a 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java @@ -46,8 +46,12 @@ protected List getCompositeCommandChain() { } @Override - public boolean getIsEnvironmentConfigRequired() { + public boolean isEnvironmentConfigRequired() { return false; } + @Override + public boolean isRunnable(PrintAllStackInformationCommand command) { + return true; + } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index 558fb3ae..5aa69b91 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -108,7 +108,8 @@ public boolean isRunnable(final CreateDatabaseCommand command) { try { cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); } catch (IllegalArgumentException iae) { - throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); + logger.error("The security group stack must exist to create the the data base stack!"); + return false; } return !cloudFormationService.isStackPresent(Stack.DATABASE.getFullName(environmentName)); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java index 7d41cd36..06a69432 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java @@ -75,10 +75,8 @@ public boolean isRunnable(final CreateEdgeDomainRecordCommand command) { final String environmentName = environmentMetadata.getName(); final String recordSetName = getEdgeDomainName(command.getBaseDomainName(), command.getEdgeDomainNameOverride()); - try { - cloudFormationService.getStackId(Stack.ROUTE53.getFullName(environmentName)); - } catch (IllegalArgumentException iae) { - throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!", iae); + if (cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName))) { + return false; } return !route53Service.recordSetWithNameAlreadyExists(recordSetName, command.getHostedZoneId()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index bf97be4e..afa4e0ed 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -97,10 +97,12 @@ public boolean isRunnable(final CreateRoute53Command command) { final String originDomainName = getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride()); if (!cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { - throw new IllegalStateException("The load balancer stack must exist to create the Route53 record!"); + logger.error("The load balancer stack must exist to create the Route53 record!"); + return false; } if (cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName))) { - throw new IllegalStateException("Route53 stack already exists."); + logger.error("Route53 stack already exists."); + return false; } return !(route53Service.recordSetWithNameAlreadyExists(loadBalancerDomainName, command.getHostedZoneId()) || diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index 02f30d73..16a3bb53 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -92,7 +92,8 @@ public boolean isRunnable(final CreateWafCommand command) { try { cloudFormationService.getStackId(Stack.LOAD_BALANCER.getFullName(environmentName)); } catch (IllegalArgumentException iae) { - throw new IllegalStateException("The load balancer stack must exist to create the WAF!", iae); + logger.error("The load balancer stack must exist to create the WAF!"); + return false; } return ! cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName)); diff --git a/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java b/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java index 9852c51d..cedc99b4 100644 --- a/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java +++ b/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java @@ -52,52 +52,4 @@ public void test_that_command_line_poxy_args_are_honored() { assertEquals(Proxy.Type.SOCKS, proxyDelegate.getProxyType()); } - @Test - public void test_that_yaml_proxy_args_are_honored() throws URISyntaxException { - URL url = getClass().getClassLoader().getResource("environment.yaml"); - File file = Paths.get(url.toURI()).toFile(); - String yamlFilePath = file.getAbsolutePath(); - String[] userInput = { - "-f", yamlFilePath, - "COMMAND", - "--some-opt", "some-value" - }; - - CerberusCommand cerberusCommand = new CerberusCommand(); - JCommander commander = new JCommander(cerberusCommand); - commander.setProgramName("cerberus"); - commander.setAcceptUnknownOptions(true); - commander.parseWithoutValidation(userInput); - - ProxyDelegate proxyDelegate = cerberusCommand.getProxyDelegate(); - - assertEquals("localhost", proxyDelegate.getProxyHost()); - assertEquals((Integer) 9000, proxyDelegate.getProxyPort()); - assertEquals(Proxy.Type.SOCKS, proxyDelegate.getProxyType()); - } - - @Test - public void test_that_command_line_proxy_args_overwrite_yaml_honored() throws URISyntaxException { - URL url = getClass().getClassLoader().getResource("environment.yaml"); - File file = Paths.get(url.toURI()).toFile(); - String yamlFilePath = file.getAbsolutePath(); - String[] userInput = { - "--proxy-host", "10.1.1.1", - "-f", yamlFilePath, - "COMMAND", - "--some-opt", "some-value" - }; - - CerberusCommand cerberusCommand = new CerberusCommand(); - JCommander commander = new JCommander(cerberusCommand); - commander.setProgramName("cerberus"); - commander.setAcceptUnknownOptions(true); - commander.parseWithoutValidation(userInput); - - ProxyDelegate proxyDelegate = cerberusCommand.getProxyDelegate(); - - assertEquals("10.1.1.1", proxyDelegate.getProxyHost()); - assertEquals((Integer) 9000, proxyDelegate.getProxyPort()); - assertEquals(Proxy.Type.SOCKS, proxyDelegate.getProxyType()); - } } diff --git a/src/test/resources/environment.yaml b/src/test/resources/environment.yaml index ca55fc66..6613a6a4 100644 --- a/src/test/resources/environment.yaml +++ b/src/test/resources/environment.yaml @@ -1,59 +1,138 @@ version: 1 -# proxy information -# You only need to configure this, if you plan on white listing proxy boxes and will need to jump -# through them to talk to the instances that will live in the Cerberus VPC -# If your not using a proxy either delete this section or make type = DIRECT for direct connecting -proxy-config: - host: localhost - port: 9000 - # [DIRECT, HTTP, SOCKS] - type: SOCKS # The name of the environment environment-name: demo -# The region the environment will run in -region: us-west-2 -# Costcenter for where to bill provisioned resources. Will be tagged on all resources. -cost-center: 11111 -# The e-mail for who owns the provisioned resources. Will be tagged on all resources. -owner-email: obvisouly.fake@nike.com -# The owning group for the provision resources. Will be tagged on all resources. -owner-group: engineering team name - -# A IAM role ARN that will be given elevated privileges for the KMS CMK created., -# If you don't separate root access from admins just use the root role here -admin-role-arn: arn:aws:iam::111111111:role/onelogin-roles-OneLoginAdminRole-2222222222 -# The Route 53 hosted zone name that will be created for CNAME records used by internal ELBs. -vpc-hosted-zone-name: demo.internal.cerberus-oss.io -# The hostname that will be exposed for Cerberus -hostname: demo.cerberis-oss.io -# The hostedZoneId that will allow the registration of the hostname + +# The primary region the environment will run in, this is where the ASG and all region specific resources will be created. +primary-region: us-west-2 + +# Optional map of tags that will be applied to any resource created by the CloudFormation Service in this CLI +global-tags: + # Name: cerberus-{env} is automatically added to this map + cost-center: 11111 + owner-email: obvisouly.fake@nike.com + owner-group: engineering-team-name + +# A IAM role ARN that will be given elevated privileges for the KMS CMK created. +admin-role-arn: arn:aws:iam::111111111:role/admin + +# This is used to create DNS records and TLS certificates +# By default the following records will be created +# {environment-name}.{base-domain} ex: demo.example.com +# origin.{environment-name}.{base-domain} ex: origin.demo.example.com +# {environment-name}.{primary-primaryRegion}.{base-domain} ex: demo.us-west-2.example.com +# In this example: +# demo.example.com points to origin.demo.example.com +# origin.demo.example points to demo.us-west-2.example.com +# demo.us-west-2.example.com points to the ALB +# This extra hop allows for more flexibility in future infrastructure operations +base-domain-name: example.com + +# Edge domain name defaults to {environment}.{base-domain} +edge-domain-name-override: new-demo.example.com +# Origin domain name defaults to origin.{environment-name}.{base-domain} override it here +origin-domain-name-override: origin.new-demo.demo.com +# Load balancer domain name defaults to {environment-name}.{primary-primaryRegion}.{base-domain} override it here +load-balancer-domain-name-override: new-demo.us-west-2.demo.com + +# Add additional subject names to the generated cert +# For example if your standing up a new architecture edition of Cerberus and you still have the existing env up you can add those domains +# here so your certificates are still valid when you cut over the edge domain name to the old domain name. +additional-subject-names: + - demo.example.com + - origin.demo.example.com + - demo.us-west-2.example.com + - demo.us-east-1.example.com + +# The SSL Policy that will get applied to the application load balancer +# see http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html +# If this is not defined than we default to 'TLS-1-2-2017-01' in the old cerberus arch we defaulted to +# 'ELBSecurityPolicy-2016-08' if you have clients running on deprecated Java versions and need to maintain tls 1.0 and +# 1.1 support use ELBSecurityPolicy-2016-08 +# load-balancer-ssl-policy-override: ELBSecurityPolicy-2016-08 + +# The hosted zone id that will allow the registration of the above domain names hosted-zone-id: X5CT6JROG9F2DR -# Here we can define the ports and CIDRs that we want to allow ingress into the Cerberus env VPC -# You can add your current ip or proxy boxes here and use the above proxy config -vpc-access-whitelist: - ports: - - 443 - - 8080 - - 8200 - - 8500 - - 8400 - - 22 - cidrs: - - 50.39.106.150/32 +# If you wish to use your own certs set this to false or else fill out the ACME api information +# Else if you are providing your own keys and certs the following files are required. +# +# domain-private-key-pkcs1.pem - Used by the ALB +# The private key in pkcs1 pem format used to sign your csr when generating your certs. +# PKCS #1 keys are human readable files that look like the following +# -----BEGIN RSA PRIVATE KEY----- +# ... +# -----END RSA PRIVATE KEY----- +# +# domain-private-key-pkcs8.pem - Used by CMS to secure the connection between the ALB and the Java Netty web server +# The same private key as above but in pkcs8 pem format +# PKCS #8 keys are human readable files that look like the following +# -----BEGIN PRIVATE KEY----- +# ... +# -----END PRIVATE KEY----- +# +# domain-public-key.pem - Used by the ALB +# The public key for the above private key in PKCS1 pem format +# PKCS #1 pem keys are human readable files that look like the following +# -----BEGIN RSA PUBLIC KEY----- +# ... +# -----END RSA PUBLIC KEY----- +# +# domain-cert.crt - Used by the ALB and CMS +# The x509 certificate +# +# chain.crt - Used by the ALB +# The x509 certificate chain +# +generate-keys-and-certs: true +# API URL to an ACME provider, if not a special ACME url, should end in /directory +# The ACME provider must support DNS-01 domain verification challenges such as the LetsEncrypt Boulder ACME V1 impl. +# LetsEncrypt is a free, automated, and open Certificate Authority, visit https://letsencrypt.org/ for more info. +#acme-api-url: acme://letsencrypt.org/staging #For testing. +acme-api-url: acme://letsencrypt.org +# If you use LetsEncrypt and don't have their certs installed in your trust store you can set this +# to true to use a hardcoded cert provided by the acme4j client, or visit https://letsencrypt.org/certificates/ +enable-le-cert-fix: true +# Contact email to send to ACME provider when creating certs +acme-contact-email: justin.field@example.com +# Folder to save generated files +local-folder-to-store-certs: /path/to/saved/certs/demo/certs/ +# Cerberus Management Service Config management-service: - cert-path: /home/fieldju/development/cerberus_environments/demo/certs/ + # The path to the certs, if you are generating the certs using an ACME provider + # just simply make this the same value as local-folder-to-store-certs or use the path to the certs you + # created outside this CLI. See the comment header above generate-keys-and-certs for required files. + cert-path: /path/to/saved/certs/demo/certs/ + # The ami id for CMS, see https://github.com/Nike-Inc/cerberus-util-scripts to bake an ami ami-id: ami-3333 instance-size: m3.medium key-pair-name: cerberus-test # Group that has admin privileges in CMS. - admin-group: lst-cerberus-admins + admin-group: cerberus-admins + # Cerberus uses the Amazon encryption library backed by KMS to encrypt secure data, this library supports encrypting + # data with data keys created by multiple KMS CMKs to make the data more highly available in case of region outage. + # Cerberus requires at least 1 additional region for CMK creation, the primary region is used automatically + additional-encryption-cmk-regions: + - us-east-1 # Dynamic parameters for setting additional properties in the CMS + # See https://github.com/Nike-Inc/cerberus-management-service#configurable-properties + # Also see https://github.com/Nike-Inc/cerberus-management-service/blob/master/src/main/resources/cms.conf + # In general any property can be overridden here for the initial setup, some of the config in cms.conf cannot be overridden. + # Future changes can be made with the update-cms-config command. + # To view the current config you can use cerberus -e env -r us-west-2 view-config --config-path data/cms/environment.properties properties: - cms.auth.connector=com.nike.cerberus.auth.connector.onelogin.OneLoginAuthConnector - auth.connector.onelogin.api_region=us - auth.connector.onelogin.client_id=123 - auth.connector.onelogin.client_secret=312 - - auth.connector.onelogin.subdomain=nike \ No newline at end of file + - auth.connector.onelogin.subdomain=example + +# Here we can define the ports and CIDRs that we want to allow ingress into the Cerberus VPC for this environment +# You can add your current ip or proxy boxes here and use the above proxy config +vpc-access-whitelist: + ports: + - 8443 #CMS SSL Port for health check rolling reboot command + - 22 #SSH + cidrs: + - 50.39.106.150/32 \ No newline at end of file From 442d6b41f8bb8d7bb45c2428230ef213ebaa5536 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Tue, 14 Nov 2017 16:20:24 -0800 Subject: [PATCH 37/69] IntelliJ auto format (#98) --- build.gradle | 2 +- gradle/check.gradle | 4 +- gradle/dependencies.gradle | 2 +- .../com/nike/cerberus/ConfigConstants.java | 12 +++--- .../com/nike/cerberus/cli/CerberusHelp.java | 6 +-- .../com/nike/cerberus/cli/CerberusRunner.java | 2 +- .../cli/EnvironmentConfigToArgsMapper.java | 14 +++---- .../cerberus/client/CerberusAdminClient.java | 8 ++-- .../cerberus/command/CerberusCommand.java | 2 +- .../command/cms/CreateCmsClusterCommand.java | 2 +- .../CreateCerberusEnvironmentCommand.java | 2 +- .../core/CreateSecurityGroupsCommand.java | 1 - .../command/core/CreateWafCommand.java | 1 - .../command/core/GenerateCertsCommand.java | 2 +- .../command/core/UpdateStackCommand.java | 1 - .../cloudformation/DeprecatedBaseOutputs.java | 2 +- .../DeprecatedBaseParameters.java | 2 +- .../cerberus/domain/environment/Stack.java | 2 +- .../cerberus/logging/LoggingConfigurer.java | 3 +- .../cms/CreateCmsClusterOperation.java | 7 ++-- .../operation/cms/CreateCmsCmkOperation.java | 2 +- .../cms/CreateCmsConfigOperation.java | 2 +- .../cms/UpdateCmsConfigOperation.java | 6 +-- .../composite/CompositeOperation.java | 9 ++--- .../operation/core/CreateBaseOperation.java | 5 ++- .../core/CreateCerberusBackupOperation.java | 40 +++++++++---------- .../core/CreateDatabaseOperation.java | 3 +- .../core/CreateLoadBalancerOperation.java | 11 ++--- .../core/CreateRoute53Operation.java | 4 +- .../core/CreateSecurityGroupsOperation.java | 5 ++- .../operation/core/CreateVpcOperation.java | 7 ++-- .../operation/core/CreateWafOperation.java | 5 ++- .../core/GenerateCertsOperation.java | 4 +- .../core/PrintStackInfoOperation.java | 1 - .../core/RestoreCerberusBackupOperation.java | 34 +++++++++------- ...RollingRebootWithHealthCheckOperation.java | 16 ++++---- .../core/UploadCertFilesOperation.java | 1 - .../operation/core/ViewConfigOperation.java | 3 +- .../cerberus/service/AmiTagCheckService.java | 11 +++-- .../cerberus/service/AutoScalingService.java | 7 +++- .../cerberus/service/CertificateService.java | 33 ++++++++------- .../service/CloudFormationService.java | 5 ++- .../com/nike/cerberus/service/Ec2Service.java | 12 +++--- .../service/IdentityManagementService.java | 8 ++-- .../com/nike/cerberus/service/KmsService.java | 9 +++-- .../nike/cerberus/service/MetricsService.java | 2 +- .../nike/cerberus/service/Route53Service.java | 1 - .../nike/cerberus/service/S3StoreService.java | 15 ++++--- .../com/nike/cerberus/store/ConfigStore.java | 6 +-- .../EnvironmentConfigToArgsMapperTest.java | 13 +++--- .../cerberus/command/CerberusCommandTest.java | 4 -- .../EnvironmentNameValidatorTest.java | 2 - ...CerberusDataFromS3BackupOperationTest.java | 2 - .../service/AmiTagCheckServiceTest.java | 6 +-- .../service/AutoScalingServiceTest.java | 4 +- .../cerberus/service/SaltGeneratorTest.java | 4 +- 56 files changed, 188 insertions(+), 191 deletions(-) diff --git a/build.gradle b/build.gradle index 4ff6d3c6..c0636fe7 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ apply from: file('gradle/integration.gradle') mainClassName = 'com.nike.cerberus.cli.CerberusRunner' -task wrapper (type: Wrapper) { +task wrapper(type: Wrapper) { gradleVersion = '3.4' } diff --git a/gradle/check.gradle b/gradle/check.gradle index e3445ba8..280a90f2 100644 --- a/gradle/check.gradle +++ b/gradle/check.gradle @@ -22,11 +22,11 @@ apply plugin: 'com.github.kt3k.coveralls' findbugs { ignoreFailures = false excludeFilter = rootProject.file('codequality/findbugs-suppressions.xml') - sourceSets = [ project.sourceSets.main ] + sourceSets = [project.sourceSets.main] } pmd { - sourceSets = [ project.sourceSets.main ] + sourceSets = [project.sourceSets.main] } tasks.withType(FindBugs) { diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 3dadaf9b..b0b4898c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -65,7 +65,7 @@ allprojects { compile group: 'org.shredzone.acme4j', name: 'acme4j-utils', version: '0.13' testCompile "junit:junit:4.12" - testCompile ("org.mockito:mockito-core:1.10.19") { + testCompile("org.mockito:mockito-core:1.10.19") { exclude group: 'org.hamcrest' } testCompile 'com.fieldju:commons:1.2.0' diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 17c0fb30..2a0e9b3c 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -60,7 +60,7 @@ public class ConfigConstants { public static final String JDBC_USERNAME_KEY = "JDBC.username"; - public static final String JDBC_PASSWORD_KEY ="JDBC.password"; + public static final String JDBC_PASSWORD_KEY = "JDBC.password"; public static final String CMK_ARNS_KEY = "cms.encryption.cmk.arns"; @@ -88,9 +88,9 @@ public class ConfigConstants { public static final String SKIP_AMI_TAG_CHECK_DESCRIPTION = "Skip validation of 'cerberus_component' tag on AMI"; public static final String AMI_TAG_CHECK_ERROR_MESSAGE - = "FAIL: AMI tag check failed!\n" - + "Given AMI ID either does not exist " - + "or does not contain cerberus tag 'cerberus_component' with stack name " - + "or cerberus tag does not match the stack that is being deployed.\n" - + "Please refer documentation on AMI Tagging or use '"+ SKIP_AMI_TAG_CHECK_ARG +"' option to skip this check."; + = "FAIL: AMI tag check failed!\n" + + "Given AMI ID either does not exist " + + "or does not contain cerberus tag 'cerberus_component' with stack name " + + "or cerberus tag does not match the stack that is being deployed.\n" + + "Please refer documentation on AMI Tagging or use '" + SKIP_AMI_TAG_CHECK_ARG + "' option to skip this check."; } \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/cli/CerberusHelp.java b/src/main/java/com/nike/cerberus/cli/CerberusHelp.java index 2327f1fa..89413fb8 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusHelp.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusHelp.java @@ -44,7 +44,7 @@ private void printCustomUsage() { int longestName = 0; List sorted = Lists.newArrayList(); for (ParameterDescription pd : commander.getParameters()) { - if (! pd.getParameter().hidden()) { + if (!pd.getParameter().hidden()) { sorted.add(pd); // + to have an extra space between the name and the description int length = pd.getNames().length() + 2; @@ -72,8 +72,8 @@ private void printCustomUsage() { : def.toString(); sb.append("\n" + s(indentCount)).append("Default: " + Chalk.on(displayedDef).yellow().bold().toString()); } - Class type = pd.getParameterized().getType(); - if(type.isEnum()){ + Class type = pd.getParameterized().getType(); + if (type.isEnum()) { String values = EnumSet.allOf((Class) type).toString(); sb.append("\n" + s(indentCount)).append("Possible Values: " + Chalk.on(values).yellow().bold().toString()); } diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index b2499525..5d58b5ad 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -130,7 +130,7 @@ public void run(String[] args) { /** * If --file, -f was passed in we will map the dsl params to args. - * + *

* Due to the way jCommander works and validates args, we will create a new local command and parse the args * without validation and get the env config and return the new args back to the main commander to parse. * diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 78da6caa..d857f13b 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -29,9 +29,9 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; +import com.nike.cerberus.command.core.GenerateCertsCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.command.core.GenerateCertsCommand; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; import com.nike.cerberus.domain.input.ManagementService; @@ -59,8 +59,8 @@ public static String[] getArgs(EnvironmentConfig environmentConfig, String[] pas for (int i = 0; i < passedArgs.length; i++) { if (StringUtils.startsWith(passedArgs[i], "-")) { args.add(passedArgs[i]); - if (i < passedArgs.length && ! StringUtils.startsWith(passedArgs[i+1], "-")) { - args.add(passedArgs[i+1]); + if (i < passedArgs.length && !StringUtils.startsWith(passedArgs[i + 1], "-")) { + args.add(passedArgs[i + 1]); i++; } } else { @@ -72,7 +72,7 @@ public static String[] getArgs(EnvironmentConfig environmentConfig, String[] pas // now if the command supplied is a command that that is reused for multiple steps, like update-stack, or upload-cert // we need to source the args - if (! StringUtils.isBlank(commandName)) { + if (!StringUtils.isBlank(commandName)) { args.addAll(getArgsForCommand(environmentConfig, commandName, passedArgs)); } @@ -178,7 +178,7 @@ private static List getCreateCmsClusterCommandArgs(EnvironmentConfig env private static List getGlobalTags(EnvironmentConfig environmentConfig) { ArgsBuilder args = ArgsBuilder.create(); - environmentConfig.getGlobalTags().forEach( (key, value) -> + environmentConfig.getGlobalTags().forEach((key, value) -> args.addDynamicProperty(TagParametersDelegate.TAG_SHORT_ARG, key, value) ); return args.build(); @@ -195,7 +195,7 @@ private static List getUploadCertFilesCommandArgs(EnvironmentConfig envi args.addOption(UploadCertFilesCommand.STACK_NAME_LONG_ARG, stackName); switch (stackName) { case "cms": - args.addOption(UploadCertFilesCommand.CERT_PATH_LONG_ARG, + args.addOption(UploadCertFilesCommand.CERT_PATH_LONG_ARG, environmentConfig.getManagementService().getCertPath()); break; default: @@ -283,7 +283,7 @@ private static List getGenerateCertificatesCommandArgs(EnvironmentConfig private static String getStackName(String[] passedArgs) { for (int i = 0; i < passedArgs.length; i++) { if (StringUtils.equals(passedArgs[i], STACK_NAME_KEY)) { - return passedArgs[i+1]; + return passedArgs[i + 1]; } } return null; diff --git a/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java b/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java index f7e5e3d7..e8caa040 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java +++ b/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java @@ -87,7 +87,7 @@ public CerberusAdminClient(UrlResolver vaultUrlResolver, public void restoreMetadata(String jsonPayload) { HttpUrl url = buildUrl("v1/", "metadata"); Response response = execute(url, HttpMethod.PUT, jsonPayload); - if (! response.isSuccessful()) { + if (!response.isSuccessful()) { String body; try { body = response.body().string(); @@ -115,7 +115,7 @@ public SdbMetadataResult getSDBMetaData(int offset, int limit) { .build(); Response response = execute(url, HttpMethod.GET, null); - if (! response.isSuccessful()) { + if (!response.isSuccessful()) { throw new RuntimeException(String.format("Failed to get metadata from Cerberus. Code: %s, Msg: %s", response.code(), response.message())); } @@ -155,7 +155,7 @@ protected Response execute(final HttpUrl url, final String method, final String .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()); requestBuilder.addHeader(HttpHeader.CONTENT_TYPE, DEFAULT_MEDIA_TYPE.toString()) - .method(method, json != null ? RequestBody.create(DEFAULT_MEDIA_TYPE, json) : null ); + .method(method, json != null ? RequestBody.create(DEFAULT_MEDIA_TYPE, json) : null); return httpClient.newCall(requestBuilder.build()).execute(); } catch (IOException e) { @@ -180,7 +180,7 @@ protected Response execute(final HttpUrl url, final String method, final String * @return Deserialized object from the response body */ protected M parseCmsResponseBody(final Response response, final Class responseClass) { - try(ResponseBody body = response.body()) { + try (ResponseBody body = response.body()) { return objectMapper.readValue(body.bytes(), responseClass); } catch (IOException e) { throw new VaultClientException("Error parsing the response body from CMS", e); diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index a0cc9ed7..503cfc8a 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -83,7 +83,7 @@ public EnvironmentConfig getEnvironmentConfig() { } File environmentConfigFile = new File(file); - if (! environmentConfigFile.exists() || environmentConfigFile.isDirectory()) { + if (!environmentConfigFile.exists() || environmentConfigFile.isDirectory()) { throw new IllegalArgumentException(String.format("The file: %s does not exist or is a directory", file)); } diff --git a/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java b/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java index 23a6eb65..124253a0 100644 --- a/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java +++ b/src/main/java/com/nike/cerberus/command/cms/CreateCmsClusterCommand.java @@ -24,9 +24,9 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.cms.CreateCmsClusterOperation; -import static com.nike.cerberus.command.cms.CreateCmsClusterCommand.COMMAND_NAME; import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_ARG; import static com.nike.cerberus.ConfigConstants.SKIP_AMI_TAG_CHECK_DESCRIPTION; +import static com.nike.cerberus.command.cms.CreateCmsClusterCommand.COMMAND_NAME; /** * Command to create the CMS cluster. diff --git a/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java b/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java index 16b383f4..303c186e 100644 --- a/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java +++ b/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java @@ -29,7 +29,7 @@ }, commandDescription = "Creates a complete Cerberus environment from scratch using an environment yaml" ) -public class CreateCerberusEnvironmentCommand implements Command { +public class CreateCerberusEnvironmentCommand implements Command { public static final String COMMAND_NAME = "create-cerberus-environment"; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java index 4fa137cf..10633136 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateSecurityGroupsCommand.java @@ -16,7 +16,6 @@ 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; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java index 373b9266..1525a811 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateWafCommand.java @@ -21,7 +21,6 @@ import com.nike.cerberus.command.Command; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.CreateLoadBalancerOperation; import com.nike.cerberus.operation.core.CreateWafOperation; import static com.nike.cerberus.command.core.CreateWafCommand.COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java index 95c0cb0a..02f7b4a3 100644 --- a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java @@ -174,7 +174,7 @@ public String getCertDir() { names = { ACME_API_LONG_ARG }, - description = "The ACME provider API URL to use, e.g. Let's Encrypt: " + LETS_ENCRYPT_ACME_API_URI, + description = "The ACME provider API URL to use, e.g. Let's Encrypt: " + LETS_ENCRYPT_ACME_API_URI, required = true ) private String acmeApiUrl; diff --git a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java index 61439324..a5226bcc 100644 --- a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java @@ -31,7 +31,6 @@ import java.util.Map; import static com.nike.cerberus.command.core.UpdateStackCommand.COMMAND_NAME; -import static com.nike.cerberus.domain.environment.Stack.ALL_STACK_NAMES; /** diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java index 457812e5..5d95aeb0 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java @@ -20,7 +20,7 @@ * Represents the base stack outputs. * * @deprecated TODO: remove this class when old backup and restore is removed - * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments + * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments */ @Deprecated public class DeprecatedBaseOutputs { diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java index e92099f1..31fc42ed 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java @@ -20,7 +20,7 @@ * Represents the base stack inputs. * * @deprecated TODO: remove this class when old backup and restore is removed - * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments + * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments */ @Deprecated public class DeprecatedBaseParameters { 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 ea498c08..a1053c11 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -118,7 +118,7 @@ public String getFullName(String environmentName) { public static Stack fromName(final String name) { for (Stack stack : ALL_STACKS) { // ignore case and all enum style stack names - if (stack.getName().equalsIgnoreCase(StringUtils.replaceAll(name,"_", "-"))) { + if (stack.getName().equalsIgnoreCase(StringUtils.replaceAll(name, "_", "-"))) { return stack; } } diff --git a/src/main/java/com/nike/cerberus/logging/LoggingConfigurer.java b/src/main/java/com/nike/cerberus/logging/LoggingConfigurer.java index 40f1be4b..69b910b9 100644 --- a/src/main/java/com/nike/cerberus/logging/LoggingConfigurer.java +++ b/src/main/java/com/nike/cerberus/logging/LoggingConfigurer.java @@ -31,7 +31,8 @@ */ public final class LoggingConfigurer { - private LoggingConfigurer() {} + private LoggingConfigurer() { + } /** * Initializes the logger and the requested log level. diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index 9d455117..d94e6919 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -88,7 +88,7 @@ public void run(final CreateCmsClusterCommand command) { } // Make sure the given AmiId is for CMS component. Check if it contains required tag - if ( ! command.isSkipAmiTagCheck() ) { + if (!command.isSkipAmiTagCheck()) { amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), Stack.CMS); } @@ -105,7 +105,8 @@ public void run(final CreateCmsClusterCommand command) { cmsParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); cmsParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(Stack.CMS)); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters, typeReference); // allow user to overwrite CloudFormation parameters with -P option @@ -142,6 +143,6 @@ public boolean isRunnable(final CreateCmsClusterCommand command) { } return configStore.getCmsEnvConfig().isPresent() && - ! cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName)); + !cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java index a2702146..15e1c11b 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java @@ -136,7 +136,7 @@ private List createCmks(CreateCmsCmkCommand command, Properties cmsConfi String policyAsJson = generateKeyPolicy(cmsConfigProperties, description); logger.info("Generated the following policy:\n" + policyAsJson); - Map tags = Maps.newHashMap(); + Map tags = Maps.newHashMap(); tags.put("created_by", "cerberus_cli"); tags.put("created_for", "cerberus_cms"); tags.put("cerberus_env", envName); diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java index ce2bd131..d51f23d9 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java @@ -68,7 +68,7 @@ public void run(final CreateCmsConfigCommand command) { public boolean isRunnable(final CreateCmsConfigCommand command) { boolean isRunnable = !configStore.getCmsEnvConfig().isPresent(); - if (! isRunnable) { + if (!isRunnable) { logger.warn("CMS config already exists, use 'update-cms-config' command."); } diff --git a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java index a41ea3ec..6cc3ea73 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java @@ -27,8 +27,8 @@ import java.util.Optional; import java.util.Properties; -import static com.nike.cerberus.ConfigConstants.SYSTEM_CONFIGURED_CMS_PROPERTIES; import static com.nike.cerberus.ConfigConstants.CMS_ADMIN_GROUP_KEY; +import static com.nike.cerberus.ConfigConstants.SYSTEM_CONFIGURED_CMS_PROPERTIES; /** * Gathers all of the CMS environment configuration and puts it in the config bucket. @@ -51,14 +51,14 @@ public void run(final UpdateCmsConfigCommand command) { final Properties newProperties = configStore.getCmsSystemProperties(); final Properties existingCustomProperties = configStore.getExistingCmsUserProperties(); - if (! command.getOverwrite()) { + if (!command.getOverwrite()) { // keep existing custom properties newProperties.putAll(existingCustomProperties); } // update existing custom properties, add new ones command.getAdditionalProperties().forEach((k, v) -> { - if (! SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(k) || command.isForce()) { + if (!SYSTEM_CONFIGURED_CMS_PROPERTIES.contains(k) || command.isForce()) { newProperties.put(k, v); } else { logger.warn("Ignoring additional property that would override system configured property, " + k); diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java index d4ce6ac0..f5b90876 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -27,7 +27,6 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -import java.util.LinkedList; import java.util.List; /** @@ -60,7 +59,7 @@ private Operation getOperationInstance(Command command) { /** * {@inheritDoc} - * + *

* Runs all the chained commands in order that are defined in the runnable chained command list, * which gets populated when isRunnable is executed */ @@ -97,8 +96,8 @@ public void run(T compositeCommand) { // If the given command is not runnable return false for the whole chain //noinspection unchecked boolean isRunnable = operation.isRunnable(chainedCommand); - if (! isRunnable) { - if (! skipOnNotRunnable()) { + if (!isRunnable) { + if (!skipOnNotRunnable()) { throw new RuntimeException("The command: " + chainedCommand.getCommandName() + " is not runnable, stopping..."); } else { log.info("The command {} reports that it is not runnable, skipping...", chainedCommand.getCommandName()); @@ -134,7 +133,7 @@ public boolean isEnvironmentConfigRequired() { /** * If your chain of commands doesn't need every command to run to succeed you can override this to tru. - * + *

* For example you could have a chain of commands that is long and complicated and commands that have completed will * return isRunnable: false on future runs, setting this to return true would just mean that the command that is * already run will be skipped moving on to commands that are still needing to be ran. Allowing you to run a diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index 3494d8c2..e97d1fb0 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -68,7 +68,8 @@ public void run(final CreateBaseCommand command) { final BaseParameters baseParameters = new BaseParameters() .setAccountAdminArn(command.getAdminRoleArn()); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); @@ -93,7 +94,7 @@ public void run(final CreateBaseCommand command) { @Override public boolean isRunnable(final CreateBaseCommand command) { String environmentName = environmentMetadata.getName(); - return ! cloudFormationService.isStackPresent(Stack.BASE.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(Stack.BASE.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java index 736144db..4e64ac01 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java @@ -44,19 +44,18 @@ import com.google.common.collect.ImmutableMap; import com.nike.cerberus.client.CerberusAdminClient; import com.nike.cerberus.client.CerberusAdminClientFactory; -import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; -import com.nike.cerberus.domain.cms.SafeDepositBox; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; +import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.backup.CerberusBackupMetadata; import com.nike.cerberus.domain.backup.CerberusSdbMetadata; +import com.nike.cerberus.domain.cms.SafeDepositBox; import com.nike.cerberus.domain.environment.BackupRegionInfo; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.MetricsService; import com.nike.cerberus.service.S3StoreService; import com.nike.cerberus.store.ConfigStore; import com.nike.vault.client.model.VaultListResponse; -import com.nike.vault.client.model.VaultResponse; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -238,7 +237,7 @@ private Map> recurseVault(String path, Map { String compositeKey = path + key; if (key.endsWith("/")) { - recurseVault( compositeKey, data); + recurseVault(compositeKey, data); } else { data.put(compositeKey, getData(compositeKey)); } @@ -271,6 +270,7 @@ private Map getData(String path) { /** * Using an S3 Encryption client saves the sdb data to the backup bucket / kms key + * * @param object The sdb data to back up * @param prefix The prefix / virtual folder to store the encrypted json */ @@ -293,7 +293,7 @@ private void saveDataToS3(Object object, String prefix, String key, List private S3StoreService getEncryptedStoreServiceForRegion(String region) { Optional backupRegionInfo = configStore.getBackupInfoForRegion(region); - if (! backupRegionInfo.isPresent()) { + if (!backupRegionInfo.isPresent()) { String kmsCmkId = provisionKmsCmkForBackupRegion(region); String backupBucket = provisionBackupBucketForRegion(region); configStore.storeBackupInfoForRegion(region, backupBucket, kmsCmkId); @@ -340,13 +340,13 @@ private String provisionKmsCmkForBackupRegion(String region) { Policy kmsPolicy = new Policy(); final List statements = new LinkedList<>(); // allow the configured admin iam principals all permissions - configStore.getBackupAdminIamPrincipals().forEach( principal -> { + configStore.getBackupAdminIamPrincipals().forEach(principal -> { log.debug("Adding principal: {} to the CMK Policy for region {}", principal, region); statements.add(new Statement(Statement.Effect.Allow) - .withId("Principal " + principal + " Has All Actions") - .withPrincipals(new Principal(AWS_PROVIDER, principal, false)) - .withActions(KMSActions.AllKMSActions) - .withResources(new Resource("*"))); + .withId("Principal " + principal + " Has All Actions") + .withPrincipals(new Principal(AWS_PROVIDER, principal, false)) + .withActions(KMSActions.AllKMSActions) + .withResources(new Resource("*"))); }); kmsPolicy.setStatements(statements); @@ -358,16 +358,16 @@ private String provisionKmsCmkForBackupRegion(String region) { AWSKMS kms = AWSKMSClient.builder().withCredentials(getAWSCredentialsProviderChain()).withRegion(region).build(); CreateKeyResult createKeyResult = kms.createKey( new CreateKeyRequest() - .withPolicy(policyString) - .withBypassPolicyLockoutSafetyCheck(true) - .withDescription(String.format("Cerberus Backup Encryption key for env: %S region: %s", - environmentMetadata.getName(), region)) - .withTags( - new Tag().withTagKey("env").withTagValue(environmentMetadata.getName()), - new Tag().withTagKey("region").withTagValue(region), - new Tag().withTagKey("cerberus-backup-key").withTagValue("true") - - ) + .withPolicy(policyString) + .withBypassPolicyLockoutSafetyCheck(true) + .withDescription(String.format("Cerberus Backup Encryption key for env: %S region: %s", + environmentMetadata.getName(), region)) + .withTags( + new Tag().withTagKey("env").withTagValue(environmentMetadata.getName()), + new Tag().withTagKey("region").withTagValue(region), + new Tag().withTagKey("cerberus-backup-key").withTagValue("true") + + ) ); String keyId = createKeyResult.getKeyMetadata().getKeyId(); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index 5aa69b91..b37e5a11 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -85,7 +85,8 @@ public void run(final CreateDatabaseCommand command) { .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); String stackId = cloudFormationService.createStack(Stack.DATABASE, parameters, true, diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index 1e1b9b99..228fa300 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -56,9 +56,9 @@ public class CreateLoadBalancerOperation implements Operation> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); final String stackId = cloudFormationService.createStack(Stack.LOAD_BALANCER, parameters, true, @@ -109,6 +110,6 @@ public boolean isRunnable(final CreateLoadBalancerCommand command) { return false; } - return ! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index afa4e0ed..b0c776f3 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Sets; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; @@ -75,7 +74,8 @@ public void run(final CreateRoute53Command command) { .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setOriginDomainName(getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride())); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); final String stackId = cloudFormationService.createStack(Stack.ROUTE53, parameters, true, diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 0370d551..c0c3b2ef 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -71,7 +71,8 @@ public void run(final CreateSecurityGroupsCommand command) { final SecurityGroupParameters securityGroupParameters = new SecurityGroupParameters() .setVpcId(vpcOutputs.getVpcId()); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); String stackId = cloudFormationService.createStack(Stack.SECURITY_GROUPS, parameters, true, @@ -89,7 +90,7 @@ public void run(final CreateSecurityGroupsCommand command) { @Override public boolean isRunnable(final CreateSecurityGroupsCommand command) { String environmentName = environmentMetadata.getName(); - return ! cloudFormationService.isStackPresent(Stack.SECURITY_GROUPS.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(Stack.SECURITY_GROUPS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index 16b5cc59..dc8a89f2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -75,14 +75,15 @@ public void run(final CreateVpcCommand command) { .setAz2(azByIdentifier.get(2)) .setAz3(azByIdentifier.get(3)); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); final String stackId = cloudFormationService.createStack(Stack.VPC, parameters, true, command.getTagsDelegate().getTags()); StackStatus endStatus = cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); + Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); if (endStatus != StackStatus.CREATE_COMPLETE) { throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); @@ -92,7 +93,7 @@ public void run(final CreateVpcCommand command) { @Override public boolean isRunnable(final CreateVpcCommand command) { String environmentName = environmentMetadata.getName(); - return ! cloudFormationService.isStackPresent(Stack.VPC.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(Stack.VPC.getFullName(environmentName)); } private Map mapAvailabilityZones() { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index 16a3bb53..830a89fe 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -71,7 +71,8 @@ public void run(final CreateWafCommand command) { .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setWafName("cerberus-" + environmentName + "-waf"); - final TypeReference> typeReference = new TypeReference>() {}; + final TypeReference> typeReference = new TypeReference>() { + }; final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); String stackId = cloudFormationService.createStack(Stack.WAF, parameters, true, @@ -96,6 +97,6 @@ public boolean isRunnable(final CreateWafCommand command) { return false; } - return ! cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java index 98edb015..d4396576 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java @@ -83,7 +83,7 @@ public void run(GenerateCertsCommand command) { // confirm with user consoleService.askUserToProceed(String.format("Preparing to generate certs with Common Name: %s and Subject Alternative Names: %s", - commonName, String.join(", ", subjectAlternativeNames)), NO); + commonName, String.join(", ", subjectAlternativeNames)), NO); // Enable the use of the hard coded lets encrypt cert if enabled if (command.enableLetsEncryptCertfix()) { @@ -97,7 +97,7 @@ public void run(GenerateCertsCommand command) { // check that we can write to the provided dir FileUtils.forceMkdir(certDir); - if (! certDir.isDirectory() || ! certDir.canWrite()) { + if (!certDir.isDirectory() || !certDir.canWrite()) { throw new RuntimeException("The certificate directory is not a directory or is not writable, path: " + certDir.getAbsolutePath()); } diff --git a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java index c2d1f05d..aa7d7cc9 100644 --- a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java @@ -30,7 +30,6 @@ import javax.inject.Inject; import javax.inject.Named; - import java.util.List; import java.util.Map; diff --git a/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java b/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java index 1fac3311..1877e391 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java @@ -76,7 +76,7 @@ public class RestoreCerberusBackupOperation implements Operation backupMetadata; try { - backupMetadata = objectMapper.readValue(backupMetadataJsonString, new TypeReference>() {}); + backupMetadata = objectMapper.readValue(backupMetadataJsonString, new TypeReference>() { + }); } catch (IOException e) { throw new RuntimeException("Failed to deserialize backup metadata", e); } @@ -162,7 +164,7 @@ private void validateRestore(S3StoreService s3StoreService, RestoreCerberusBacku .append("\nYou are attempting to restore this backup to ") .append(Chalk.on(command.getCerberusUrl()).green().toString()); - if (! backupApiUrl.equalsIgnoreCase(command.getCerberusUrl())) { + if (!backupApiUrl.equalsIgnoreCase(command.getCerberusUrl())) { msg.append("\n\n") .append(Chalk.on("Warning: ").red().toString()) .append(Chalk.on("The backup was created for ").red().toString()) @@ -187,7 +189,7 @@ private void validateRestore(S3StoreService s3StoreService, RestoreCerberusBacku throw new RuntimeException("Failed to validate that the user wanted to proceed with backup", e); } - if (! proceed.equalsIgnoreCase("proceed")) { + if (!proceed.equalsIgnoreCase("proceed")) { throw new RuntimeException("User did not confirm to proceed with backup restore"); } } @@ -210,7 +212,7 @@ private S3StoreService getS3EncryptionStoreService(String cmkId, private String getKmsCmkId(String path, S3StoreService s3StoreService) { Map metadata = s3StoreService.getS3ObjectUserMetaData(path); - if (! metadata.containsKey("x-amz-matdesc")) { + if (!metadata.containsKey("x-amz-matdesc")) { throw new RuntimeException("Failed to get Customer Master Key ID from object user metadata. " + "'x-amz-matdesc' not found in metadata for object at path: " + path); } @@ -220,7 +222,8 @@ private String getKmsCmkId(String path, S3StoreService s3StoreService) { Map encryptionContextMap; try { encryptionContextMap = objectMapper.readValue(serializedEncryptionContext, - new TypeReference>() {}); + new TypeReference>() { + }); } catch (IOException e) { throw new RuntimeException("Failed to convert encryption context metadata value into Map"); } @@ -239,6 +242,7 @@ private String getDecryptedJson(String sdbBackupKey, S3StoreService s3StoreServi * Process the stored backup json * Step 1: Restores the metadata to CMS * Step 2: Restores the secrete data to Vault + * * @param sdbBackupJson the json string from s3 */ protected void processBackup(String sdbBackupJson, CerberusAdminClient cerberusAdminClient) throws IOException { @@ -251,19 +255,21 @@ protected void processBackup(String sdbBackupJson, CerberusAdminClient cerberusA // restore secret vault data JsonNode data = sdb.get("data"); Map> kvPairs = objectMapper.convertValue(data, - new TypeReference>>() {}); + new TypeReference>>() { + }); kvPairs.forEach((String path, Map secretData) -> { Map genericDataMap = new HashMap<>(); - secretData.forEach((String key , JsonNode valueNode)-> { + secretData.forEach((String key, JsonNode valueNode) -> { if (valueNode.isObject()) { - genericDataMap.put(key , objectMapper.convertValue(valueNode, - new TypeReference>() {}) + genericDataMap.put(key, objectMapper.convertValue(valueNode, + new TypeReference>() { + }) ); } else if (valueNode.isTextual()) { - genericDataMap.put(key , valueNode.textValue()); + genericDataMap.put(key, valueNode.textValue()); } else if (valueNode.isBoolean()) { - genericDataMap.put(key , valueNode.booleanValue()); + genericDataMap.put(key, valueNode.booleanValue()); } else { throw new RuntimeException("Unexpected value type for secret value. Type: " + valueNode.getClass()); } @@ -300,7 +306,7 @@ protected void deleteAllSecrets(final String path, VaultAdminClient vaultAdminCl vaultAdminClient.delete(fixedPath + "/" + key); } } - } catch (VaultClientException vce) { + } catch (VaultClientException vce) { throw new RuntimeException("Failed to delete secrets from Vault. for path: " + path); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java index 9bc9623b..d8feabd2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java @@ -57,7 +57,7 @@ public class RollingRebootWithHealthCheckOperation implements Operation HEALTH_CHECK_MAP = ImmutableMap.of( - Stack.CMS.getName(), "http://%s:8080/healthcheck" + Stack.CMS.getName(), "http://%s:8080/healthcheck" ); private final static int DEFAULT_HTTP_TIMEOUT = 15; @@ -105,7 +105,7 @@ public void run(final RollingRebootWithHealthCheckCommand command) { logger.warn(Chalk.on( "If this command fails: the minimum instance size may need to be increased and an EC2 instance" + - " may need to be set to 'in-service' state on the auto scaling group").yellow().toString()); + " may need to be set to 'in-service' state on the auto scaling group").yellow().toString()); final Stack stack = command.getStack(); final String stackId = stack.getFullName(environmentMetadata.getName()); @@ -157,7 +157,7 @@ private void rebootInstance(Stack stack, String autoScalingGroupId, Instance ins // wait for health check pass to confirm instance is healthy after reboot logger.warn(Chalk.on( "If a proxy is required to talk to the EC2 instance, then make sure it is set up." + - " Otherwise this command will never succeed.").yellow().toString()); + " Otherwise this command will never succeed.").yellow().toString()); logger.info("Waiting for health check to pass again to confirm instance is healthy..."); waitForHealthCheckStatusCode(healthCheckUrl, HttpStatus.OK, EXPECTED_NUM_SUCCESSES_AFTER_REBOOT); @@ -167,7 +167,8 @@ private void rebootInstance(Stack stack, String autoScalingGroupId, Instance ins /** * Poll the health check 'n' times, looking for the given response - * @param healthCheckUrl - The health check URL + * + * @param healthCheckUrl - The health check URL * @param numConsecutiveResponsesExpected - The number of times to poll health check */ private void waitForHealthCheckStatusCode(final String healthCheckUrl, @@ -199,6 +200,7 @@ private void waitForHealthCheckStatusCode(final String healthCheckUrl, /** * Execute the given health check + * * @param healthCheckUrl - Name of that EC2 instance belongs to * @return - Response code of the health check */ @@ -219,7 +221,7 @@ private int executeHealthCheck(final String healthCheckUrl) { final Call healthCheckCall = okHttpClient.newCall(requestBuilder); - try(final Response response = healthCheckCall.execute()) { + try (final Response response = healthCheckCall.execute()) { logger.debug("Health check returned status: {}, URL: {}", response.code(), healthCheckUrl); return response.code(); } catch (IOException ioe) { @@ -238,10 +240,10 @@ public boolean isRunnable(final RollingRebootWithHealthCheckCommand command) { final String stackId = stack.getFullName(environmentMetadata.getName()); final Map stackParameters = cloudFormationService.getStackParameters(stackId); - if (! HEALTH_CHECK_MAP.containsKey(stackNameStr)) { + if (!HEALTH_CHECK_MAP.containsKey(stackNameStr)) { logger.error("Cannot reboot cluster: {}. Allowed stacks: {}", stack, HEALTH_CHECK_MAP.keySet()); return false; - } else if (! stackParameters.containsKey(MIN_INSTANCES_STACK_PARAMETER_KEY)) { + } else if (!stackParameters.containsKey(MIN_INSTANCES_STACK_PARAMETER_KEY)) { logger.error("Could not find parameter 'minInstances' on stack: {}", stackId); return false; } else { diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java index 6c6c3a74..950daf72 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java @@ -16,7 +16,6 @@ package com.nike.cerberus.operation.core; -import com.beust.jcommander.ParameterException; import com.beust.jcommander.internal.Sets; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; diff --git a/src/main/java/com/nike/cerberus/operation/core/ViewConfigOperation.java b/src/main/java/com/nike/cerberus/operation/core/ViewConfigOperation.java index 50a75228..9baa6c05 100644 --- a/src/main/java/com/nike/cerberus/operation/core/ViewConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/ViewConfigOperation.java @@ -57,8 +57,7 @@ public void run(final ViewConfigCommand command) { Set keys = configStore.listUnderPartialPath(path); if (!keys.isEmpty()) { logger.info("List under path '{}': {}", path, keys); - } - else { + } else { logger.error(String.format("Failed to load file: '%s'", path)); } } diff --git a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java index 694b5800..7f66ab65 100644 --- a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java +++ b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java @@ -21,14 +21,13 @@ import com.amazonaws.services.ec2.model.DescribeImagesRequest; import com.amazonaws.services.ec2.model.DescribeImagesResult; import com.amazonaws.services.ec2.model.Filter; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.domain.environment.Stack; import javax.inject.Inject; import java.util.HashMap; import java.util.Map; -import com.nike.cerberus.domain.environment.Stack; -import com.nike.cerberus.ConfigConstants; - /** * Service wrapper for AWS EC2. */ @@ -54,8 +53,8 @@ public AmiTagCheckService(final AmazonEC2 ec2Client) { public boolean isAmiWithTagExist(final String amiId, final String tagName, final String tagValue) { final DescribeImagesRequest request = new DescribeImagesRequest() - .withFilters(new Filter().withName(tagName).withValues(tagValue)) - .withFilters(new Filter().withName("image-id").withValues(amiId)); + .withFilters(new Filter().withName(tagName).withValues(tagValue)) + .withFilters(new Filter().withName("image-id").withValues(amiId)); try { final DescribeImagesResult result = ec2Client.describeImages(request); @@ -75,7 +74,7 @@ public boolean isAmiWithTagExist(final String amiId, final String tagName, final * @return void */ public void validateAmiTagForStack(final String amiId, final Stack stack) { - if (!isAmiWithTagExist(amiId, ConfigConstants.CERBERUS_AMI_TAG_NAME, stackAmiTagValueMap.get(stack) )) { + if (!isAmiWithTagExist(amiId, ConfigConstants.CERBERUS_AMI_TAG_NAME, stackAmiTagValueMap.get(stack))) { throw new IllegalStateException(ConfigConstants.AMI_TAG_CHECK_ERROR_MESSAGE); } } diff --git a/src/main/java/com/nike/cerberus/service/AutoScalingService.java b/src/main/java/com/nike/cerberus/service/AutoScalingService.java index 1e5a19db..7f2ec56f 100644 --- a/src/main/java/com/nike/cerberus/service/AutoScalingService.java +++ b/src/main/java/com/nike/cerberus/service/AutoScalingService.java @@ -78,6 +78,7 @@ public List getPublicDnsForAutoScalingGroup(final String logicalId) { /** * Updates the minimum number of instances allowed in the auto scaling group + * * @param logicalId - Name of the auto scaling group */ public void updateMinInstancesForAutoScalingGroup(final String logicalId, final int minInstances) { @@ -93,7 +94,8 @@ public void updateMinInstancesForAutoScalingGroup(final String logicalId, final * Set an EC2 instance to standby state, so that the desired instance count on the AutoScaling group is decreased * and a new instance is not spun up on instance reboot. This also removes the instance from the ELB, so that the * instance is not terminated when the health check fails. - * @param logicalId - Name of the auto scaling group + * + * @param logicalId - Name of the auto scaling group * @param instanceId - ID of the EC2 instance */ public void setInstanceStateToStandby(final String logicalId, final String instanceId) { @@ -108,7 +110,8 @@ public void setInstanceStateToStandby(final String logicalId, final String insta /** * Signify that the EC2 instance is now in service and ready to be re-added to the ELB and AutoScaling group. This * will also increase the desired instance count for the ASG. - * @param logicalId - Name of the auto scaling group + * + * @param logicalId - Name of the auto scaling group * @param instanceId - ID of the EC2 instance */ public void setInstanceStateToInService(final String logicalId, final String instanceId) { diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index b3485233..6c9580a7 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -69,7 +69,7 @@ /** * Service for managing Cerificates and calls to the ACME cert provider - * + *

* TODO: Update TOS * TODO: Rotate ACME user private key * TODO: Support Venafi ACME Impl @@ -132,8 +132,7 @@ public CertificateService(ConsoleService console, * it somewhere. If you need to get access to your account later, reconnect to it via * {@link Registration#bind(Session, URI)} by using the stored location. * - * @param session - * {@link Session} to bind with + * @param session {@link Session} to bind with * @return {@link Registration} connected to your account */ protected Registration findOrRegisterAccount(Session session, String contactEmail) throws AcmeException, IOException { @@ -147,7 +146,7 @@ protected Registration findOrRegisterAccount(Session session, String contactEmai // We won't be able to authorize domains until the ToS is accepted. URI agreement = reg.getAgreement(); - if (! (agreement == null)) { + if (!(agreement == null)) { acceptAgreement(reg, agreement); } } catch (AcmeConflictException ex) { @@ -162,14 +161,14 @@ protected Registration findOrRegisterAccount(Session session, String contactEmai /** * Prompts a user to read and accept or deny a ACME providers TOS * - * @param reg The registration object + * @param reg The registration object * @param agreement The link to the TOS */ protected void acceptAgreement(Registration reg, URI agreement) { try { log.info("Please download and review the Terms of Service: " + agreement); String userResponse = console.readLine("Type \"I Accept\" to accept the Terms of Service, anything else will exit: "); - if (! StringUtils.equalsIgnoreCase("I Accept", userResponse)) { + if (!StringUtils.equalsIgnoreCase("I Accept", userResponse)) { throw new RuntimeException("User did not accept the Terms of Service"); } reg.modify().setAgreement(agreement).commit(); @@ -182,8 +181,8 @@ protected void acceptAgreement(Registration reg, URI agreement) { /** * Creates a Txt record in Route53 * - * @param name The record name - * @param digest The value for the txt record + * @param name The record name + * @param digest The value for the txt record * @param hostedZoneId The hosted zone id to create the record in */ protected void generateChallengeTxtEntry(String name, String digest, String hostedZoneId) { @@ -253,10 +252,10 @@ protected KeyPair loadOrCreateKeyPair(File file) throws IOException { /** * Generates the certs needed for a Cerberus Environment * - * @param certDir The folder to store the generated files - * @param commonName The primary CNAME for the cert + * @param certDir The folder to store the generated files + * @param commonName The primary CNAME for the cert * @param subjectAlternativeNames Additional CNAMEs for the cert - * @param hostedZoneId The hosted zone id that can be used to create txt records for the ACME ownership challenges + * @param hostedZoneId The hosted zone id that can be used to create txt records for the ACME ownership challenges */ public void generateCerts(File certDir, String acmeServerUrl, @@ -322,7 +321,7 @@ public void generateCerts(File certDir, /** * Takes a challenge and performs it * - * @param challenge The challenge + * @param challenge The challenge * @param domainName The domain that is being verified * @param hostedZone The hosted zone id if applicable */ @@ -338,7 +337,7 @@ protected void doChallenge(Challenge challenge, String domainName, String hosted /** * Attempts to get an acceptable challenge from the ACME provider. - * + *

* This CLI currently only supports DNS-01 Challenges * * @param authorization The authorization object from the ACME provider @@ -351,7 +350,7 @@ protected Collection getChallenges(Authorization authorization) { for (String[] desiredChallengeCombo : desiredChallengeCombos) { Collection challenges = authorization.findCombination(desiredChallengeCombo); - if (! challenges.isEmpty()) { + if (!challenges.isEmpty()) { return challenges; } } @@ -366,8 +365,8 @@ protected Collection getChallenges(Authorization authorization) { /** * Performs the ACME DNS 01 Challenge by creating txt records in Route 53 * - * @param challenge The ACME challenge info with digest - * @param domainName The domain name that is being verified + * @param challenge The ACME challenge info with digest + * @param domainName The domain name that is being verified * @param hostedZoneId The hosted zone id that has permissions to create dns records for the domain name */ protected void doDns01Challenge(Dns01Challenge challenge, String domainName, String hostedZoneId) { @@ -380,7 +379,7 @@ protected void doDns01Challenge(Dns01Challenge challenge, String domainName, Str log.info("Waiting for name: '{}' to have txt record digest value: '{}' before triggering challenge", cname, digest); Thread.sleep(TimeUnit.SECONDS.toMillis(60)); recordValue = getTxtRecordValue(cname); - } while (! recordValue.isPresent() || ! recordValue.get().equals(String.format("\"%s\"", digest))); + } while (!recordValue.isPresent() || !recordValue.get().equals(String.format("\"%s\"", digest))); } catch (InterruptedException e) { throw new RuntimeException("Failed to complete DNS 01 challenge", e); } diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index 9880cf0c..0b47582e 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -238,6 +238,7 @@ public List getStackEvents(final String stackId) { /** * Get the full ID of the stack + * * @param stackName - The stack logical id / full stack name * @return */ @@ -292,10 +293,10 @@ public Collection convertParameters(final Map paramet } - /** * Since there doesn't appear to be a first class way through the SDK at this time to get a CF export. We can * iterate through the stacks for a given output key and return the value. + * * @param outputKey The exported CF variable to search and retrieve the value of. * @return The value for the export if found */ @@ -381,7 +382,7 @@ public StackStatus waitForStatus(final String stackId, final HashSet getTags(Map globalTags) { Set tags = new HashSet<>(); - globalTags.forEach((k,v) -> { + globalTags.forEach((k, v) -> { tags.add(new Tag().withKey(k).withValue(v)); }); diff --git a/src/main/java/com/nike/cerberus/service/Ec2Service.java b/src/main/java/com/nike/cerberus/service/Ec2Service.java index f2476882..5ac9ab95 100644 --- a/src/main/java/com/nike/cerberus/service/Ec2Service.java +++ b/src/main/java/com/nike/cerberus/service/Ec2Service.java @@ -21,7 +21,6 @@ import com.amazonaws.services.ec2.model.AvailabilityZone; import com.amazonaws.services.ec2.model.AvailabilityZoneState; import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult; -import com.amazonaws.services.ec2.model.DescribeInstanceStatusRequest; import com.amazonaws.services.ec2.model.DescribeInstancesRequest; import com.amazonaws.services.ec2.model.DescribeInstancesResult; import com.amazonaws.services.ec2.model.DescribeKeyPairsRequest; @@ -30,15 +29,12 @@ import com.amazonaws.services.ec2.model.ImportKeyPairRequest; import com.amazonaws.services.ec2.model.ImportKeyPairResult; import com.amazonaws.services.ec2.model.Instance; -import com.amazonaws.services.ec2.model.InstanceStatus; import com.amazonaws.services.ec2.model.RebootInstancesRequest; -import com.amazonaws.services.ec2.model.Reservation; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import javax.inject.Inject; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -65,7 +61,7 @@ public Ec2Service(final AmazonEC2 ec2Client) { /** * Import a key pair to AWS EC2. * - * @param keyName Friendly name for the key + * @param keyName Friendly name for the key * @param publicKeyMaterial Public key * @return Key name */ @@ -112,9 +108,10 @@ public List getAvailabilityZones() { /** * Gets all EC2 instances with the given tag key/value pair - * @param tagKey - Key of the tag + * + * @param tagKey - Key of the tag * @param tagValue - Value of the tag - * @param filters - Array of EC2 filters + * @param filters - Array of EC2 filters * @return - List of instances with the given tag */ public List getInstancesByTag(final String tagKey, final String tagValue, final Filter... filters) { @@ -137,6 +134,7 @@ public List getInstancesByTag(final String tagKey, final String tagVal /** * Reboots the EC2 instance with the given ID + * * @param instanceId - EC2 instance ID */ public void rebootEc2Instance(final String instanceId) { diff --git a/src/main/java/com/nike/cerberus/service/IdentityManagementService.java b/src/main/java/com/nike/cerberus/service/IdentityManagementService.java index e148773a..048b0691 100644 --- a/src/main/java/com/nike/cerberus/service/IdentityManagementService.java +++ b/src/main/java/com/nike/cerberus/service/IdentityManagementService.java @@ -42,11 +42,11 @@ public IdentityManagementService(final AmazonIdentityManagement client) { /** * Uploads a server certificate to AWS IAM. * - * @param name The server certificate name. No spaces. - * @param path Path to store the certificate under. - * @param body PEM-encoded certificate body. + * @param name The server certificate name. No spaces. + * @param path Path to store the certificate under. + * @param body PEM-encoded certificate body. * @param chain PEM-encoded certificate chain. - * @param key PEM-encoded certificate key. + * @param key PEM-encoded certificate key. * @return The server certificate ID of the uploaded certificate. */ public String uploadServerCertificate( diff --git a/src/main/java/com/nike/cerberus/service/KmsService.java b/src/main/java/com/nike/cerberus/service/KmsService.java index 6b939b31..5f4b7398 100644 --- a/src/main/java/com/nike/cerberus/service/KmsService.java +++ b/src/main/java/com/nike/cerberus/service/KmsService.java @@ -52,11 +52,12 @@ public KmsService(KmsClientFactory kmsClientFactory) { /** * Create keys with a shared alias in multiple regions - * @param regions the target regions to create CMKs in - * @param alias the alias for the keys - * @param policy the policy + * + * @param regions the target regions to create CMKs in + * @param alias the alias for the keys + * @param policy the policy * @param description the key description - * @param tags tags to apply to the keys + * @param tags tags to apply to the keys * @return the ARNs of the created CMKs */ public List createKeysAndAliases(List regions, String alias, String policy, String description, Map tags) { diff --git a/src/main/java/com/nike/cerberus/service/MetricsService.java b/src/main/java/com/nike/cerberus/service/MetricsService.java index 36e0b3ad..49814fa8 100644 --- a/src/main/java/com/nike/cerberus/service/MetricsService.java +++ b/src/main/java/com/nike/cerberus/service/MetricsService.java @@ -50,7 +50,7 @@ public void trackMetric(MetricType metricType, String key, Integer value, Map get(String path) { Optional s3ObjectOptional = getS3Object(path); - if (! s3ObjectOptional.isPresent()) { + if (!s3ObjectOptional.isPresent()) { return Optional.empty(); } @@ -94,8 +94,7 @@ protected Optional getS3Object(String path) { GetObjectRequest request = new GetObjectRequest(s3Bucket, getFullPath(path)); try { return Optional.of(s3Client.getObject(request)); - } - catch (AmazonServiceException ase) { + } catch (AmazonServiceException ase) { if (StringUtils.equalsIgnoreCase(ase.getErrorCode(), "NoSuchKey")) { logger.debug(String.format("The S3 object doesn't exist. Bucket: %s, Key: %s", s3Bucket, request.getKey())); return Optional.empty(); @@ -108,7 +107,7 @@ protected Optional getS3Object(String path) { public Map getS3ObjectUserMetaData(String path) { Optional encryptedObjectOptional = getS3Object(path); - if (! encryptedObjectOptional.isPresent()) { + if (!encryptedObjectOptional.isPresent()) { throw new RuntimeException("Cannot get metadata for an S3 Object that is not present"); } S3Object s3Object = encryptedObjectOptional.get(); @@ -119,10 +118,10 @@ public Set getKeysInPartialPath(String path) { ObjectListing objectListing = s3Client.listObjects(s3Bucket, getFullPath(path)); return objectListing - .getObjectSummaries() - .stream() - .map(objectSummary -> StringUtils.stripStart(objectSummary.getKey(), s3Prefix + "/")) - .collect(Collectors.toSet()); + .getObjectSummaries() + .stream() + .map(objectSummary -> StringUtils.stripStart(objectSummary.getKey(), s3Prefix + "/")) + .collect(Collectors.toSet()); } /** diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index eeb647ed..ab6fb5d1 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -224,7 +224,7 @@ public Optional getCertPart(final Stack stack, final String part) { /** * Stores the certificate files encrypted and adds the certificate name to the environment data. * - * @param stack Stack that the cert is for + * @param stack Stack that the cert is for * @param certificateName Certificate name in IAM * @param caContents CA chain * @param certContents Certificate body @@ -519,7 +519,7 @@ public GatewayParameters getGatewayStackParamters() { /** * Get the stack outputs for a specific stack name. * - * @param stack Stack name + * @param stack Stack name * @param outputClass Outputs class * @param Outputs type * @return Outputs @@ -560,7 +560,7 @@ public M getStackOutputs(final String stackName, final Class outputClass) /** * Get the stack parameters for a specific stack name. * - * @param stack Stack name + * @param stack Stack name * @param parameterClass Parameters class * @param Parameters type * @return Parameters diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index d32eab23..fea347c3 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -26,7 +26,6 @@ import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; -import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; import org.junit.Before; @@ -283,8 +282,8 @@ private void assertArgsAreEqual(String[] expected, String[] actual, String comma boolean isPairE = false; String eVal = ""; if (i < expected.length - 1) { - eVal = expected[i+1]; - if (! StringUtils.startsWith(eVal, "-")) { + eVal = expected[i + 1]; + if (!StringUtils.startsWith(eVal, "-")) { isPairE = true; } } @@ -300,8 +299,8 @@ private void assertArgsAreEqual(String[] expected, String[] actual, String comma boolean isPairA = false; String aVal = ""; if (n < actual.length - 1) { - aVal = actual[n+1]; - if (! StringUtils.startsWith(aVal, "-")) { + aVal = actual[n + 1]; + if (!StringUtils.startsWith(aVal, "-")) { isPairA = true; } } @@ -313,12 +312,12 @@ private void assertArgsAreEqual(String[] expected, String[] actual, String comma if (isPairE && isPairA && eKey.equals(aKey) && eVal.equals(aVal)) { found = true; break; - } else if (! isPairE && ! isPairA && eKey.equals(aKey)) { + } else if (!isPairE && !isPairA && eKey.equals(aKey)) { found = true; break; } } - if (! found) { + if (!found) { if (isPairE) { fail(String.format("Failed to find [ %s %s ] in actual args \nactual args: %s\nexpected args: %s\nactual size: %s\nexpected size: %s", eKey, eVal, String.join(" ", actual), String.join(" ", expected), actual.length, expected.length)); } else { diff --git a/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java b/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java index cedc99b4..a0bfb56b 100644 --- a/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java +++ b/src/test/java/com/nike/cerberus/command/CerberusCommandTest.java @@ -19,11 +19,7 @@ import com.beust.jcommander.JCommander; import org.junit.Test; -import java.io.File; import java.net.Proxy; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Paths; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/com/nike/cerberus/command/validator/EnvironmentNameValidatorTest.java b/src/test/java/com/nike/cerberus/command/validator/EnvironmentNameValidatorTest.java index 0ff2fdbd..8639913d 100644 --- a/src/test/java/com/nike/cerberus/command/validator/EnvironmentNameValidatorTest.java +++ b/src/test/java/com/nike/cerberus/command/validator/EnvironmentNameValidatorTest.java @@ -19,8 +19,6 @@ import com.beust.jcommander.ParameterException; import org.junit.Test; -import javax.print.attribute.standard.MediaSize; - public class EnvironmentNameValidatorTest { private final static String NAME = "NAME"; diff --git a/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java b/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java index 77d71e28..4bc59199 100644 --- a/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java +++ b/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java @@ -67,6 +67,4 @@ public void test_that_process_backup_can_handle_nested_maps() throws IOException } - - } diff --git a/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java b/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java index 10993704..3d1df1d1 100644 --- a/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java @@ -24,8 +24,6 @@ import com.amazonaws.services.ec2.model.Image; import org.junit.Test; -import java.util.List; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -51,7 +49,7 @@ public void isAmiWithTagExistTrue() { ) ).thenReturn( new DescribeImagesResult().withImages(new Image()) - ); + ); // invoke method under test assertTrue(amiTagCheckService.isAmiWithTagExist(amiId, tagName, tagValue)); @@ -73,7 +71,7 @@ public void isAmiWithTagExistFalse() { ) ).thenReturn( new DescribeImagesResult() - ); + ); // invoke method under test assertFalse(amiTagCheckService.isAmiWithTagExist(amiId, tagName, tagValue)); diff --git a/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java b/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java index 197dc98c..0c58fc0d 100644 --- a/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java @@ -132,8 +132,8 @@ public void testIncrementMinInstancesForAsgHappy() { .withAutoScalingGroups( new AutoScalingGroup().withInstances( new Instance().withInstanceId(instanceId)) - .withMinSize(minSize) - ) + .withMinSize(minSize) + ) ); autoScalingService.updateMinInstancesForAutoScalingGroup(logicalId, minSize - 1); diff --git a/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java b/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java index 740b37ea..4681a98d 100644 --- a/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java +++ b/src/test/java/com/nike/cerberus/service/SaltGeneratorTest.java @@ -5,8 +5,8 @@ import java.security.NoSuchAlgorithmException; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; public class SaltGeneratorTest { From 8ede752fc082280d3b94b5eab9abef3eabad0eb6 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 15 Nov 2017 10:41:28 -0800 Subject: [PATCH 38/69] =?UTF-8?q?Create=20delete=20stack=20command=20and?= =?UTF-8?q?=20make=20CloudFormation=20Service=20use=20AWS=20S=E2=80=A6=20(?= =?UTF-8?q?#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create delete stack command and make CloudFormation Service use AWS SDK Waiters * take care of findbugs issues * remove python dependency as its broken and not needed for highlander * misc clean up from everyones refactoring * change the default policy name to the real name, aws docs online are wrong * rename method to in CloudFormation Service * remove duplicated code --- .travis.yml | 4 - .../com/nike/cerberus/cli/CerberusRunner.java | 7 + .../DeleteCerberusEnvironmentCommand.java | 29 +++ .../command/core/DeleteStackCommand.java | 61 +++++ .../domain/environment/Environment.java | 5 +- .../cerberus/domain/environment/Stack.java | 16 +- .../domain/environment/StackDeserializer.java | 41 ---- .../environment/StackKeyDeserializer.java | 15 ++ .../domain/environment/StackSerializer.java | 39 --- .../cerberus/domain/input/SecurityGroups.java | 33 --- .../cms/CreateCmsClusterOperation.java | 16 +- .../DeleteCerberusEnvironmentOperation.java | 76 ++++++ .../operation/core/CreateBaseOperation.java | 24 +- .../core/CreateDatabaseOperation.java | 15 +- .../core/CreateLoadBalancerOperation.java | 12 +- .../core/CreateRoute53Operation.java | 13 +- .../core/CreateSecurityGroupsOperation.java | 13 +- .../operation/core/CreateVpcOperation.java | 12 +- .../operation/core/CreateWafOperation.java | 13 +- .../operation/core/DeleteStackOperation.java | 79 ++++++ .../operation/core/UpdateStackOperation.java | 23 +- .../service/CloudFormationService.java | 230 +++++++++++------- .../com/nike/cerberus/store/ConfigStore.java | 15 +- .../cloudformation/load-balancer.yaml | 2 +- .../com/nike/cerberus/domain/StackTest.java | 26 +- 25 files changed, 470 insertions(+), 349 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/StackKeyDeserializer.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java delete mode 100644 src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java diff --git a/.travis.yml b/.travis.yml index 02de1a38..04704516 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,6 @@ language: java dist: trusty jdk: - oraclejdk8 -before_install: - - sudo apt-get -y install python3-pip python-dev - - python3 -V - - pip3 -V deploy: skip_cleanup: true provider: releases diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 5d58b5ad..4aa6ecc9 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -32,6 +32,7 @@ import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand; +import com.nike.cerberus.command.composite.DeleteCerberusEnvironmentCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; @@ -42,6 +43,7 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; +import com.nike.cerberus.command.core.DeleteStackCommand; import com.nike.cerberus.command.core.GenerateCertsCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; import com.nike.cerberus.command.core.RestoreCerberusBackupCommand; @@ -126,6 +128,9 @@ public void run(String[] args) { System.exit(1); } } + + // Shutdown the thread pool executors + System.exit(0); } /** @@ -188,6 +193,8 @@ private void registerAllCommands() { registerCommand(new CreateLoadBalancerCommand()); registerCommand(new CreateEdgeDomainRecordCommand()); registerCommand(new CreateCerberusEnvironmentCommand()); + registerCommand(new DeleteStackCommand()); + registerCommand(new DeleteCerberusEnvironmentCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java b/src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java new file mode 100644 index 00000000..a62f832c --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java @@ -0,0 +1,29 @@ +package com.nike.cerberus.command.composite; + +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.composite.DeleteCerberusEnvironmentOperation; + +import static com.nike.cerberus.command.composite.DeleteCerberusEnvironmentCommand.COMMAND_NAME; + +@Parameters( + commandNames = { + COMMAND_NAME + }, + commandDescription = "Deletes all the cf stacks, and data associated with a Cerberus environment that is managed by the CLI" +) +public class DeleteCerberusEnvironmentCommand implements Command { + + public static final String COMMAND_NAME = "delete-cerberus-environment"; + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return DeleteCerberusEnvironmentOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java b/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java new file mode 100644 index 00000000..4929038c --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.Command; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.DeleteStackOperation; +import com.nike.cerberus.util.StackConverter; + +import static com.nike.cerberus.command.core.DeleteStackCommand.COMMAND_NAME; + +@Parameters( + commandNames = { + COMMAND_NAME + }, + commandDescription = "Uses CloudFormation to delete a Stack" +) +public class DeleteStackCommand implements Command { + + public static final String COMMAND_NAME = "delete-stack"; + + public static final String STACK_NAME_LONG_ARG = "--stack-name"; + + @Parameter( + names = {STACK_NAME_LONG_ARG}, + required = true, + description = "Stack name to delete", + converter = StackConverter.class) + private Stack stack; + + public Stack getStack() { + return stack; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return DeleteStackOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java index 0ebbc2db..8be193f7 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Environment.java @@ -16,6 +16,8 @@ package com.nike.cerberus.domain.environment; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -28,8 +30,9 @@ public class Environment { private String domainName; + @JsonDeserialize(keyUsing = StackKeyDeserializer.class) private Map stackMap; - + @JsonDeserialize(keyUsing = StackKeyDeserializer.class) private Map serverCertificateIdMap; private String configKeyId; 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 a1053c11..69f7725a 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -16,8 +16,6 @@ package com.nike.cerberus.domain.environment; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.collect.ImmutableList; import com.nike.cerberus.ConfigConstants; import org.apache.commons.io.IOUtils; @@ -32,9 +30,7 @@ /** * Describes the stacks that make up Cerberus. */ -@JsonSerialize(using = StackSerializer.class) -@JsonDeserialize(using = StackDeserializer.class) -public class Stack { +public class Stack implements Comparable { public static final Stack BASE = new Stack("base", "base.yaml", false); public static final Stack VAULT = new Stack("vault", null, false); @@ -137,10 +133,20 @@ public boolean equals(Object o) { } + @Override + public String toString() { + return getName(); + } + @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + templatePath.hashCode(); return result; } + + @Override + public int compareTo(Stack o) { + return this.name.compareTo(o.name); + } } diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java b/src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java deleted file mode 100644 index 0dbfea1d..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/StackDeserializer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2017 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.environment; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - -import java.io.IOException; - -public class StackDeserializer extends StdDeserializer { - - public StackDeserializer() { - this(null); - } - - public StackDeserializer(Class t) { - super(t); - } - - @Override - public Stack deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - String stackName = p.getCodec().readValue(p, String.class); - return Stack.fromName(stackName); - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackKeyDeserializer.java b/src/main/java/com/nike/cerberus/domain/environment/StackKeyDeserializer.java new file mode 100644 index 00000000..f027c044 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/StackKeyDeserializer.java @@ -0,0 +1,15 @@ +package com.nike.cerberus.domain.environment; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; + +import java.io.IOException; + +public class StackKeyDeserializer extends KeyDeserializer { + + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return Stack.fromName(key); + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java b/src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java deleted file mode 100644 index d58979b1..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/StackSerializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2017 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.environment; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import java.io.IOException; - -public class StackSerializer extends StdSerializer { - - public StackSerializer() { - this(null); - } - - public StackSerializer(Class t) { - super(t); - } - - @Override - public void serialize(Stack value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeString(value.getName()); - } -} diff --git a/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java b/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java deleted file mode 100644 index 64f9d1c2..00000000 --- a/src/main/java/com/nike/cerberus/domain/input/SecurityGroups.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2017 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.input; - -/** - * Stores Management Service-specific parameters parsed from YAML - */ -public class SecurityGroups { - - private String loadBalancerCidrBlock; - - public String getLoadBalancerCidrBlock() { - return loadBalancerCidrBlock; - } - - public void setLoadBalancerCidrBlock(String loadBalancerCidrBlock) { - this.loadBalancerCidrBlock = loadBalancerCidrBlock; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index d94e6919..3ca1311f 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -16,10 +16,8 @@ package com.nike.cerberus.operation.cms; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.domain.EnvironmentMetadata; @@ -27,7 +25,6 @@ import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; @@ -112,20 +109,9 @@ public void run(final CreateCmsClusterCommand command) { // allow user to overwrite CloudFormation parameters with -P option parameters.putAll(command.getStackDelegate().getDynamicParameters()); - final String stackId = cloudFormationService.createStack(Stack.CMS, + cloudFormationService.createStackAndWait(Stack.CMS, parameters, true, command.getStackDelegate().getTagParameters().getTags()); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - final String errorMessage = String.format("Unexpected end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java new file mode 100644 index 00000000..2f965f83 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java @@ -0,0 +1,76 @@ +package com.nike.cerberus.operation.composite; + +import com.google.common.collect.ImmutableList; +import com.nike.cerberus.command.composite.DeleteCerberusEnvironmentCommand; +import com.nike.cerberus.command.core.DeleteStackCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.service.ConsoleService; + +import javax.inject.Inject; +import java.util.LinkedList; +import java.util.List; + +import static com.nike.cerberus.domain.environment.Stack.*; + +public class DeleteCerberusEnvironmentOperation extends CompositeOperation { + + private final ConsoleService consoleService; + + private final EnvironmentMetadata environmentMetadata; + + @Inject + public DeleteCerberusEnvironmentOperation(ConsoleService consoleService, + EnvironmentMetadata environmentMetadata) { + + this.consoleService = consoleService; + this.environmentMetadata = environmentMetadata; + } + + @Override + protected List getCompositeCommandChain() { + List chainableCommandList = new LinkedList<>(); + + ImmutableList.of( + ROUTE53, + WAF, + CMS, + LOAD_BALANCER, + DATABASE, + SECURITY_GROUPS, + VPC, + BASE + ).forEach(stack -> + chainableCommandList.add(ChainableCommand.Builder.create() + .withCommand(new DeleteStackCommand()) + .withAdditionalArg(DeleteStackCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(stack.getName()) + .build()) + ); + + return chainableCommandList; + } + + @Override + public boolean isRunnable(DeleteCerberusEnvironmentCommand command) { + try { + String warning = String.format( + "This will delete the environment '%s' including all the secure data.", + environmentMetadata.getName() + ); + consoleService.askUserToProceed(warning, ConsoleService.DefaultAction.NO); + } catch (RuntimeException e) { + return false; + } + return true; + } + + @Override + public boolean isEnvironmentConfigRequired() { + return false; + } + + @Override + public boolean skipOnNotRunnable() { + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index e97d1fb0..2e0d5a21 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -16,16 +16,13 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.BaseParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; @@ -73,22 +70,15 @@ public void run(final CreateBaseCommand command) { final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); - final String stackId = cloudFormationService.createStack(Stack.BASE, parameters, true, + cloudFormationService.createStackAndWait(Stack.BASE, parameters, true, command.getTagsDelegate().getTags()); - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (StackStatus.CREATE_COMPLETE == endStatus) { - logger.info("Stack creation complete, initializing the configuration bucket."); - environmentMetadata.setBucketName(configStore.getBaseStackOutputs().getConfigBucketName()); - configStore.initEnvironmentData(); - configStore.initSecretsData(); - logger.info("Initialization complete."); - } else { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } + + logger.info("Stack creation complete, initializing the configuration bucket."); + environmentMetadata.setBucketName(configStore.getBaseStackOutputs().getConfigBucketName()); + configStore.initEnvironmentData(); + configStore.initSecretsData(); + logger.info("Initialization complete."); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index b37e5a11..a5863819 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -16,10 +16,8 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.DatabaseParameters; @@ -27,7 +25,6 @@ import com.nike.cerberus.domain.cloudformation.VpcParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.RandomStringGenerator; @@ -89,18 +86,10 @@ public void run(final CreateDatabaseCommand command) { }; final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); - String stackId = cloudFormationService.createStack(Stack.DATABASE, parameters, true, + cloudFormationService.createStackAndWait(Stack.DATABASE, parameters, true, command.getTagsDelegate().getTags()); - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (StackStatus.CREATE_COMPLETE == endStatus) { - configStore.storeCmsDatabasePassword(databasePassword); - } else { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } + configStore.storeCmsDatabasePassword(databasePassword); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index 228fa300..6d4e0682 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -16,17 +16,14 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; @@ -89,15 +86,8 @@ public void run(final CreateLoadBalancerCommand command) { }; final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); - final String stackId = cloudFormationService.createStack(Stack.LOAD_BALANCER, parameters, true, + cloudFormationService.createStackAndWait(Stack.LOAD_BALANCER, parameters, true, command.getTagsDelegate().getTags()); - - StackStatus endStatus = cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index b0c776f3..3773ab4c 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -16,16 +16,13 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Route53Service; import org.apache.commons.lang3.StringUtils; @@ -78,16 +75,8 @@ public void run(final CreateRoute53Command command) { }; final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); - final String stackId = cloudFormationService.createStack(Stack.ROUTE53, parameters, true, + cloudFormationService.createStackAndWait(Stack.ROUTE53, parameters, true, command.getTagsDelegate().getTags()); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (StackStatus.CREATE_COMPLETE != endStatus) { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index c0c3b2ef..4610f93c 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -16,17 +16,14 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; @@ -75,16 +72,8 @@ public void run(final CreateSecurityGroupsCommand command) { }; final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); - String stackId = cloudFormationService.createStack(Stack.SECURITY_GROUPS, parameters, true, + cloudFormationService.createStackAndWait(Stack.SECURITY_GROUPS, parameters, true, command.getTagParameters().getTags()); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (StackStatus.CREATE_COMPLETE != endStatus) { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index dc8a89f2..41dab0d2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -16,17 +16,14 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.beust.jcommander.internal.Maps; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.VpcParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2Service; import org.slf4j.Logger; @@ -79,15 +76,8 @@ public void run(final CreateVpcCommand command) { }; final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); - final String stackId = cloudFormationService.createStack(Stack.VPC, parameters, true, + cloudFormationService.createStackAndWait(Stack.VPC, parameters, true, command.getTagsDelegate().getTags()); - - StackStatus endStatus = cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (endStatus != StackStatus.CREATE_COMPLETE) { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index 830a89fe..052d98e0 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -16,16 +16,13 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.WafParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; @@ -75,16 +72,8 @@ public void run(final CreateWafCommand command) { }; final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); - String stackId = cloudFormationService.createStack(Stack.WAF, parameters, true, + cloudFormationService.createStackAndWait(Stack.WAF, parameters, true, command.getTagsDelegate().getTags()); - - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet(StackStatus.CREATE_COMPLETE, StackStatus.ROLLBACK_COMPLETE)); - - if (StackStatus.CREATE_COMPLETE != endStatus) { - throw new UnexpectedCloudFormationStatusException(String.format("Unexpected end status: %s", endStatus.name())); - } } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java new file mode 100644 index 00000000..85d5e40b --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 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.services.s3.AmazonS3; +import com.amazonaws.services.s3.iterable.S3Versions; +import com.amazonaws.services.s3.model.S3VersionSummary; +import com.google.inject.Inject; +import com.nike.cerberus.command.core.DeleteStackCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DeleteStackOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + + private final CloudFormationService cloudFormationService; + + private final AmazonS3 amazonS3; + + @Inject + public DeleteStackOperation(EnvironmentMetadata environmentMetadata, + CloudFormationService cloudFormationService, AmazonS3 amazonS3) { + + this.environmentMetadata = environmentMetadata; + this.cloudFormationService = cloudFormationService; + this.amazonS3 = amazonS3; + } + + @Override + public void run(DeleteStackCommand command) { + String stackName = command.getStack().getFullName(environmentMetadata.getName()); + cloudFormationService.getStackResources(stackName) + .forEach(stackResourceSummary -> { + if (stackResourceSummary.getResourceType().equals("AWS::S3::Bucket")) { + String bucketName = stackResourceSummary.getPhysicalResourceId(); + log.info("Detected S3 Bucket: {}, emptying contents before deleting stack", bucketName); + for (S3VersionSummary version : S3Versions.inBucket(amazonS3, bucketName)) { + String key = version.getKey(); + String versionId = version.getVersionId(); + amazonS3.deleteVersion(bucketName, key, versionId); + } + } + }); + + log.info("Deleting stack: {}", stackName); + cloudFormationService.deleteStackAndWait(stackName); + log.info("Finished deleting stack: {}", stackName); + } + + @Override + public boolean isRunnable(DeleteStackCommand command) { + if (! cloudFormationService.isStackPresent(command.getStack().getFullName(environmentMetadata.getName()))) { + log.error("The stack: {} does not exists", command.getStack()); + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 3beb9fd8..7cfe5492 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -17,14 +17,10 @@ package com.nike.cerberus.operation.core; import com.amazonaws.AmazonServiceException; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; import org.apache.commons.lang3.StringUtils; @@ -85,26 +81,9 @@ public void run(final UpdateStackCommand command) { try { logger.info("Starting the update for '{}' overwrite:{}.", stackId, command.isOverwriteTemplate()); - cloudFormationService.updateStack(command.getStack(), parameters, true, command.isOverwriteTemplate(), + cloudFormationService.updateStackAndWait(command.getStack(), parameters, true, command.isOverwriteTemplate(), command.getTagsDelegate().getTags()); - final StackStatus endStatus = - cloudFormationService.waitForStatus(stackId, - Sets.newHashSet( - UPDATE_COMPLETE, - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS, - UPDATE_ROLLBACK_COMPLETE, - UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS, - UPDATE_ROLLBACK_FAILED - )); - - if (!ImmutableList.of(UPDATE_COMPLETE, UPDATE_COMPLETE_CLEANUP_IN_PROGRESS).contains(endStatus)) { - final String errorMessage = String.format("CloudFormation reports that updating the stack was not successful. end status: %s", endStatus.name()); - logger.error(errorMessage); - - throw new UnexpectedCloudFormationStatusException(errorMessage); - } - logger.info("Update complete."); } catch (AmazonServiceException ase) { if (ase.getStatusCode() == 400 && diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index 0b47582e..376ada6c 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -17,6 +17,7 @@ package com.nike.cerberus.service; import com.amazonaws.AmazonServiceException; +import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.services.cloudformation.AmazonCloudFormation; import com.amazonaws.services.cloudformation.model.CreateStackRequest; import com.amazonaws.services.cloudformation.model.CreateStackResult; @@ -25,12 +26,19 @@ import com.amazonaws.services.cloudformation.model.DescribeStackEventsResult; import com.amazonaws.services.cloudformation.model.DescribeStacksRequest; import com.amazonaws.services.cloudformation.model.DescribeStacksResult; +import com.amazonaws.services.cloudformation.model.ListStackResourcesRequest; +import com.amazonaws.services.cloudformation.model.ListStackResourcesResult; import com.amazonaws.services.cloudformation.model.Output; import com.amazonaws.services.cloudformation.model.Parameter; import com.amazonaws.services.cloudformation.model.StackEvent; +import com.amazonaws.services.cloudformation.model.StackResourceSummary; import com.amazonaws.services.cloudformation.model.StackStatus; import com.amazonaws.services.cloudformation.model.Tag; import com.amazonaws.services.cloudformation.model.UpdateStackRequest; +import com.amazonaws.services.cloudformation.waiters.AmazonCloudFormationWaiters; +import com.amazonaws.waiters.Waiter; +import com.amazonaws.waiters.WaiterHandler; +import com.amazonaws.waiters.WaiterParameters; import com.beust.jcommander.internal.Maps; import com.github.tomaslanger.chalk.Chalk; import com.google.common.base.Preconditions; @@ -39,6 +47,7 @@ import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -49,11 +58,14 @@ import javax.inject.Inject; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -72,11 +84,15 @@ public class CloudFormationService { private final EnvironmentMetadata environmentMetadata; + private final AmazonCloudFormationWaiters waiters; + @Inject public CloudFormationService(final AmazonCloudFormation cloudFormationClient, final EnvironmentMetadata environmentMetadata) { this.cloudFormationClient = cloudFormationClient; this.environmentMetadata = environmentMetadata; + + waiters = new AmazonCloudFormationWaiters(cloudFormationClient); } /** @@ -85,11 +101,14 @@ public CloudFormationService(final AmazonCloudFormation cloudFormationClient, * @param parameters Input parameters. * @return Stack ID */ - public String createStack(final Stack stack, - final Map parameters, - final boolean iamCapabilities, - final Map globalTags) { - logger.info(String.format("Executing the Cloud Formation: %s, Stack Name: %s", stack.getTemplatePath(), stack.getFullName(environmentMetadata.getName()))); + public String createStackAndWait(final Stack stack, + final Map parameters, + final boolean iamCapabilities, + final Map globalTags) { + + String stackName = stack.getFullName(environmentMetadata.getName()); + + logger.info(String.format("Executing the Cloud Formation: %s, Stack Name: %s", stack.getTemplatePath(), stackName)); final CreateStackRequest request = new CreateStackRequest() .withStackName(stack.getFullName(environmentMetadata.getName())) @@ -102,21 +121,77 @@ public String createStack(final Stack stack, } final CreateStackResult result = cloudFormationClient.createStack(request); + + waitAndPrintCFEvents(stackName, waiters.stackCreateComplete()); + return result.getStackId(); } + /** + * Uses AWS CF Aync Waiters to wait for Cloud Formation actions to complete, while logging events and verifying success + * @param stackName The stack that is having an action performed + * @param waiter The Amazon waiter + */ + private void waitAndPrintCFEvents(String stackName, Waiter waiter) { + SuccessTrackingWaiterHandler handler = new SuccessTrackingWaiterHandler(); + + @SuppressWarnings("unchecked") + Future future = waiter + .runAsync(new WaiterParameters<>(new DescribeStacksRequest().withStackName(stackName)), handler); + + do { + try { + DateTime now = DateTime.now(DateTimeZone.UTC).minusSeconds(10); + Set recordedStackEvents = Sets.newHashSet(); + + TimeUnit.SECONDS.sleep(5); + + StackStatus stackStatus = getStackStatus(stackName); + + if (stackStatus != null) { + List stackEvents = getStackEvents(stackName); + stackEvents.sort(Comparator.comparing(StackEvent::getTimestamp)); + + for (StackEvent stackEvent : stackEvents) { + DateTime eventTime = new DateTime(stackEvent.getTimestamp()); + if (!recordedStackEvents.contains(stackEvent.getEventId()) && now.isBefore(eventTime)) { + logger.info( + String.format("TS: %s, Status: %s, Type: %s, Reason: %s", + Chalk.on(stackEvent.getTimestamp().toString()).yellow(), + getStatusColor(stackEvent.getResourceStatus()), + Chalk.on(stackEvent.getResourceType()).yellow(), + Chalk.on(stackEvent.getResourceStatusReason()).yellow())); + + recordedStackEvents.add(stackEvent.getEventId()); + } + } + } + } catch (InterruptedException e) { + logger.error("Polling interrupted", e); + Thread.currentThread().interrupt(); + } catch (Exception e) { + logger.error("Failed to poll and print stack", e); + } + } while (!future.isDone()); + + if (!handler.wasSuccess) { + throw new UnexpectedCloudFormationStatusException( + String.format("Failed to create stack: %s, msg: %s", stackName, handler.getErrorMessage())); + } + } /** * Updates an existing stack */ - public void updateStack(final Stack stack, - final Map parameters, - final boolean iamCapabilities, - final boolean overwrite, - final Map globalTags) { + public void updateStackAndWait(final Stack stack, + final Map parameters, + final boolean iamCapabilities, + final boolean overwrite, + final Map globalTags) { + String stackName = stack.getFullName(environmentMetadata.getName()); final UpdateStackRequest request = new UpdateStackRequest() - .withStackName(stack.getFullName(environmentMetadata.getName())) + .withStackName(stackName) .withParameters(convertParameters(parameters)); if (overwrite) { @@ -132,27 +207,31 @@ public void updateStack(final Stack stack, request.setTags(getTags(globalTags)); cloudFormationClient.updateStack(request); + + waitAndPrintCFEvents(stackName, waiters.stackUpdateComplete()); + } /** * Deletes an existing stack by name. * - * @param stackId Stack ID. + * @param stackName Stack ID. */ - public void deleteStack(final String stackId) { - final DeleteStackRequest request = new DeleteStackRequest().withStackName(stackId); + public void deleteStackAndWait(final String stackName) { + final DeleteStackRequest request = new DeleteStackRequest().withStackName(stackName); cloudFormationClient.deleteStack(request); + waitAndPrintCFEvents(stackName, waiters.stackDeleteComplete()); } /** * Returns the current status of the named stack. * - * @param stackId Stack ID. + * @param stackName Stack ID. * @return Stack status data. */ @Nullable - public StackStatus getStackStatus(final String stackId) { - final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackId); + public StackStatus getStackStatus(final String stackName) { + final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); try { final DescribeStacksResult result = cloudFormationClient.describeStacks(request); @@ -177,11 +256,11 @@ public StackStatus getStackStatus(final String stackId) { /** * Returns the current status of the named stack. * - * @param stackId Stack name. + * @param stackName Stack name. * @return Stack outputs data. */ - public Map getStackParameters(final String stackId) { - final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackId); + public Map getStackParameters(final String stackName) { + final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); final DescribeStacksResult result = cloudFormationClient.describeStacks(request); final Map parameters = Maps.newHashMap(); @@ -197,11 +276,11 @@ public Map getStackParameters(final String stackId) { /** * Returns the current status of the named stack. * - * @param stackId Stack name. + * @param stackName Stack name. * @return Stack outputs data. */ - public Map getStackOutputs(final String stackId) { - final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackId); + public Map getStackOutputs(final String stackName) { + final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); final DescribeStacksResult result = cloudFormationClient.describeStacks(request); final Map outputs = Maps.newHashMap(); @@ -214,14 +293,27 @@ public Map getStackOutputs(final String stackId) { return outputs; } + public List getStackResources(String stackName) { + List stackResourceSummaries = new LinkedList<>(); + ListStackResourcesResult result; + do { + result = cloudFormationClient.listStackResources( + new ListStackResourcesRequest().withStackName(stackName) + ); + stackResourceSummaries.addAll(result.getStackResourceSummaries()); + } while (result.getNextToken() != null); + + return stackResourceSummaries; + } + /** * Returns the events for a named stack. * - * @param stackId Stack ID. + * @param stackName Stack ID. * @return Collection of events */ - public List getStackEvents(final String stackId) { - final DescribeStackEventsRequest request = new DescribeStackEventsRequest().withStackName(stackId); + public List getStackEvents(final String stackName) { + final DescribeStackEventsRequest request = new DescribeStackEventsRequest().withStackName(stackName); try { final DescribeStackEventsResult result = cloudFormationClient.describeStackEvents(request); @@ -264,13 +356,13 @@ public String getStackId(String stackName) { /** * Checks if a stack exists with the specified ID. * - * @param stackId Stack ID. + * @param stackName Stack ID. * @return boolean */ - public boolean isStackPresent(final String stackId) { - Preconditions.checkArgument(StringUtils.isNotBlank(stackId), "Stack ID cannot be blank"); + public boolean isStackPresent(final String stackName) { + Preconditions.checkArgument(StringUtils.isNotBlank(stackName), "Stack ID cannot be blank"); - return getStackStatus(stackId) != null; + return getStackStatus(stackName) != null; } /** @@ -321,59 +413,6 @@ public Optional searchStacksForOutput(String outputKey) { return Optional.empty(); } - /** - * Blocking call that waits for a stack change to complete. Times out if waiting more than 30 minutes. - * - * @param endStatuses Status to end on - * @return The final status - */ - public StackStatus waitForStatus(final String stackId, final HashSet endStatuses) { - DateTime now = DateTime.now(DateTimeZone.UTC).minusSeconds(10); - DateTime timeoutDateTime = now.plusMinutes(90); - Set recordedStackEvents = Sets.newHashSet(); - boolean isRunning = true; - StackStatus stackStatus = null; - - while (isRunning) { - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - logger.warn("Thread sleep interrupted. Continuing...", e); - } - stackStatus = getStackStatus(stackId); - - if (endStatuses.contains(stackStatus) || stackStatus == null) { - isRunning = false; - } - - if (stackStatus != null) { - List stackEvents = getStackEvents(stackId); - stackEvents.sort((o1, o2) -> o1.getTimestamp().compareTo(o2.getTimestamp())); - - for (StackEvent stackEvent : stackEvents) { - DateTime eventTime = new DateTime(stackEvent.getTimestamp()); - if (!recordedStackEvents.contains(stackEvent.getEventId()) && now.isBefore(eventTime)) { - logger.info( - String.format("TS: %s, Status: %s, Type: %s, Reason: %s", - Chalk.on(stackEvent.getTimestamp().toString()).yellow(), - getStatusColor(stackEvent.getResourceStatus()), - Chalk.on(stackEvent.getResourceType()).yellow(), - Chalk.on(stackEvent.getResourceStatusReason()).yellow())); - - recordedStackEvents.add(stackEvent.getEventId()); - } - } - } - - if (timeoutDateTime.isBeforeNow()) { - logger.error("Timed out waiting for CloudFormation completion status."); - isRunning = false; - } - } - - return stackStatus; - } - /** * Takes a map of key values and converts them to a collection of tags adding more global tags * @@ -404,4 +443,29 @@ private String getStatusColor(String status) { } return status; } + + /** + * Waiter Handler that keeps track of status + */ + private static class SuccessTrackingWaiterHandler extends WaiterHandler { + + boolean wasSuccess = false; + + Exception e; + + @Override + public void onWaitSuccess(AmazonWebServiceRequest request) { + wasSuccess = true; + } + + @Override + public void onWaitFailure(Exception e) { + this.e = e; + wasSuccess = false; + } + + public Object getErrorMessage() { + return e == null ? "unknown" : e.getMessage(); + } + } } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index ab6fb5d1..8e4850a8 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -377,6 +377,9 @@ public Optional getConfigProperties(String path) { return getEncryptedObject(path); } + /** + * returns the complete stack name + */ public String getStackLogicalId(Stack stack) { return stack.getFullName(environmentMetadata.getName()); } @@ -547,13 +550,11 @@ public M getStackOutputs(final Stack stack, final Class outputClass) { * @return Outputs */ public M getStackOutputs(final String stackName, final Class outputClass) { - final String stackId = Stack.fromName(stackName).getFullName(environmentMetadata.getName()); - - if (!cloudFormationService.isStackPresent(stackId)) { + if (!cloudFormationService.isStackPresent(stackName)) { throw new IllegalStateException("Failed to get CloudFormation output for stack: '" + stackName + "'. Stack does not exist."); } - final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); + final Map stackOutputs = cloudFormationService.getStackOutputs(stackName); return cloudFormationObjectMapper.convertValue(stackOutputs, outputClass); } @@ -587,13 +588,11 @@ public M getStackParameters(final Stack stack, final Class parameterClass * @return Parameters */ public M getStackParameters(final String stackName, final Class parameterClass) { - final String stackId = Stack.fromName(stackName).getFullName(environmentMetadata.getName()); - - if (!cloudFormationService.isStackPresent(stackId)) { + if (!cloudFormationService.isStackPresent(stackName)) { throw new IllegalStateException("Failed to get CloudFormation parameters for stack: '" + stackName + "'. Stack does not exist."); } - final Map stackOutputs = cloudFormationService.getStackParameters(stackId); + final Map stackOutputs = cloudFormationService.getStackParameters(stackName); return cloudFormationObjectMapper.convertValue(stackOutputs, parameterClass); } diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 8d869d8b..4e6e0e14 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -42,7 +42,7 @@ Parameters: see http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html for more information Type: String - Default: TLS-1-2-2017-01 + Default: ELBSecurityPolicy-TLS-1-2-2017-01 Resources: ALBListener: Type: AWS::ElasticLoadBalancingV2::Listener diff --git a/src/test/java/com/nike/cerberus/domain/StackTest.java b/src/test/java/com/nike/cerberus/domain/StackTest.java index 3c9ce5db..02fedc4c 100644 --- a/src/test/java/com/nike/cerberus/domain/StackTest.java +++ b/src/test/java/com/nike/cerberus/domain/StackTest.java @@ -18,32 +18,40 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.domain.environment.Environment; import com.nike.cerberus.domain.environment.Stack; import org.junit.Test; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class StackTest { @Test - public void test_stack_serializes_as_expected() throws JsonProcessingException { - Stack stack = Stack.CMS; + public void test_that_stack_can_go_into_serialized_map() throws JsonProcessingException { + Map map = new HashMap<>(); + map.put(Stack.CMS, "foo"); ObjectMapper objectMapper = new ObjectMapper(); - String json = objectMapper.writeValueAsString(stack); + String json = objectMapper.writeValueAsString(map); - assertEquals("\"cms\"", json); + assertEquals("{\"cms\":\"foo\"}", json); } @Test - public void test_stack_deserializes_as_expected() throws IOException { - Stack expected = Stack.CMS; - + public void test_that_stack_can_deserialize_from_map() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); - Stack actual = objectMapper.readValue("\"cms\"", Stack.class); - assertEquals(expected, actual); + Environment environment = new Environment(); + environment.getServerCertificateIdMap().put(Stack.CMS, "foo"); + String json = objectMapper.writeValueAsString(environment); + Environment actual = objectMapper.readValue(json, Environment.class); + + assertTrue(actual.getServerCertificateIdMap().size() == 1); } + } From 773213b3ed93f18e9142cd0f7fe7c941db480b42 Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Wed, 15 Nov 2017 10:55:30 -0800 Subject: [PATCH 39/69] Minor refactor: introducing CloudFormationObjectMapper to hide TypeReference and remove some duplication (#101) --- .../nike/cerberus/module/CerberusModule.java | 18 ---------- .../cms/CreateCmsClusterOperation.java | 16 ++++----- .../operation/core/CreateBaseOperation.java | 19 ++++------ .../core/CreateDatabaseOperation.java | 18 ++++------ .../core/CreateLoadBalancerOperation.java | 18 ++++------ .../core/CreateRoute53Operation.java | 18 ++++------ .../core/CreateSecurityGroupsOperation.java | 18 ++++------ .../operation/core/CreateVpcOperation.java | 16 ++++----- .../operation/core/CreateWafOperation.java | 18 ++++------ .../com/nike/cerberus/store/ConfigStore.java | 6 ++-- .../util/CloudFormationObjectMapper.java | 36 +++++++++++++++++++ 11 files changed, 93 insertions(+), 108 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/util/CloudFormationObjectMapper.java diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index 3b5203ff..d5c3e163 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -47,7 +47,6 @@ import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.sns.AmazonSNS; import com.amazonaws.services.sns.AmazonSNSClient; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; @@ -80,8 +79,6 @@ */ public class CerberusModule extends AbstractModule { - public static final String CF_OBJECT_MAPPER = "cloudformationObjectMapper"; - public static final String CONFIG_OBJECT_MAPPER = "configObjectMapper"; public static final String CERBERUS_ASSUME_ROLE_ARN = "CERBERUS_ASSUME_ROLE_ARN"; @@ -124,21 +121,6 @@ protected void configure() { bind(AmazonRoute53.class).toInstance(createAmazonClientInstance(AmazonRoute53Client.class, region)); } - /** - * Object mapper for handling CloudFormation parameters and outputs. - * - * @return Object mapper - */ - @Provides - @Singleton - @Named(CF_OBJECT_MAPPER) - public ObjectMapper cloudFormationObjectMapper() { - final ObjectMapper om = new ObjectMapper(); - om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - om.setSerializationInclusion(JsonInclude.Include.NON_ABSENT); - return om; - } - /** * Object mapper for handling configuration objects in the config bucket. * diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index 3ca1311f..2b920219 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.cms; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.domain.EnvironmentMetadata; @@ -29,16 +29,14 @@ import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; 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 java.util.Optional; -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; - /** * Operation for creating the CMS cluster. */ @@ -56,7 +54,7 @@ public class CreateCmsClusterOperation implements Operation> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters); // allow user to overwrite CloudFormation parameters with -P option parameters.putAll(command.getStackDelegate().getDynamicParameters()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index 2e0d5a21..871c9395 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.core; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.BaseParameters; @@ -25,15 +25,13 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; 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.CF_OBJECT_MAPPER; - /** * Creates the base components via CloudFormation used by all of Cerberus. */ @@ -47,17 +45,17 @@ public class CreateBaseOperation implements Operation { private final ConfigStore configStore; - private final ObjectMapper cloudFormationObjectMapper; + private final CloudFormationObjectMapper cloudFormationObjectMapper; @Inject public CreateBaseOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationObjectMapper cloudFormationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.configStore = configStore; - this.cloudFormationObjectMapper = cloudformationObjectMapper; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; } @Override @@ -65,10 +63,7 @@ public void run(final CreateBaseCommand command) { final BaseParameters baseParameters = new BaseParameters() .setAccountAdminArn(command.getAdminRoleArn()); - final TypeReference> typeReference = new TypeReference>() { - }; - - final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters); cloudFormationService.createStackAndWait(Stack.BASE, parameters, true, command.getTagsDelegate().getTags()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index a5863819..f37e4975 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.core; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.DatabaseParameters; @@ -27,16 +27,14 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.CloudFormationObjectMapper; import com.nike.cerberus.util.RandomStringGenerator; 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.CF_OBJECT_MAPPER; - /** * Creates the base components via CloudFormation used by all of Cerberus. */ @@ -50,7 +48,7 @@ public class CreateDatabaseOperation implements Operation private final ConfigStore configStore; - private final ObjectMapper cloudFormationObjectMapper; + private final CloudFormationObjectMapper cloudFormationObjectMapper; private RandomStringGenerator passwordGenerator = new RandomStringGenerator(); @@ -58,11 +56,11 @@ public class CreateDatabaseOperation implements Operation public CreateDatabaseOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationObjectMapper cloudFormationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.configStore = configStore; - this.cloudFormationObjectMapper = cloudformationObjectMapper; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; } @Override @@ -82,9 +80,7 @@ public void run(final CreateDatabaseCommand command) { .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); - final TypeReference> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters); cloudFormationService.createStackAndWait(Stack.DATABASE, parameters, true, command.getTagsDelegate().getTags()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index 6d4e0682..fef25030 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.core; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; @@ -26,16 +26,14 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; 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.CF_OBJECT_MAPPER; - /** * Creates the base components via CloudFormation used by all of Cerberus. */ @@ -49,17 +47,17 @@ public class CreateLoadBalancerOperation implements Operation> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters); cloudFormationService.createStackAndWait(Stack.LOAD_BALANCER, parameters, true, command.getTagsDelegate().getTags()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index 3773ab4c..fc8893d1 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.core; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateRoute53Command; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; @@ -25,16 +25,14 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Route53Service; +import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; 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.CF_OBJECT_MAPPER; - /** * Creates the origin and load balancer Route53 records for Cerberus */ @@ -48,17 +46,17 @@ public class CreateRoute53Operation implements Operation { private final Route53Service route53Service; - private final ObjectMapper cloudFormationObjectMapper; + private final CloudFormationObjectMapper cloudFormationObjectMapper; @Inject public CreateRoute53Operation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, final Route53Service route53Service, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationObjectMapper cloudFormationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.route53Service = route53Service; - this.cloudFormationObjectMapper = cloudformationObjectMapper; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; } @Override @@ -71,9 +69,7 @@ public void run(final CreateRoute53Command command) { .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setOriginDomainName(getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride())); - final TypeReference> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters); cloudFormationService.createStackAndWait(Stack.ROUTE53, parameters, true, command.getTagsDelegate().getTags()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 4610f93c..94fd45ae 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.core; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; @@ -26,15 +26,13 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; 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.CF_OBJECT_MAPPER; - /** * Creates the base components via CloudFormation used by all of Cerberus. */ @@ -48,17 +46,17 @@ public class CreateSecurityGroupsOperation implements Operation> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters); cloudFormationService.createStackAndWait(Stack.SECURITY_GROUPS, parameters, true, command.getTagParameters().getTags()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index 41dab0d2..d6131187 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -17,8 +17,7 @@ package com.nike.cerberus.operation.core; import com.beust.jcommander.internal.Maps; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.VpcParameters; @@ -26,16 +25,15 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2Service; +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.List; import java.util.Map; import static com.nike.cerberus.ConfigConstants.MINIMUM_AZS; -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; /** * Creates the base components via CloudFormation used by all of Cerberus. @@ -50,17 +48,17 @@ public class CreateVpcOperation implements Operation { private final Ec2Service ec2Service; - private final ObjectMapper cloudFormationObjectMapper; + private final CloudFormationObjectMapper cloudFormationObjectMapper; @Inject public CreateVpcOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, final Ec2Service ec2Service, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationObjectMapper cloudFormationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.ec2Service = ec2Service; - this.cloudFormationObjectMapper = cloudformationObjectMapper; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; } @Override @@ -72,9 +70,7 @@ public void run(final CreateVpcCommand command) { .setAz2(azByIdentifier.get(2)) .setAz3(azByIdentifier.get(3)); - final TypeReference> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters); cloudFormationService.createStackAndWait(Stack.VPC, parameters, true, command.getTagsDelegate().getTags()); diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index 052d98e0..511d6680 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -16,8 +16,8 @@ package com.nike.cerberus.operation.core; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.amazonaws.services.cloudformation.model.StackStatus; +import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.WafParameters; @@ -25,15 +25,13 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; 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.CF_OBJECT_MAPPER; - /** * Creates the base components via CloudFormation used by all of Cerberus. */ @@ -47,17 +45,17 @@ public class CreateWafOperation implements Operation { private final ConfigStore configStore; - private final ObjectMapper cloudFormationObjectMapper; + private final CloudFormationObjectMapper cloudFormationObjectMapper; @Inject public CreateWafOperation(final EnvironmentMetadata environmentMetadata, final CloudFormationService cloudFormationService, final ConfigStore configStore, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudformationObjectMapper) { + final CloudFormationObjectMapper cloudFormationObjectMapper) { this.environmentMetadata = environmentMetadata; this.cloudFormationService = cloudFormationService; this.configStore = configStore; - this.cloudFormationObjectMapper = cloudformationObjectMapper; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; } @Override @@ -68,9 +66,7 @@ public void run(final CreateWafCommand command) { .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setWafName("cerberus-" + environmentName + "-waf"); - final TypeReference> typeReference = new TypeReference>() { - }; - final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters, typeReference); + final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters); cloudFormationService.createStackAndWait(Stack.WAF, parameters, true, command.getTagsDelegate().getTags()); diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 8e4850a8..38692ed7 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -53,6 +53,7 @@ import com.nike.cerberus.service.S3StoreService; import com.nike.cerberus.service.SaltGenerator; import com.nike.cerberus.service.StoreService; +import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,7 +81,6 @@ import static com.nike.cerberus.ConfigConstants.JDBC_USERNAME_KEY; import static com.nike.cerberus.ConfigConstants.ROOT_USER_ARN_KEY; import static com.nike.cerberus.ConfigConstants.SYSTEM_CONFIGURED_CMS_PROPERTIES; -import static com.nike.cerberus.module.CerberusModule.CF_OBJECT_MAPPER; import static com.nike.cerberus.module.CerberusModule.CONFIG_OBJECT_MAPPER; /** @@ -96,7 +96,7 @@ public class ConfigStore { private final ObjectMapper configObjectMapper; - private final ObjectMapper cloudFormationObjectMapper; + private final CloudFormationObjectMapper cloudFormationObjectMapper; private final IdentityManagementService iamService; @@ -124,7 +124,7 @@ public ConfigStore(final AmazonS3 s3Client, final EnvironmentMetadata environmentMetadata, final SaltGenerator saltGenerator, @Named(CONFIG_OBJECT_MAPPER) final ObjectMapper configObjectMapper, - @Named(CF_OBJECT_MAPPER) final ObjectMapper cloudFormationObjectMapper) { + final CloudFormationObjectMapper cloudFormationObjectMapper) { this.cloudFormationService = cloudFormationService; this.iamService = iamService; diff --git a/src/main/java/com/nike/cerberus/util/CloudFormationObjectMapper.java b/src/main/java/com/nike/cerberus/util/CloudFormationObjectMapper.java new file mode 100644 index 00000000..2ac57cae --- /dev/null +++ b/src/main/java/com/nike/cerberus/util/CloudFormationObjectMapper.java @@ -0,0 +1,36 @@ +package com.nike.cerberus.util; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.inject.Singleton; +import java.util.Map; + +/** + * Convert objects to {@code Map} to pass to CloudFormation + */ +@Singleton +public class CloudFormationObjectMapper { + + private final ObjectMapper om; + + public CloudFormationObjectMapper() { + om = new ObjectMapper(); + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + om.setSerializationInclusion(JsonInclude.Include.NON_ABSENT); + } + + public Map convertValue(Object object) { + return om.convertValue( + object, + new TypeReference>() { + } + ); + } + + public T convertValue(Map stackOutputs, Class outputClass) { + return om.convertValue(stackOutputs, outputClass); + } +} From 50866e1941ad383c3e271471c79aebd3faf6a53c Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 15 Nov 2017 14:43:02 -0800 Subject: [PATCH 40/69] parallelize challenges and clean up records when complete (#102) * parallelize challenges and clean up records when complete * Add fb supression as it can't handle anonymous runnables --- .../core/GenerateCertsOperation.java | 8 +-- .../cerberus/service/CertificateService.java | 62 ++++++++++++------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java index d4396576..74aea5c7 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java @@ -81,10 +81,6 @@ public void run(GenerateCertsCommand command) { subjectAlternativeNames.add(originName); subjectAlternativeNames.add(loadBalancerName); - // confirm with user - consoleService.askUserToProceed(String.format("Preparing to generate certs with Common Name: %s and Subject Alternative Names: %s", - commonName, String.join(", ", subjectAlternativeNames)), NO); - // Enable the use of the hard coded lets encrypt cert if enabled if (command.enableLetsEncryptCertfix()) { log.warn("Setting acme4j.le.certfix system property to 'true', this only works if you use the special acme:// lets encrypt addr. See: https://shredzone.org/maven/acme4j/usage/session.html and https://shredzone.org/maven/acme4j/provider.html"); @@ -101,6 +97,10 @@ public void run(GenerateCertsCommand command) { throw new RuntimeException("The certificate directory is not a directory or is not writable, path: " + certDir.getAbsolutePath()); } + // confirm with user + consoleService.askUserToProceed(String.format("Preparing to generate certs in %s with Common Name: %s and Subject Alternative Names: %s", + certDir.getAbsolutePath(), commonName, String.join(", ", subjectAlternativeNames)), NO); + // generate the certs certService.generateCerts( certDir, diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index 6c9580a7..2eba32ea 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -61,10 +61,14 @@ import java.security.KeyPair; import java.security.cert.X509Certificate; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** @@ -76,7 +80,8 @@ */ @SuppressFBWarnings(value = { "DM_DEFAULT_ENCODING", - "REC_CATCH_EXCEPTION" + "REC_CATCH_EXCEPTION", + "UC_USELESS_OBJECT" }) public class CertificateService { @@ -178,14 +183,7 @@ protected void acceptAgreement(Registration reg, URI agreement) { } } - /** - * Creates a Txt record in Route53 - * - * @param name The record name - * @param digest The value for the txt record - * @param hostedZoneId The hosted zone id to create the record in - */ - protected void generateChallengeTxtEntry(String name, String digest, String hostedZoneId) { + protected void executeRecordSetChanges(String name, String digest, String hostedZoneId, ChangeAction action) { ResourceRecordSet recordSet = new ResourceRecordSet() .withName(name) .withType(RRType.TXT) @@ -194,13 +192,9 @@ protected void generateChallengeTxtEntry(String name, String digest, String host new ResourceRecord(String.format("\"%s\"", digest)) ); - createOrUpdateRecordSets(recordSet, hostedZoneId); - } - - protected void createOrUpdateRecordSets(ResourceRecordSet recordSet, String hostedZoneId) { ChangeBatch changeBatch = new ChangeBatch().withChanges( new Change() - .withAction(ChangeAction.UPSERT) + .withAction(action) .withResourceRecordSet(recordSet) ); @@ -274,11 +268,24 @@ public void generateCerts(File certDir, Session session = new Session(acmeServerUrl, userKeyPair); Registration registration = findOrRegisterAccount(session, contactEmail); + Map> domainChallengeCollectionMap = new HashMap<>(); for (String name : names) { Authorization authorization = registration.authorizeDomain(name); - getChallenges(authorization).forEach(challenge -> doChallenge(challenge, name, hostedZoneId)); + domainChallengeCollectionMap.put(name, getChallenges(authorization)); } + ThreadPoolExecutor challengeExecutorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(names.size()); + domainChallengeCollectionMap.forEach((name, challengeCollection) -> { + challengeExecutorService.execute(() -> + challengeCollection.forEach(challenge -> doChallenge(challenge, name, hostedZoneId))); + }); + + do { + log.info("Waiting for all challenges to complete before continuing"); + Thread.sleep(TimeUnit.SECONDS.toMillis(5)); + } while (challengeExecutorService.getActiveCount() > 0); + challengeExecutorService.shutdown(); + KeyPair domainKeyPair = loadOrCreateKeyPair(new File(certDir.getAbsolutePath() + File.separator + DOMAIN_PKCS1_KEY_FILE)); createPKCS8PrivateKeyPemFileFromKeyPair(domainKeyPair, certDir); createPKCS1PublicKeyPem(domainKeyPair, certDir); @@ -371,20 +378,23 @@ protected Collection getChallenges(Authorization authorization) { */ protected void doDns01Challenge(Dns01Challenge challenge, String domainName, String hostedZoneId) { String digest = challenge.getDigest(); + String name = String.format(CHALLENGE_ENTRY_TEMPLATE, domainName); try { - String cname = String.format(CHALLENGE_ENTRY_TEMPLATE, domainName); - generateChallengeTxtEntry(cname, digest, hostedZoneId); + executeRecordSetChanges(name, digest, hostedZoneId, ChangeAction.UPSERT); Optional recordValue; do { - log.info("Waiting for name: '{}' to have txt record digest value: '{}' before triggering challenge", cname, digest); + log.info("Waiting for name: '{}' to have txt record digest value: '{}' before triggering challenge", name, digest); Thread.sleep(TimeUnit.SECONDS.toMillis(60)); - recordValue = getTxtRecordValue(cname); + recordValue = getTxtRecordValue(name); } while (!recordValue.isPresent() || !recordValue.get().equals(String.format("\"%s\"", digest))); } catch (InterruptedException e) { throw new RuntimeException("Failed to complete DNS 01 challenge", e); } - triggerChallenge(challenge); + triggerChallenge(challenge, 0); + + log.info("Deleting record: {}", name); + executeRecordSetChanges(name, digest, hostedZoneId, ChangeAction.DELETE); } /** @@ -392,7 +402,7 @@ protected void doDns01Challenge(Dns01Challenge challenge, String domainName, Str * * @param challenge The challenge that is ready to be triggered */ - protected void triggerChallenge(Challenge challenge) { + protected void triggerChallenge(Challenge challenge, int retryCount) { try { challenge.trigger(); @@ -404,7 +414,15 @@ protected void triggerChallenge(Challenge challenge) { } log.info("challenge accepted"); } catch (AcmeException | InterruptedException e) { - log.error("failed to trigger and wait for challenge to be accepted", e); + log.error("failed to trigger and wait for challenge to be accepted msg: {}", e.getMessage()); + // parallelizing challenges causes a race condition, retrying works past it an drastically improves overall time + if (e.getMessage().startsWith("JWS has invalid anti-replay nonce")) { + final int maxRetries = 10; + if (retryCount < maxRetries) { + log.info("Retrying {} out of {}", retryCount + 1, maxRetries); + triggerChallenge(challenge, retryCount + 1); + } + } } } From 28c535987aeb1a3150943263bb229af080ba1947 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 15 Nov 2017 14:50:16 -0800 Subject: [PATCH 41/69] make cert gen optional as per yaml --- .../com/nike/cerberus/cli/CerberusRunner.java | 8 +- ...and.java => CreateEnvironmentCommand.java} | 10 +- ...and.java => DeleteEnvironmentCommand.java} | 10 +- .../composite/CompositeOperation.java | 2 +- .../CreateCerberusEnvironmentOperation.java | 116 ----------------- .../composite/CreateEnvironmentOperation.java | 122 ++++++++++++++++++ ...n.java => DeleteEnvironmentOperation.java} | 10 +- .../core/CreateEdgeDomainRecordOperation.java | 40 +++++- .../core/CreateRoute53Operation.java | 16 ++- .../cerberus/service/CertificateService.java | 3 +- .../nike/cerberus/service/Route53Service.java | 15 ++- 11 files changed, 203 insertions(+), 149 deletions(-) rename src/main/java/com/nike/cerberus/command/composite/{CreateCerberusEnvironmentCommand.java => CreateEnvironmentCommand.java} (74%) rename src/main/java/com/nike/cerberus/command/composite/{DeleteCerberusEnvironmentCommand.java => DeleteEnvironmentCommand.java} (57%) delete mode 100644 src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java rename src/main/java/com/nike/cerberus/operation/composite/{DeleteCerberusEnvironmentOperation.java => DeleteEnvironmentOperation.java} (79%) diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 4aa6ecc9..d2dfe9e4 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -31,8 +31,8 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; -import com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand; -import com.nike.cerberus.command.composite.DeleteCerberusEnvironmentCommand; +import com.nike.cerberus.command.composite.CreateEnvironmentCommand; +import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateCerberusBackupCommand; @@ -192,9 +192,9 @@ private void registerAllCommands() { registerCommand(new CreateSecurityGroupsCommand()); registerCommand(new CreateLoadBalancerCommand()); registerCommand(new CreateEdgeDomainRecordCommand()); - registerCommand(new CreateCerberusEnvironmentCommand()); + registerCommand(new CreateEnvironmentCommand()); registerCommand(new DeleteStackCommand()); - registerCommand(new DeleteCerberusEnvironmentCommand()); + registerCommand(new DeleteEnvironmentCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java b/src/main/java/com/nike/cerberus/command/composite/CreateEnvironmentCommand.java similarity index 74% rename from src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java rename to src/main/java/com/nike/cerberus/command/composite/CreateEnvironmentCommand.java index 303c186e..b5db3799 100644 --- a/src/main/java/com/nike/cerberus/command/composite/CreateCerberusEnvironmentCommand.java +++ b/src/main/java/com/nike/cerberus/command/composite/CreateEnvironmentCommand.java @@ -19,9 +19,9 @@ import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.composite.CreateCerberusEnvironmentOperation; +import com.nike.cerberus.operation.composite.CreateEnvironmentOperation; -import static com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand.COMMAND_NAME; +import static com.nike.cerberus.command.composite.CreateEnvironmentCommand.COMMAND_NAME; @Parameters( commandNames = { @@ -29,9 +29,9 @@ }, commandDescription = "Creates a complete Cerberus environment from scratch using an environment yaml" ) -public class CreateCerberusEnvironmentCommand implements Command { +public class CreateEnvironmentCommand implements Command { - public static final String COMMAND_NAME = "create-cerberus-environment"; + public static final String COMMAND_NAME = "create-environment"; @Override public String getCommandName() { @@ -40,6 +40,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return CreateCerberusEnvironmentOperation.class; + return CreateEnvironmentOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java b/src/main/java/com/nike/cerberus/command/composite/DeleteEnvironmentCommand.java similarity index 57% rename from src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java rename to src/main/java/com/nike/cerberus/command/composite/DeleteEnvironmentCommand.java index a62f832c..2a8109ea 100644 --- a/src/main/java/com/nike/cerberus/command/composite/DeleteCerberusEnvironmentCommand.java +++ b/src/main/java/com/nike/cerberus/command/composite/DeleteEnvironmentCommand.java @@ -3,9 +3,9 @@ import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.composite.DeleteCerberusEnvironmentOperation; +import com.nike.cerberus.operation.composite.DeleteEnvironmentOperation; -import static com.nike.cerberus.command.composite.DeleteCerberusEnvironmentCommand.COMMAND_NAME; +import static com.nike.cerberus.command.composite.DeleteEnvironmentCommand.COMMAND_NAME; @Parameters( commandNames = { @@ -13,9 +13,9 @@ }, commandDescription = "Deletes all the cf stacks, and data associated with a Cerberus environment that is managed by the CLI" ) -public class DeleteCerberusEnvironmentCommand implements Command { +public class DeleteEnvironmentCommand implements Command { - public static final String COMMAND_NAME = "delete-cerberus-environment"; + public static final String COMMAND_NAME = "delete-environment"; @Override public String getCommandName() { @@ -24,6 +24,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return DeleteCerberusEnvironmentOperation.class; + return DeleteEnvironmentOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java index f5b90876..71345f53 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -41,7 +41,7 @@ public abstract class CompositeOperation implements Operation private Injector injector; - private EnvironmentConfig environmentConfig; + protected EnvironmentConfig environmentConfig; @Inject public void setInjector(Injector injector) { diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java deleted file mode 100644 index 1f4462c4..00000000 --- a/src/main/java/com/nike/cerberus/operation/composite/CreateCerberusEnvironmentOperation.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2017 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.composite; - -import com.google.common.collect.Lists; -import com.nike.cerberus.command.cms.CreateCmsClusterCommand; -import com.nike.cerberus.command.cms.CreateCmsCmkCommand; -import com.nike.cerberus.command.cms.CreateCmsConfigCommand; -import com.nike.cerberus.command.composite.CreateCerberusEnvironmentCommand; -import com.nike.cerberus.command.core.CreateBaseCommand; -import com.nike.cerberus.command.core.CreateDatabaseCommand; -import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; -import com.nike.cerberus.command.core.CreateLoadBalancerCommand; -import com.nike.cerberus.command.core.CreateRoute53Command; -import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; -import com.nike.cerberus.command.core.CreateVpcCommand; -import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.command.core.GenerateCertsCommand; -import com.nike.cerberus.command.core.UploadCertFilesCommand; -import com.nike.cerberus.domain.environment.Stack; - -import java.util.List; - -/** - * Operation class for CreateCerberusEnvironmentCommand - */ -public class CreateCerberusEnvironmentOperation extends CompositeOperation { - - /** - * {@inheritDoc} - */ - @Override - protected List getCompositeCommandChain() { - return Lists.newArrayList( - // Step 1 Create the Base Cloud Formation Stack that creates S3 Buckets, Iam Roles and KMS keys needed for config - ChainableCommand.Builder.create().withCommand(new CreateBaseCommand()).build(), - - // Step 2 Create the VPC Cloud Formation Stack that Cerberus will use - ChainableCommand.Builder.create().withCommand(new CreateVpcCommand()).build(), - - // Step 3 Create the Security Group Cloud Formation Stack - ChainableCommand.Builder.create().withCommand(new CreateSecurityGroupsCommand()).build(), - - // Step 4 Create the RDS Database Cloud Formation Stack - ChainableCommand.Builder.create().withCommand(new CreateDatabaseCommand()).build(), - - // Step 5 Generate the PKCS private and public keys as well as the x509 certificates needed to enable https - ChainableCommand.Builder.create().withCommand(new GenerateCertsCommand()).build(), - - // Step 6 Upload the certs and keys to S3 and the IAM Cert Management service so that the ALB and CMS can use the certs - ChainableCommand.Builder.create().withCommand(new UploadCertFilesCommand()) - .withAdditionalArg(UploadCertFilesCommand.STACK_NAME_LONG_ARG) - .withAdditionalArg(Stack.CMS.getName()) - .build(), - - // Step 7 Create the Application Load Balancer Cloud Formation Stack - ChainableCommand.Builder.create().withCommand(new CreateLoadBalancerCommand()).build(), - - // Step 8 Generate the CMS config with org specific setting and first secrets encrypt, - // Upload to S3 for CMS to download at service start - ChainableCommand.Builder.create().withCommand(new CreateCmsConfigCommand()).build(), - - // Step 9 Create CMS CMK, create KMS master keys in the regions specified for CMS to use with the AWS Encryption - // client to encrypt secure data in a manor that is decryptable in multiple regions and store in cms props - ChainableCommand.Builder.create().withCommand(new CreateCmsCmkCommand()).build(), - - // Step 10 Create the CMS Cluster Stack - ChainableCommand.Builder.create().withCommand(new CreateCmsClusterCommand()).build(), - - // Step 11 Create the Web Application Fire wall stack - ChainableCommand.Builder.create().withCommand(new CreateWafCommand()).build(), - - // Step 12 Create the Route 53 DNS Record Stack for origin and the load balancer - ChainableCommand.Builder.create().withCommand(new CreateRoute53Command()).build(), - - // Step 13 Create the outer most domain name record that will point to the origin record - ChainableCommand.Builder.create().withCommand(new CreateEdgeDomainRecordCommand()).build() - ); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunnable(CreateCerberusEnvironmentCommand command) { - // Always return true and depend on isRunnable of the chained commands to skip commands that cannot be re-ran - // we want this command to be able to be run more than once to complete an environment for example say temporary - // aws creds expire half way through environment creation - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean skipOnNotRunnable() { - // Since we always return true for isRunnable on this command sub of the sub commands may have already been run - // we want this command to be able to be run more than once to complete an environment for example say temporary - // aws creds expire half way through environment creation - return true; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java new file mode 100644 index 00000000..70a7841e --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.google.common.collect.Lists; +import com.nike.cerberus.command.cms.CreateCmsClusterCommand; +import com.nike.cerberus.command.cms.CreateCmsCmkCommand; +import com.nike.cerberus.command.cms.CreateCmsConfigCommand; +import com.nike.cerberus.command.composite.CreateEnvironmentCommand; +import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; +import com.nike.cerberus.command.core.CreateLoadBalancerCommand; +import com.nike.cerberus.command.core.CreateRoute53Command; +import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; +import com.nike.cerberus.command.core.CreateVpcCommand; +import com.nike.cerberus.command.core.CreateWafCommand; +import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.command.core.UploadCertFilesCommand; +import com.nike.cerberus.domain.environment.Stack; + +import java.util.List; + +/** + * Operation class for CreateCerberusEnvironmentCommand + */ +public class CreateEnvironmentOperation extends CompositeOperation { + + /** + * {@inheritDoc} + */ + @Override + protected List getCompositeCommandChain() { + List list = Lists.newArrayList( + // Step 1 Create the Base Cloud Formation Stack that creates S3 Buckets, Iam Roles and KMS keys needed for config + ChainableCommand.Builder.create().withCommand(new CreateBaseCommand()).build(), + + // Step 2 Create the VPC Cloud Formation Stack that Cerberus will use + ChainableCommand.Builder.create().withCommand(new CreateVpcCommand()).build(), + + // Step 3 Create the Security Group Cloud Formation Stack + ChainableCommand.Builder.create().withCommand(new CreateSecurityGroupsCommand()).build(), + + // Step 4 Create the RDS Database Cloud Formation Stack + ChainableCommand.Builder.create().withCommand(new CreateDatabaseCommand()).build() + ); + + // Step 5 Generate the PKCS private and public keys as well as the x509 certificates needed to enable https + if (environmentConfig.isGenerateKeysAndCerts()) { + list.add(ChainableCommand.Builder.create().withCommand(new GenerateCertsCommand()).build()); + } + + list.addAll(Lists.newArrayList( + // Step 6 Upload the certs and keys to S3 and the IAM Cert Management service so that the ALB and CMS can use the certs + ChainableCommand.Builder.create().withCommand(new UploadCertFilesCommand()) + .withAdditionalArg(UploadCertFilesCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(Stack.CMS.getName()) + .build(), + + // Step 7 Create the Application Load Balancer Cloud Formation Stack + ChainableCommand.Builder.create().withCommand(new CreateLoadBalancerCommand()).build(), + + // Step 8 Generate the CMS config with org specific setting and first secrets encrypt, + // Upload to S3 for CMS to download at service start + ChainableCommand.Builder.create().withCommand(new CreateCmsConfigCommand()).build(), + + // Step 9 Create CMS CMK, create KMS master keys in the regions specified for CMS to use with the AWS Encryption + // client to encrypt secure data in a manor that is decryptable in multiple regions and store in cms props + ChainableCommand.Builder.create().withCommand(new CreateCmsCmkCommand()).build(), + + // Step 10 Create the CMS Cluster Stack + ChainableCommand.Builder.create().withCommand(new CreateCmsClusterCommand()).build(), + + // Step 11 Create the Web Application Fire wall stack + ChainableCommand.Builder.create().withCommand(new CreateWafCommand()).build(), + + // Step 12 Create the Route 53 DNS Record Stack for origin and the load balancer + ChainableCommand.Builder.create().withCommand(new CreateRoute53Command()).build(), + + // Step 13 Create the outer most domain name record that will point to the origin record + ChainableCommand.Builder.create().withCommand(new CreateEdgeDomainRecordCommand()).build() + )); + + return list; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunnable(CreateEnvironmentCommand command) { + // Always return true and depend on isRunnable of the chained commands to skip commands that cannot be re-ran + // we want this command to be able to be run more than once to complete an environment for example say temporary + // aws creds expire half way through environment creation + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean skipOnNotRunnable() { + // Since we always return true for isRunnable on this command sub of the sub commands may have already been run + // we want this command to be able to be run more than once to complete an environment for example say temporary + // aws creds expire half way through environment creation + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java similarity index 79% rename from src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java rename to src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java index 2f965f83..c04239a4 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/DeleteCerberusEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java @@ -1,7 +1,7 @@ package com.nike.cerberus.operation.composite; import com.google.common.collect.ImmutableList; -import com.nike.cerberus.command.composite.DeleteCerberusEnvironmentCommand; +import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; import com.nike.cerberus.command.core.DeleteStackCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.service.ConsoleService; @@ -12,15 +12,15 @@ import static com.nike.cerberus.domain.environment.Stack.*; -public class DeleteCerberusEnvironmentOperation extends CompositeOperation { +public class DeleteEnvironmentOperation extends CompositeOperation { private final ConsoleService consoleService; private final EnvironmentMetadata environmentMetadata; @Inject - public DeleteCerberusEnvironmentOperation(ConsoleService consoleService, - EnvironmentMetadata environmentMetadata) { + public DeleteEnvironmentOperation(ConsoleService consoleService, + EnvironmentMetadata environmentMetadata) { this.consoleService = consoleService; this.environmentMetadata = environmentMetadata; @@ -51,7 +51,7 @@ protected List getCompositeCommandChain() { } @Override - public boolean isRunnable(DeleteCerberusEnvironmentCommand command) { + public boolean isRunnable(DeleteEnvironmentCommand command) { try { String warning = String.format( "This will delete the environment '%s' including all the secure data.", diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java index 06a69432..7d1a4c80 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java @@ -17,11 +17,14 @@ package com.nike.cerberus.operation.core; import com.amazonaws.services.route53.model.RRType; +import com.amazonaws.services.route53.model.ResourceRecord; +import com.amazonaws.services.route53.model.ResourceRecordSet; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.ConsoleService; import com.nike.cerberus.service.Route53Service; import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; @@ -29,6 +32,8 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.util.Optional; +import java.util.stream.Collectors; /** * Creates the edge domain Route53 record for Cerberus @@ -47,15 +52,20 @@ public class CreateEdgeDomainRecordOperation implements Operation edgeRecordOptional = route53Service + .getRecordSetByName(recordSetName, command.getHostedZoneId()); + + if (edgeRecordOptional.isPresent()) { + String msg = String.format( + "The edge domain: '%s' already has a record set in hosted zone: '%s' with type: '%s' and value: " + + "'%s', if you proceed this will be overridden to value = '%s'", + recordSetName, + command.getHostedZoneId(), edgeRecordOptional.get().getType(), + edgeRecordOptional.get().getResourceRecords() + .stream().map(ResourceRecord::getValue) + .collect(Collectors.joining(", ")), + configStore.getRoute53StackOutputs().getOriginDomainName() + ); + + try { + consoleService.askUserToProceed(msg, ConsoleService.DefaultAction.NO); + } catch (RuntimeException e) { + isRunnable = false; + } + } + return isRunnable; } private String getEdgeDomainName(String baseDomainName, final String edgeDomainNameOverride) { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index fc8893d1..dc96f030 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -81,17 +81,25 @@ public boolean isRunnable(final CreateRoute53Command command) { final String loadBalancerDomainName = getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride()); final String originDomainName = getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride()); + boolean isRunnable = true; if (!cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { logger.error("The load balancer stack must exist to create the Route53 record!"); - return false; + isRunnable = false; } if (cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName))) { logger.error("Route53 stack already exists."); - return false; + isRunnable = false; + } + if (route53Service.getRecordSetByName(loadBalancerDomainName, command.getHostedZoneId()).isPresent()) { + logger.error("The load balancer name is already registered"); + isRunnable = false; + } + if (route53Service.getRecordSetByName(originDomainName, command.getHostedZoneId()).isPresent()) { + logger.error("The origin name is already registered"); + isRunnable = false; } - return !(route53Service.recordSetWithNameAlreadyExists(loadBalancerDomainName, command.getHostedZoneId()) || - route53Service.recordSetWithNameAlreadyExists(originDomainName, command.getHostedZoneId())); + return isRunnable; } private String getLoadBalancerDomainName(final String baseDomainName, final String loadBalancerDomainNameOverride) { diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index 2eba32ea..bcbf3917 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -281,7 +281,8 @@ public void generateCerts(File certDir, }); do { - log.info("Waiting for all challenges to complete before continuing"); + log.info("Waiting for all challenges to complete before continuing, current active challenge threads: {}", + challengeExecutorService.getActiveCount()); Thread.sleep(TimeUnit.SECONDS.toMillis(5)); } while (challengeExecutorService.getActiveCount() > 0); challengeExecutorService.shutdown(); diff --git a/src/main/java/com/nike/cerberus/service/Route53Service.java b/src/main/java/com/nike/cerberus/service/Route53Service.java index c99c6daa..65a76296 100644 --- a/src/main/java/com/nike/cerberus/service/Route53Service.java +++ b/src/main/java/com/nike/cerberus/service/Route53Service.java @@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.util.Optional; /** * Service wrapper for AWS CloudFormation. @@ -61,7 +62,7 @@ public void createRoute53RecordSet(String hostedZoneId, final ChangeBatch recordSetChangeBatch = new ChangeBatch() .withChanges(new Change() - .withAction(ChangeAction.CREATE) + .withAction(ChangeAction.UPSERT) .withResourceRecordSet(recordSet)); route53Client.changeResourceRecordSets(new ChangeResourceRecordSetsRequest() @@ -69,13 +70,17 @@ public void createRoute53RecordSet(String hostedZoneId, .withChangeBatch(recordSetChangeBatch)); } - public boolean recordSetWithNameAlreadyExists(final String recordSetName, final String hostedZoneId) { + public Optional getRecordSetByName(final String recordSetName, final String hostedZoneId) { final ListResourceRecordSetsResult recordSets = route53Client.listResourceRecordSets( new ListResourceRecordSetsRequest() .withHostedZoneId(hostedZoneId)); - return recordSets.getResourceRecordSets() - .stream() - .anyMatch(recordSet -> recordSet.getName().equals(recordSetName + ".")); + for (ResourceRecordSet recordSet : recordSets.getResourceRecordSets()) { + if (recordSet.getName().equals(recordSetName + ".")) { + return Optional.of(recordSet); + } + } + + return Optional.empty(); } } From 587d7a6ca210059beb2b79c031e958a2151accc0 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Fri, 17 Nov 2017 11:18:43 -0800 Subject: [PATCH 42/69] Refactor the way certs are uploaded and stub out rotate command (#103) * Refactor the way certs are uploaded and stub out rotate composite command * Update CreateCmsClusterOperation.java * Update EnvironmentConfigToArgsMapper.java * Also keep track of im cert arn as it is needed by alb * fix typo --- gradle/dependencies.gradle | 5 +- ...omS3BackupOperationUserAcceptanceTest.java | 3 +- ...GenerateCertsOperationIntegrationTest.java | 20 +- .../com/nike/cerberus/ConfigConstants.java | 11 +- .../com/nike/cerberus/cli/CerberusRunner.java | 14 +- .../cli/EnvironmentConfigToArgsMapper.java | 61 +-- .../client/CerberusAdminClientFactory.java | 45 +- .../composite/RotateCertificatesCommand.java | 45 ++ .../core/AddCertificateToAlbCommand.java | 45 ++ .../core/CreateCerberusBackupCommand.java | 68 --- ...a => GenerateCertificateFilesCommand.java} | 12 +- .../core/SetBackupAdminPrincipalsCommand.java | 70 ---- ...ava => UploadCertificateFilesCommand.java} | 39 +- .../cloudformation/LoadBalancerOutputs.java | 17 +- .../domain/cloudformation/VaultOutputs.java | 100 ----- .../environment/CerberusEnvironmentData.java | 70 ++++ .../environment/CertificateInformation.java | 168 ++++++++ .../domain/environment/CmsSecrets.java | 56 --- .../domain/environment/Environment.java | 100 ----- .../cerberus/domain/environment/Secrets.java | 45 -- .../domain/environment/VaultSecrets.java | 48 --- .../nike/cerberus/module/CerberusModule.java | 5 + .../cms/CreateCmsClusterOperation.java | 13 +- .../cms/CreateCmsConfigOperation.java | 2 - .../cms/UpdateCmsConfigOperation.java | 1 - .../operation/composite/ChainableCommand.java | 22 +- .../composite/CompositeOperation.java | 18 +- .../composite/CreateEnvironmentOperation.java | 34 +- .../RotateCertificatesOperation.java | 82 ++++ .../core/AddCertificateToAlbOperation.java | 59 +++ .../operation/core/CreateBaseOperation.java | 3 - .../core/CreateCerberusBackupOperation.java | 389 ------------------ .../core/CreateLoadBalancerOperation.java | 27 +- ...=> GenerateCertificateFilesOperation.java} | 14 +- .../SetBackupAdminPrincipalsOperation.java | 124 ------ .../core/UploadCertFilesOperation.java | 162 -------- .../UploadCertificatesFilesOperation.java | 61 +++ .../cerberus/service/CertificateService.java | 131 +++++- .../com/nike/cerberus/store/ConfigStore.java | 385 ++++------------- .../vault/VaultAdminClientFactory.java | 167 -------- src/main/resources/cloudformation/base.yaml | 5 +- .../cloudformation/load-balancer.yaml | 4 +- .../cloudformation/web-app-firewall.yaml | 2 +- .../EnvironmentConfigToArgsMapperTest.java | 6 +- .../com/nike/cerberus/domain/StackTest.java | 16 - 45 files changed, 901 insertions(+), 1873 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/core/CreateCerberusBackupCommand.java rename src/main/java/com/nike/cerberus/command/core/{GenerateCertsCommand.java => GenerateCertificateFilesCommand.java} (94%) delete mode 100644 src/main/java/com/nike/cerberus/command/core/SetBackupAdminPrincipalsCommand.java rename src/main/java/com/nike/cerberus/command/core/{UploadCertFilesCommand.java => UploadCertificateFilesCommand.java} (54%) delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/VaultOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/CmsSecrets.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/Environment.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/Secrets.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/VaultSecrets.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java rename src/main/java/com/nike/cerberus/operation/core/{GenerateCertsOperation.java => GenerateCertificateFilesOperation.java} (89%) delete mode 100644 src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java delete mode 100644 src/main/java/com/nike/cerberus/vault/VaultAdminClientFactory.java diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index b0b4898c..7952f1dd 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -19,7 +19,7 @@ allprojects { jcenter() } - def awsSDKVersion = '1.11.220' + def awsSDKVersion = '1.11.229' //noinspection GroovyAssignabilityCheck dependencies { @@ -34,7 +34,7 @@ allprojects { compile group: 'com.amazonaws', name: 'aws-java-sdk-sns', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-lambda', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: awsSDKVersion - + compile group: 'com.amazonaws', name: 'aws-java-sdk-elasticloadbalancingv2', version: awsSDKVersion compile 'com.nike:vault-client:1.4.1' compile 'com.squareup.okhttp3:okhttp:3.3.1' @@ -45,6 +45,7 @@ allprojects { compile 'com.fasterxml.jackson.core:jackson-annotations:2.7.+' compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7+' compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.5' + compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-joda', version: '2.4.3' compile 'org.slf4j:slf4j-api:1.7.+' compile 'ch.qos.logback:logback-classic:1.1.+' diff --git a/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java b/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java index adb5d06f..b8eaf7b1 100644 --- a/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java +++ b/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java @@ -23,7 +23,6 @@ import com.nike.cerberus.operation.core.RestoreCerberusBackupOperation; import com.nike.cerberus.service.ConsoleService; import com.nike.cerberus.utils.TestUtils; -import com.nike.cerberus.vault.VaultAdminClientFactory; import com.nike.vault.client.StaticVaultUrlResolver; import okhttp3.OkHttpClient; import org.apache.http.conn.ssl.NoopHostnameVerifier; @@ -85,7 +84,7 @@ public void before() { CerberusAdminClient adminClient = new CerberusAdminClient( new StaticVaultUrlResolver("http://127.0.0.1:8200"), - new VaultAdminClientFactory.RootCredentialsProvider(rootToken), + null, new OkHttpClient.Builder() .hostnameVerifier(new NoopHostnameVerifier()) .connectTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) diff --git a/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java b/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java index 4126993b..81d34900 100644 --- a/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java +++ b/src/integration-test/java/com/nike/cerberus/operation/core/GenerateCertsOperationIntegrationTest.java @@ -24,10 +24,13 @@ import com.amazonaws.services.identitymanagement.model.UploadServerCertificateResult; import com.amazonaws.services.route53.AmazonRoute53Client; import com.google.common.collect.ImmutableSet; -import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.service.CertificateService; import com.nike.cerberus.service.ConsoleService; +import com.nike.cerberus.service.IdentityManagementService; +import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.UuidSupplier; import io.netty.handler.ssl.SslContextBuilder; import org.apache.commons.lang3.StringUtils; import org.junit.Before; @@ -82,7 +85,7 @@ public class GenerateCertsOperationIntegrationTest { public static String CERT_DIR = "build/certs/"; @Mock - private GenerateCertsCommand command; + private GenerateCertificateFilesCommand command; @Mock private ConsoleService consoleService; @@ -90,7 +93,13 @@ public class GenerateCertsOperationIntegrationTest { @Mock private EnvironmentMetadata environmentMetadata; - private GenerateCertsOperation operation; + @Mock + private ConfigStore configStore; + + @Mock + IdentityManagementService identityManagementService; + + private GenerateCertificateFilesOperation operation; private File certDir; @@ -99,9 +108,10 @@ public void before() { initMocks(this); CertificateService certificateService = new CertificateService(consoleService, - AmazonRoute53Client.builder().withRegion("us-west-2").build()); + AmazonRoute53Client.builder().withRegion("us-west-2").build(), new UuidSupplier(), configStore, + identityManagementService, environmentMetadata); - operation = new GenerateCertsOperation(certificateService, environmentMetadata, consoleService); + operation = new GenerateCertificateFilesOperation(certificateService, environmentMetadata, consoleService); certDir = new File(CERT_DIR); } diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 2a0e9b3c..5cfc86cf 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -30,9 +30,7 @@ public class ConfigConstants { public static final String DEFAULT_CMS_DB_NAME = "cms"; - public static final String ENV_DATA_FILE = "config/environment.json"; - - public static final String SECRETS_DATA_FILE = "config/secrets.json"; + public static final String ENVIRONMENT_DATA_FILE = "environment.json"; public static final String CERT_PART_CA = "ca.pem"; @@ -44,7 +42,7 @@ public class ConfigConstants { public static final String CERT_PART_PUBKEY = "pubkey.pem"; - public static final String CMS_ENV_CONFIG_PATH = "data/cms/environment.properties"; + public static final String CMS_ENV_CONFIG_PATH = "cms/environment.properties"; public static final String VERSION_PROPERTY = "cli.version"; @@ -68,6 +66,8 @@ public class ConfigConstants { public static final String CMS_ENV_NAME = "cms.env.name"; + public static final String CMS_CERTIFICATE_TO_USE = "cms.ssl.certificateName"; + public static final ImmutableSet SYSTEM_CONFIGURED_CMS_PROPERTIES = ImmutableSet.of( ROOT_USER_ARN_KEY, ADMIN_ROLE_ARN_KEY, @@ -77,7 +77,8 @@ public class ConfigConstants { JDBC_PASSWORD_KEY, CMK_ARNS_KEY, HASH_SALT, - CMS_ENV_NAME); + CMS_ENV_NAME, + CMS_CERTIFICATE_TO_USE); public static final String CERBERUS_AMI_TAG_NAME = "tag:cerberus_component"; diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index d2dfe9e4..4ef24a68 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -31,11 +31,11 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; +import com.nike.cerberus.command.core.AddCertificateToAlbCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.core.CreateBaseCommand; -import com.nike.cerberus.command.core.CreateCerberusBackupCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; @@ -44,13 +44,12 @@ import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.DeleteStackCommand; -import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; import com.nike.cerberus.command.core.RestoreCerberusBackupCommand; import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; -import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.command.core.UploadCertFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.ViewConfigCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; @@ -170,7 +169,7 @@ private void printCliVersion() { */ private void registerAllCommands() { registerCommand(new CreateBaseCommand()); - registerCommand(new UploadCertFilesCommand()); + registerCommand(new UploadCertificateFilesCommand()); registerCommand(new CreateCmsConfigCommand()); registerCommand(new CreateCmsClusterCommand()); registerCommand(new CreateCmsCmkCommand()); @@ -182,9 +181,7 @@ private void registerAllCommands() { registerCommand(new ViewConfigCommand()); registerCommand(new UpdateCmsConfigCommand()); registerCommand(new RollingRebootWithHealthCheckCommand()); - registerCommand(new CreateCerberusBackupCommand()); - registerCommand(new SetBackupAdminPrincipalsCommand()); - registerCommand(new GenerateCertsCommand()); + registerCommand(new GenerateCertificateFilesCommand()); registerCommand(new CreateVpcCommand()); registerCommand(new CreateWafCommand()); registerCommand(new CreateDatabaseCommand()); @@ -195,6 +192,7 @@ private void registerAllCommands() { registerCommand(new CreateEnvironmentCommand()); registerCommand(new DeleteStackCommand()); registerCommand(new DeleteEnvironmentCommand()); + registerCommand(new AddCertificateToAlbCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index d857f13b..b0f0b227 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -29,8 +29,8 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.command.core.GenerateCertsCommand; -import com.nike.cerberus.command.core.UploadCertFilesCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; @@ -38,7 +38,6 @@ import com.nike.cerberus.domain.input.VpcAccessWhitelist; import org.apache.commons.lang3.StringUtils; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -84,8 +83,8 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig switch (commandName) { case CreateBaseCommand.COMMAND_NAME: return getCreateBaseCommandArgs(environmentConfig); - case UploadCertFilesCommand.COMMAND_NAME: - return getUploadCertFilesCommandArgs(environmentConfig, passedArgs); + case UploadCertificateFilesCommand.COMMAND_NAME: + return getUploadCertFilesCommandArgs(environmentConfig); case CreateCmsClusterCommand.COMMAND_NAME: return getCreateCmsClusterCommandArgs(environmentConfig); case WhitelistCidrForVpcAccessCommand.COMMAND_NAME: @@ -106,7 +105,7 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig return getCreateRoute53CommandArgs(environmentConfig); case CreateWafCommand.COMMAND_NAME: return getCreateWafCommandArgs(environmentConfig); - case GenerateCertsCommand.COMMAND_NAME: + case GenerateCertificateFilesCommand.COMMAND_NAME: return getGenerateCertificatesCommandArgs(environmentConfig); case CreateCmsCmkCommand.COMMAND_NAME: return getCreateCmsCmkCommandArgs(environmentConfig); @@ -184,31 +183,11 @@ private static List getGlobalTags(EnvironmentConfig environmentConfig) { return args.build(); } - private static List getUploadCertFilesCommandArgs(EnvironmentConfig environmentConfig, String[] passedArgs) { - String stackName = getStackName(passedArgs); - ArgsBuilder args = ArgsBuilder.create(); - - if (stackName == null) { - return args.build(); - } - - args.addOption(UploadCertFilesCommand.STACK_NAME_LONG_ARG, stackName); - switch (stackName) { - case "cms": - args.addOption(UploadCertFilesCommand.CERT_PATH_LONG_ARG, - environmentConfig.getManagementService().getCertPath()); - break; - default: - break; - } - - Arrays.stream(passedArgs).forEach(arg -> { - if (arg.equals("--overwrite")) { - args.addFlag(UploadCertFilesCommand.OVERWRITE_LONG_ARG); - } - }); - - return args.build(); + private static List getUploadCertFilesCommandArgs(EnvironmentConfig environmentConfig) { + return ArgsBuilder.create() + .addOption(UploadCertificateFilesCommand.CERT_PATH_LONG_ARG, + environmentConfig.getManagementService().getCertPath()) + .build(); } private static List getCreateBaseCommandArgs(EnvironmentConfig config) { @@ -258,22 +237,22 @@ private static List getCreateWafCommandArgs(EnvironmentConfig config) { private static List getGenerateCertificatesCommandArgs(EnvironmentConfig config) { ArgsBuilder argsBuilder = ArgsBuilder.create() - .addOption(GenerateCertsCommand.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) - .addOption(GenerateCertsCommand.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()) - .addOption(GenerateCertsCommand.ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getOriginDomainNameOverride()) - .addOption(GenerateCertsCommand.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getLoadBalancerDomainNameOverride()) - .addOption(GenerateCertsCommand.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) - .addOption(GenerateCertsCommand.ACME_API_LONG_ARG, config.getAcmeApiUrl()) - .addOption(GenerateCertsCommand.CONTACT_EMAIL_LONG_ARG, config.getAcmeContactEmail()) - .addOption(GenerateCertsCommand.CERT_FOLDER_LONG_ARG, config.getLocalFolderToStoreCerts()); + .addOption(GenerateCertificateFilesCommand.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) + .addOption(GenerateCertificateFilesCommand.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()) + .addOption(GenerateCertificateFilesCommand.ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getOriginDomainNameOverride()) + .addOption(GenerateCertificateFilesCommand.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getLoadBalancerDomainNameOverride()) + .addOption(GenerateCertificateFilesCommand.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) + .addOption(GenerateCertificateFilesCommand.ACME_API_LONG_ARG, config.getAcmeApiUrl()) + .addOption(GenerateCertificateFilesCommand.CONTACT_EMAIL_LONG_ARG, config.getAcmeContactEmail()) + .addOption(GenerateCertificateFilesCommand.CERT_FOLDER_LONG_ARG, config.getLocalFolderToStoreCerts()); if (config.isEnableLeCertFix()) { - argsBuilder.addFlag(GenerateCertsCommand.ENABLE_LE_CERTFIX_LONG_ARG); + argsBuilder.addFlag(GenerateCertificateFilesCommand.ENABLE_LE_CERTFIX_LONG_ARG); } if (config.getAdditionalSubjectNames() != null) { config.getAdditionalSubjectNames().forEach(sn -> { - argsBuilder.addOption(GenerateCertsCommand.SUBJECT_ALT_NAME_LONG_ARG, sn); + argsBuilder.addOption(GenerateCertificateFilesCommand.SUBJECT_ALT_NAME_LONG_ARG, sn); }); } diff --git a/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java b/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java index 710e6b99..619c582f 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java +++ b/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java @@ -19,22 +19,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.Inject; import com.nike.cerberus.module.CerberusModule; -import com.nike.cerberus.vault.VaultAdminClientFactory; import com.nike.vault.client.StaticVaultUrlResolver; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.VaultClientException; -import com.nike.vault.client.model.VaultAuthResponse; -import com.nike.vault.client.model.VaultTokenAuthRequest; import okhttp3.OkHttpClient; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Named; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; import java.util.concurrent.TimeUnit; public class CerberusAdminClientFactory { @@ -44,21 +35,17 @@ public class CerberusAdminClientFactory { public static final int DEFAULT_TIMEOUT = 60; public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; - private final VaultAdminClientFactory vaultAdminClientFactory; private final ObjectMapper objectMapper; @Inject - public CerberusAdminClientFactory(VaultAdminClientFactory vaultAdminClientFactory, - @Named(CerberusModule.CONFIG_OBJECT_MAPPER) ObjectMapper objectMapper) { - - this.vaultAdminClientFactory = vaultAdminClientFactory; + public CerberusAdminClientFactory(@Named(CerberusModule.CONFIG_OBJECT_MAPPER) ObjectMapper objectMapper) { this.objectMapper = objectMapper; } public CerberusAdminClient createCerberusAdminClient(String url) { return new CerberusAdminClient( new StaticVaultUrlResolver(url), - new VaultAdminClientFactory.RootCredentialsProvider(generateAdminToken()), + null, new OkHttpClient.Builder() .hostnameVerifier(new NoopHostnameVerifier()) .connectTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) @@ -69,32 +56,4 @@ public CerberusAdminClient createCerberusAdminClient(String url) { ); } - /** - * Generates and admin token that CMS will recognize as an Admin so we can us the restore endpoint - */ - private String generateAdminToken() { - try { - logger.info("Attempting to generate an admin token with the root token"); - VaultAdminClient adminClient = vaultAdminClientFactory.getClientForLeader().get(); - - Map metadata = new HashMap<>(); - metadata.put("is_admin", "true"); - metadata.put("username", "admin-cli"); - - Set policies = new HashSet<>(); - policies.add("root"); - - VaultAuthResponse vaultAuthResponse = adminClient.createOrphanToken(new VaultTokenAuthRequest() - .setDisplayName("admin-cli") - .setPolicies(policies) - .setMeta(metadata) - .setTtl("30m") - .setNoDefaultPolicy(true)); - - return vaultAuthResponse.getClientToken(); - } catch (VaultClientException e) { - throw new RuntimeException("There was an error while trying to create an admin token, this command " + - "requires proxy access or direct a connect to the vault leader, is your ip white listed?", e); - } - } } diff --git a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java new file mode 100644 index 00000000..cafd7649 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.composite.RotateCertificatesOperation; + +import static com.nike.cerberus.command.composite.RotateCertificatesCommand.COMMAND_DESCRIPTION; +import static com.nike.cerberus.command.composite.RotateCertificatesCommand.COMMAND_NAME; + +@Parameters( + commandNames = COMMAND_NAME, + commandDescription = COMMAND_DESCRIPTION +) +public class RotateCertificatesCommand implements Command { + + public static final String COMMAND_NAME = "rotate-certificates"; + public static final String COMMAND_DESCRIPTION = "rotates certs"; // TODO + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return RotateCertificatesOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java b/src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java new file mode 100644 index 00000000..29180c0c --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 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.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.AddCertificateToAlbOperation; + +import static com.nike.cerberus.command.core.AddCertificateToAlbCommand.COMMAND_NAME; + +@Parameters( + commandNames = { + COMMAND_NAME + }, + commandDescription = "Adds a new cert to the alb" +) +public class AddCertificateToAlbCommand implements Command { + + public static final String COMMAND_NAME = "add-certificate-to-alb"; + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return AddCertificateToAlbOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateCerberusBackupCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateCerberusBackupCommand.java deleted file mode 100644 index a2b968fb..00000000 --- a/src/main/java/com/nike/cerberus/command/core/CreateCerberusBackupCommand.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017 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.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.CreateCerberusBackupOperation; - -import java.util.ArrayList; -import java.util.List; - -import static com.nike.cerberus.command.core.CreateCerberusBackupCommand.COMMAND_NAME; - - -/** - * Command for creating Safe Deposit Box Metadata and Vault secret data backups in S3 encrypted with KMS - */ -@Parameters( - commandNames = COMMAND_NAME, - commandDescription = "Allows Cerberus operators to create a complete backup in S3 encrypted with KMS. that can be restored with the restore command" -) -public class CreateCerberusBackupCommand implements Command { - - public static final String COMMAND_NAME = "create-backup"; - - public static final String BACKUP_REGIONS_LONG_ARG = "--backup-region"; - public static final String BACKUP_REGIONS_SHORT_ARG = "-br"; - - @Parameter( - names = { - BACKUP_REGIONS_LONG_ARG, - BACKUP_REGIONS_SHORT_ARG - }, - description = "One or more regions to store backup data in.", - required = true - ) - private List backupRegions = new ArrayList<>(); - - public List getBackupRegions() { - return backupRegions; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateCerberusBackupOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java similarity index 94% rename from src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java rename to src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java index 02f7b4a3..71da94a6 100644 --- a/src/main/java/com/nike/cerberus/command/core/GenerateCertsCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java @@ -20,21 +20,21 @@ import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.GenerateCertsOperation; +import com.nike.cerberus.operation.core.GenerateCertificateFilesOperation; import java.util.ArrayList; import java.util.List; -import static com.nike.cerberus.command.core.GenerateCertsCommand.COMMAND_DESCRIPTION; -import static com.nike.cerberus.command.core.GenerateCertsCommand.COMMAND_NAME; +import static com.nike.cerberus.command.core.GenerateCertificateFilesCommand.COMMAND_DESCRIPTION; +import static com.nike.cerberus.command.core.GenerateCertificateFilesCommand.COMMAND_NAME; @Parameters( commandNames = COMMAND_NAME, commandDescription = COMMAND_DESCRIPTION ) -public class GenerateCertsCommand implements Command { +public class GenerateCertificateFilesCommand implements Command { - public static final String COMMAND_NAME = "generate-certificates"; + public static final String COMMAND_NAME = "generate-certificate-files"; public static final String COMMAND_DESCRIPTION = "Generates the TLS certificates needed to enable https " + "through out the system, using an ACME provider such as LetsEncrypt"; @@ -203,6 +203,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return GenerateCertsOperation.class; + return GenerateCertificateFilesOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/command/core/SetBackupAdminPrincipalsCommand.java b/src/main/java/com/nike/cerberus/command/core/SetBackupAdminPrincipalsCommand.java deleted file mode 100644 index e32c36a8..00000000 --- a/src/main/java/com/nike/cerberus/command/core/SetBackupAdminPrincipalsCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2017 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.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.SetBackupAdminPrincipalsOperation; - -import java.util.ArrayList; -import java.util.List; - -import static com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand.COMMAND_NAME; - -/** - * Command to update which principals besides for the root account will have permissions to use the backup cmk, - * AKA create and restore backups. - */ -@Parameters( - commandNames = COMMAND_NAME, - commandDescription = "Update the IAM Principals that are allowed to create and restore backups. " + - "This command automatically adds by default the root user and configured admin user arn, " + - "but you can use this command to add iam principals such as CI systems and additional user principals " + - "that will have access to encrypt and decrypt backup data" -) -public class SetBackupAdminPrincipalsCommand implements Command { - - public static final String COMMAND_NAME = "set-backup-principals"; - - public static final String PRINCIPAL_LONG_ARG = "--principal"; - public static final String PRINCIPAL_SHORT_ARG = "-p"; - - @Parameter( - names = { - PRINCIPAL_LONG_ARG, - PRINCIPAL_SHORT_ARG - }, - description = "One or more additional principals to grant access to." - ) - private List additionalPrincipals = new ArrayList<>(); - - public List getAdditionalPrincipals() { - return additionalPrincipals; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return SetBackupAdminPrincipalsOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java similarity index 54% rename from src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java rename to src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java index 65065df8..05e24548 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java @@ -20,34 +20,21 @@ import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; import com.nike.cerberus.command.validator.UploadCertFilesPathValidator; -import com.nike.cerberus.command.validator.UploadCertFilesStackNameValidator; -import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.UploadCertFilesOperation; -import com.nike.cerberus.util.StackConverter; +import com.nike.cerberus.operation.core.UploadCertificatesFilesOperation; import java.nio.file.Path; -import static com.nike.cerberus.command.core.UploadCertFilesCommand.COMMAND_NAME; +import static com.nike.cerberus.command.core.UploadCertificateFilesCommand.COMMAND_NAME; /** * Command for uploading a certificate for a specific component. */ @Parameters(commandNames = COMMAND_NAME, commandDescription = "Upload certificate files for a specific component.") -public class UploadCertFilesCommand implements Command { +public class UploadCertificateFilesCommand implements Command { - public static final String COMMAND_NAME = "upload-cert"; - public static final String STACK_NAME_LONG_ARG = "--stack-name"; - public static final String CERT_PATH_LONG_ARG = "--cert-path"; - public static final String OVERWRITE_LONG_ARG = "--overwrite"; - - @Parameter( - names = {STACK_NAME_LONG_ARG}, - required = true, - description = "Stack name the certificate is for.", - validateValueWith = UploadCertFilesStackNameValidator.class, - converter = StackConverter.class) - private Stack stack; + public static final String COMMAND_NAME = "upload-certificate-files"; + public static final String CERT_PATH_LONG_ARG = "--cert-dir-path"; @Parameter( names = {CERT_PATH_LONG_ARG}, @@ -56,24 +43,10 @@ public class UploadCertFilesCommand implements Command { validateValueWith = UploadCertFilesPathValidator.class) private Path certPath; - @Parameter( - names = {OVERWRITE_LONG_ARG}, - description = "If certificate is already present for the environment and component, overwrite it." - ) - private boolean overwrite; - - public Stack getStack() { - return stack; - } - public Path getCertPath() { return certPath; } - public boolean isOverwrite() { - return overwrite; - } - @Override public String getCommandName() { return COMMAND_NAME; @@ -81,6 +54,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return UploadCertFilesOperation.class; + return UploadCertificatesFilesOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java index dae70bf6..80903f34 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerOutputs.java @@ -21,19 +21,19 @@ */ public class LoadBalancerOutputs { - private Integer cmsTargetGroup; + private String cmsTargetGroup; private String loadBalancerAccessLogBucket; private String loadBalancerDnsName; - private String loadBalancerLogicalId; + private String loadBalancerPhysicalId; - public Integer getCmsTargetGroup() { + public String getCmsTargetGroup() { return cmsTargetGroup; } - public LoadBalancerOutputs setCmsTargetGroup(Integer cmsTargetGroup) { + public LoadBalancerOutputs setCmsTargetGroup(String cmsTargetGroup) { this.cmsTargetGroup = cmsTargetGroup; return this; } @@ -56,12 +56,11 @@ public LoadBalancerOutputs setLoadBalancerDnsName(String loadBalancerDnsName) { return this; } - public String getLoadBalancerLogicalId() { - return loadBalancerLogicalId; + public String getLoadBalancerPhysicalId() { + return loadBalancerPhysicalId; } - public LoadBalancerOutputs setLoadBalancerLogicalId(String loadBalancerLogicalId) { - this.loadBalancerLogicalId = loadBalancerLogicalId; - return this; + public void setLoadBalancerPhysicalId(String loadBalancerPhysicalId) { + this.loadBalancerPhysicalId = loadBalancerPhysicalId; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VaultOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VaultOutputs.java deleted file mode 100644 index 8fa403fa..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/VaultOutputs.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2016 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 output parameters for the Vault CloudFormation script. - */ -public class VaultOutputs { - - private String autoscalingGroupLogicalId; - - private String launchConfigurationLogicalId; - - private String elbLogicalId; - - private String elbCanonicalHostedZoneNameId; - - private String elbDnsName; - - private String elbSourceSecurityGroupName; - - private String elbSourceSecurityGroupOwnerAlias; - - public String getAutoscalingGroupLogicalId() { - return autoscalingGroupLogicalId; - } - - public VaultOutputs setAutoscalingGroupLogicalId(String autoscalingGroupLogicalId) { - this.autoscalingGroupLogicalId = autoscalingGroupLogicalId; - return this; - } - - public String getLaunchConfigurationLogicalId() { - return launchConfigurationLogicalId; - } - - public VaultOutputs setLaunchConfigurationLogicalId(String launchConfigurationLogicalId) { - this.launchConfigurationLogicalId = launchConfigurationLogicalId; - return this; - } - - public String getElbLogicalId() { - return elbLogicalId; - } - - public VaultOutputs setElbLogicalId(String elbLogicalId) { - this.elbLogicalId = elbLogicalId; - return this; - } - - public String getElbCanonicalHostedZoneNameId() { - return elbCanonicalHostedZoneNameId; - } - - public VaultOutputs setElbCanonicalHostedZoneNameId(String elbCanonicalHostedZoneNameId) { - this.elbCanonicalHostedZoneNameId = elbCanonicalHostedZoneNameId; - return this; - } - - public String getElbDnsName() { - return elbDnsName; - } - - public VaultOutputs setElbDnsName(String elbDnsName) { - this.elbDnsName = elbDnsName; - return this; - } - - public String getElbSourceSecurityGroupName() { - return elbSourceSecurityGroupName; - } - - public VaultOutputs setElbSourceSecurityGroupName(String elbSourceSecurityGroupName) { - this.elbSourceSecurityGroupName = elbSourceSecurityGroupName; - return this; - } - - public String getElbSourceSecurityGroupOwnerAlias() { - return elbSourceSecurityGroupOwnerAlias; - } - - public VaultOutputs setElbSourceSecurityGroupOwnerAlias(String elbSourceSecurityGroupOwnerAlias) { - this.elbSourceSecurityGroupOwnerAlias = elbSourceSecurityGroupOwnerAlias; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java b/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java new file mode 100644 index 00000000..544886e1 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 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.environment; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Container for data needed by Cerberus. + */ +public class CerberusEnvironmentData { + + private String databasePassword; + + private LinkedList certificateInfoList; + + private String metricsTopicArn; + + public String getDatabasePassword() { + return databasePassword; + } + + public void setDatabasePassword(String databasePassword) { + this.databasePassword = databasePassword; + } + + public LinkedList getCertificateData() { + if (certificateInfoList == null) { + certificateInfoList = new LinkedList<>(); + } + return certificateInfoList; + } + + public void addNewCertificateData(CertificateInformation certificateData) { + getCertificateData().add(certificateData); + } + + public void removeCertificateInformationByName(String identityManagementCertificateName) { + List certificateInformationList = getCertificateData(); + List certsInfoToBeDeleted = certificateInformationList.stream() + .filter(certificateInformation -> + certificateInformation.getIdentityManagementCertificateName().equals(identityManagementCertificateName)) + .collect(Collectors.toList()); + + certificateInformationList.removeAll(certsInfoToBeDeleted); + } + + public String getMetricsTopicArn() { + return metricsTopicArn; + } + + public void setMetricsTopicArn(String metricsTopicArn) { + this.metricsTopicArn = metricsTopicArn; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java new file mode 100644 index 00000000..4e43b4ab --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017 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.environment; + +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import java.util.List; + +public class CertificateInformation { + + private String identityManagementCertificateName; + private String identityManagementCertificateArn; + private DateTime notBefore; + private DateTime notAfter; + private DateTime uploaded; + private String commonName; + private List subjectAlternateNames; + + public String getIdentityManagementCertificateName() { + return identityManagementCertificateName; + } + + public void setIdentityManagementCertificateName(String identityManagementCertificateName) { + this.identityManagementCertificateName = identityManagementCertificateName; + } + + public DateTime getNotBefore() { + return notBefore; + } + + public void setNotBefore(DateTime notBefore) { + this.notBefore = notBefore; + } + + public DateTime getNotAfter() { + return notAfter; + } + + public void setNotAfter(DateTime notAfter) { + this.notAfter = notAfter; + } + + public DateTime getUploaded() { + return uploaded; + } + + public void setUploaded(DateTime uploaded) { + this.uploaded = uploaded; + } + + public String getCommonName() { + return commonName; + } + + public void setCommonName(String commonName) { + this.commonName = commonName; + } + + public List getSubjectAlternateNames() { + return subjectAlternateNames; + } + + public void setSubjectAlternateNames(List subjectAlternateNames) { + this.subjectAlternateNames = subjectAlternateNames; + } + + public String getIdentityManagementCertificateArn() { + return identityManagementCertificateArn; + } + + public void setIdentityManagementCertificateArn(String identityManagementCertificateArn) { + this.identityManagementCertificateArn = identityManagementCertificateArn; + } + + @Override + public String toString() { + DateTimeFormatter fmt = DateTimeFormat.fullDateTime(); + return "CertificateInformation{" + + "identityManagementCertificateName='" + identityManagementCertificateName + '\'' + + ", identityManagementCertificateArn='" + identityManagementCertificateArn + '\'' + + ", notBefore=" + fmt.print(notBefore) + + ", notAfter=" + fmt.print(notAfter) + + ", uploaded=" + fmt.print(uploaded) + + ", commonName='" + commonName + '\'' + + ", subjectAlternateNames=" + subjectAlternateNames + + '}'; + } + + public static final class Builder { + private String identityManagementCertificateName; + private String identityManagementCertificateArn; + private DateTime notBefore; + private DateTime notAfter; + private DateTime uploaded; + private String commonName; + private List subjectAlternateNames; + + private Builder() { + } + + public static Builder create() { + return new Builder(); + } + + public Builder withIdentityManagementCertificateName(String identityManagementCertificateName) { + this.identityManagementCertificateName = identityManagementCertificateName; + return this; + } + + public Builder withIdentityManagementCertificateArn(String identityManagementCertificateArn) { + this.identityManagementCertificateArn = identityManagementCertificateArn; + return this; + } + + public Builder withNotBefore(DateTime notBefore) { + this.notBefore = notBefore; + return this; + } + + public Builder withNotAfter(DateTime notAfter) { + this.notAfter = notAfter; + return this; + } + + public Builder withUploaded(DateTime uploaded) { + this.uploaded = uploaded; + return this; + } + + public Builder withCommonName(String commonName) { + this.commonName = commonName; + return this; + } + + public Builder withSubjectAlternateNames(List subjectAlternateNames) { + this.subjectAlternateNames = subjectAlternateNames; + return this; + } + + public CertificateInformation build() { + CertificateInformation certificateInformation = new CertificateInformation(); + certificateInformation.setIdentityManagementCertificateName(identityManagementCertificateName); + certificateInformation.setIdentityManagementCertificateArn(identityManagementCertificateArn); + certificateInformation.setNotBefore(notBefore); + certificateInformation.setNotAfter(notAfter); + certificateInformation.setUploaded(uploaded); + certificateInformation.setCommonName(commonName); + certificateInformation.setSubjectAlternateNames(subjectAlternateNames); + return certificateInformation; + } + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/CmsSecrets.java b/src/main/java/com/nike/cerberus/domain/environment/CmsSecrets.java deleted file mode 100644 index 04752ad0..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/CmsSecrets.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -/** - * Represents sensitive information used by CMS. - */ -public class CmsSecrets { - - private String adminGroup; - - private String databasePassword; - - private String vaultToken; - - public String getAdminGroup() { - return adminGroup; - } - - public CmsSecrets setAdminGroup(String adminGroup) { - this.adminGroup = adminGroup; - return this; - } - - public String getDatabasePassword() { - return databasePassword; - } - - public CmsSecrets setDatabasePassword(String databasePassword) { - this.databasePassword = databasePassword; - return this; - } - - public String getVaultToken() { - return vaultToken; - } - - public CmsSecrets setVaultToken(String vaultToken) { - this.vaultToken = vaultToken; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/Environment.java b/src/main/java/com/nike/cerberus/domain/environment/Environment.java deleted file mode 100644 index 8be193f7..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/Environment.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * General purpose environment data that isn't sensitive. - */ -public class Environment { - - private String domainName; - - @JsonDeserialize(keyUsing = StackKeyDeserializer.class) - private Map stackMap; - @JsonDeserialize(keyUsing = StackKeyDeserializer.class) - private Map serverCertificateIdMap; - - private String configKeyId; - - private Map regionBackupBucketMap; - - private Set backupAdminIamPrincipals; - - private String metricsTopicArn; - - public Environment() { - stackMap = new HashMap<>(); - for (Stack stack : Stack.ALL_STACKS) { - stackMap.put(stack, ""); - } - - serverCertificateIdMap = new HashMap<>(); - serverCertificateIdMap.put(Stack.CMS, ""); - } - - public String getDomainName() { - return domainName; - } - - public void setDomainName(String domainName) { - this.domainName = domainName; - } - - public Map getStackMap() { - return stackMap; - } - - public Map getServerCertificateIdMap() { - return serverCertificateIdMap; - } - - public String getConfigKeyId() { - return configKeyId; - } - - public Environment setConfigKeyId(String configKeyId) { - this.configKeyId = configKeyId; - return this; - } - - public Map getRegionBackupBucketMap() { - return regionBackupBucketMap == null ? new HashMap<>() : regionBackupBucketMap; - } - - public Set getBackupAdminIamPrincipals() { - return backupAdminIamPrincipals == null ? new HashSet<>() : backupAdminIamPrincipals; - } - - public void setBackupAdminIamPrincipals(Set backupAdminIamPrincipals) { - this.backupAdminIamPrincipals = backupAdminIamPrincipals; - } - - public String getMetricsTopicArn() { - return metricsTopicArn; - } - - public void setMetricsTopicArn(String metricsTopicArn) { - this.metricsTopicArn = metricsTopicArn; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/Secrets.java b/src/main/java/com/nike/cerberus/domain/environment/Secrets.java deleted file mode 100644 index b4bc65d6..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/Secrets.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -/** - * Container for sensitive data needed by Cerberus. - */ -public class Secrets { - - private CmsSecrets cms = new CmsSecrets(); - - private VaultSecrets vault = new VaultSecrets(); - - public CmsSecrets getCms() { - return cms; - } - - public Secrets setCms(CmsSecrets cms) { - this.cms = cms; - return this; - } - - public VaultSecrets getVault() { - return vault; - } - - public Secrets setVault(VaultSecrets vault) { - this.vault = vault; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/VaultSecrets.java b/src/main/java/com/nike/cerberus/domain/environment/VaultSecrets.java deleted file mode 100644 index c0c9e5a4..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/VaultSecrets.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -import java.util.LinkedList; -import java.util.List; - -/** - * Represents sensitive information used by Vault. - */ -public class VaultSecrets { - - private List keys = new LinkedList<>(); - - private String rootToken; - - public List getKeys() { - return keys; - } - - public VaultSecrets setKeys(List keys) { - this.keys = keys; - return this; - } - - public String getRootToken() { - return rootToken; - } - - public VaultSecrets setRootToken(String rootToken) { - this.rootToken = rootToken; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index d5c3e163..c59e9e97 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -32,6 +32,8 @@ import com.amazonaws.services.cloudformation.AmazonCloudFormationClient; import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.AmazonEC2Client; +import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancing; +import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancingClient; import com.amazonaws.services.identitymanagement.AmazonIdentityManagement; import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; import com.amazonaws.services.kms.AWSKMS; @@ -51,6 +53,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.joda.JodaModule; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.MustacheFactory; import com.google.inject.AbstractModule; @@ -119,6 +122,7 @@ protected void configure() { bind(AWSLambda.class).toInstance(createAmazonClientInstance(AWSLambdaClient.class, region)); bind(AmazonSNS.class).toInstance(createAmazonClientInstance(AmazonSNSClient.class, region)); bind(AmazonRoute53.class).toInstance(createAmazonClientInstance(AmazonRoute53Client.class, region)); + bind(AmazonElasticLoadBalancing.class).toInstance(createAmazonClientInstance(AmazonElasticLoadBalancingClient.class, region)); } /** @@ -132,6 +136,7 @@ protected void configure() { public static ObjectMapper configObjectMapper() { final ObjectMapper om = new ObjectMapper(); om.findAndRegisterModules(); + om.registerModule(new JodaModule()); om.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); om.enable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS); diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index 2b920219..5e229027 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -16,13 +16,11 @@ package com.nike.cerberus.operation.cms; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; +import com.nike.cerberus.domain.environment.CertificateInformation; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AmiTagCheckService; @@ -34,8 +32,8 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.util.List; import java.util.Map; -import java.util.Optional; /** * Operation for creating the CMS cluster. @@ -75,11 +73,10 @@ public CreateCmsClusterOperation(final EnvironmentMetadata environmentMetadata, public void run(final CreateCmsClusterCommand command) { final String environmentName = environmentMetadata.getName(); final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); - final Optional cmsServerCertificateArn = configStore.getServerCertificateArn(Stack.CMS); - final Optional pubKey = configStore.getCertPart(Stack.CMS, ConfigConstants.CERT_PART_PUBKEY); + final List certInfoListForStack = configStore.getCertificationInformationList(); - if (!cmsServerCertificateArn.isPresent() || !pubKey.isPresent()) { - throw new IllegalStateException("CMS certificate has not been uploaded!"); + if (certInfoListForStack.isEmpty()) { + throw new IllegalStateException("Certificate for cerberus environment has not been uploaded!"); } // Make sure the given AmiId is for CMS component. Check if it contains required tag diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java index d51f23d9..4bca8e3c 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsConfigOperation.java @@ -43,8 +43,6 @@ public CreateCmsConfigOperation(final ConfigStore configStore) { @Override public void run(final CreateCmsConfigCommand command) { - configStore.storeCmsAdminGroup(command.getAdminGroup()); - logger.info("Retrieving configuration data from the configuration bucket."); final Properties cmsConfigProperties = configStore.getCmsSystemProperties(); diff --git a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java index 6cc3ea73..2a2c6b7e 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/UpdateCmsConfigOperation.java @@ -71,7 +71,6 @@ public void run(final UpdateCmsConfigCommand command) { if (shouldOverwriteAdminGroup(existingAdminGroup, adminGroupParameter)) { logger.warn(String.format("Updating CMS admin group from '%s' to '%s'", existingAdminGroup, adminGroupParameter)); - configStore.storeCmsAdminGroup(adminGroupParameter); newAdminGroupValue = adminGroupParameter; // overwrite admin group } newProperties.put(CMS_ADMIN_GROUP_KEY, newAdminGroupValue); diff --git a/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java b/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java index c7942488..ca738017 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java +++ b/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java @@ -35,23 +35,27 @@ public class ChainableCommand { private Command command; - private Operation operation; private String[] additionalArgs; - public Command getCommand() { - return command; + + public ChainableCommand() { } - public String[] getAdditionalArgs() { - return additionalArgs; + public ChainableCommand(Command command) { + this.command = command; } - public Operation getOperation() { - return operation; + public ChainableCommand(Command command, String[] additionalArgs) { + this.command = command; + this.additionalArgs = additionalArgs; } - public void setOperation(Operation operation) { - this.operation = operation; + public Command getCommand() { + return command; + } + + public String[] getAdditionalArgs() { + return additionalArgs; } public static final class Builder { diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java index 71345f53..a83e895d 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -37,7 +37,7 @@ */ public abstract class CompositeOperation implements Operation { - private final Logger log = LoggerFactory.getLogger(getClass()); + protected final Logger log = LoggerFactory.getLogger(getClass()); private Injector injector; @@ -65,6 +65,8 @@ private Operation getOperationInstance(Command command) { */ @SuppressWarnings("unchecked") public void run(T compositeCommand) { + beforeChain(); + if (isEnvironmentConfigRequired() && environmentConfig == null) { throw new RuntimeException(String.format("The %s command requires that -f or --file must be supplied as a global option with " + "a path to a valid environment yaml", compositeCommand.getCommandName())); @@ -113,6 +115,20 @@ public void run(T compositeCommand) { } log.info("Finished command: {}\n", chainedCommand.getCommandName()); } + + afterChain(); + } + + /** + * A method that can be overridden that is run before the chain of commands + */ + private void beforeChain() { + } + + /** + * A method that can be overridden that is run after the chain of commands + */ + private void afterChain() { } /** diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java index 70a7841e..0ebe98b0 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java @@ -29,9 +29,8 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.command.core.GenerateCertsCommand; -import com.nike.cerberus.command.core.UploadCertFilesCommand; -import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommand; import java.util.List; @@ -47,52 +46,49 @@ public class CreateEnvironmentOperation extends CompositeOperation getCompositeCommandChain() { List list = Lists.newArrayList( // Step 1 Create the Base Cloud Formation Stack that creates S3 Buckets, Iam Roles and KMS keys needed for config - ChainableCommand.Builder.create().withCommand(new CreateBaseCommand()).build(), + new ChainableCommand(new CreateBaseCommand()), // Step 2 Create the VPC Cloud Formation Stack that Cerberus will use - ChainableCommand.Builder.create().withCommand(new CreateVpcCommand()).build(), + new ChainableCommand(new CreateVpcCommand()), // Step 3 Create the Security Group Cloud Formation Stack - ChainableCommand.Builder.create().withCommand(new CreateSecurityGroupsCommand()).build(), + new ChainableCommand(new CreateSecurityGroupsCommand()), // Step 4 Create the RDS Database Cloud Formation Stack - ChainableCommand.Builder.create().withCommand(new CreateDatabaseCommand()).build() + new ChainableCommand(new CreateDatabaseCommand()) ); // Step 5 Generate the PKCS private and public keys as well as the x509 certificates needed to enable https if (environmentConfig.isGenerateKeysAndCerts()) { - list.add(ChainableCommand.Builder.create().withCommand(new GenerateCertsCommand()).build()); + list.add(new ChainableCommand(new GenerateCertificateFilesCommand())); } list.addAll(Lists.newArrayList( // Step 6 Upload the certs and keys to S3 and the IAM Cert Management service so that the ALB and CMS can use the certs - ChainableCommand.Builder.create().withCommand(new UploadCertFilesCommand()) - .withAdditionalArg(UploadCertFilesCommand.STACK_NAME_LONG_ARG) - .withAdditionalArg(Stack.CMS.getName()) - .build(), + new ChainableCommand(new UploadCertificateFilesCommand()), // Step 7 Create the Application Load Balancer Cloud Formation Stack - ChainableCommand.Builder.create().withCommand(new CreateLoadBalancerCommand()).build(), + new ChainableCommand(new CreateLoadBalancerCommand()), // Step 8 Generate the CMS config with org specific setting and first secrets encrypt, // Upload to S3 for CMS to download at service start - ChainableCommand.Builder.create().withCommand(new CreateCmsConfigCommand()).build(), + new ChainableCommand(new CreateCmsConfigCommand()), // Step 9 Create CMS CMK, create KMS master keys in the regions specified for CMS to use with the AWS Encryption // client to encrypt secure data in a manor that is decryptable in multiple regions and store in cms props - ChainableCommand.Builder.create().withCommand(new CreateCmsCmkCommand()).build(), + new ChainableCommand(new CreateCmsCmkCommand()), // Step 10 Create the CMS Cluster Stack - ChainableCommand.Builder.create().withCommand(new CreateCmsClusterCommand()).build(), + new ChainableCommand(new CreateCmsClusterCommand()), // Step 11 Create the Web Application Fire wall stack - ChainableCommand.Builder.create().withCommand(new CreateWafCommand()).build(), + new ChainableCommand(new CreateWafCommand()), // Step 12 Create the Route 53 DNS Record Stack for origin and the load balancer - ChainableCommand.Builder.create().withCommand(new CreateRoute53Command()).build(), + new ChainableCommand(new CreateRoute53Command()), // Step 13 Create the outer most domain name record that will point to the origin record - ChainableCommand.Builder.create().withCommand(new CreateEdgeDomainRecordCommand()).build() + new ChainableCommand(new CreateEdgeDomainRecordCommand()) )); return list; diff --git a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java new file mode 100644 index 00000000..945e04d2 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; +import com.nike.cerberus.command.cms.CreateCmsConfigCommand; +import com.nike.cerberus.command.core.AddCertificateToAlbCommand; +import com.nike.cerberus.command.composite.RotateCertificatesCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; +import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.service.CloudFormationService; + +import java.util.LinkedList; +import java.util.List; + +public class RotateCertificatesOperation extends CompositeOperation { + + private final CloudFormationService cloudFormationService; + + @Inject + public RotateCertificatesOperation(CloudFormationService cloudFormationService) { + this.cloudFormationService = cloudFormationService; + } + + @Override + protected List getCompositeCommandChain() { + List commandList = new LinkedList<>(); + + if (environmentConfig.isGenerateKeysAndCerts()) { + commandList.add(ChainableCommand.Builder.create().withCommand(new GenerateCertificateFilesCommand()).build()); + } + + commandList.addAll(Lists.newArrayList( + // Add the cert and key files to S3 + ChainableCommand.Builder.create().withCommand(new UploadCertificateFilesCommand()).build(), + // Add the new cert to the ALB + ChainableCommand.Builder.create().withCommand(new AddCertificateToAlbCommand()).build(), + // Generate new CMS config that points to the new cert + ChainableCommand.Builder.create().withCommand(new CreateCmsConfigCommand()).build(), + // Do a rolling reboot of the management service + ChainableCommand.Builder.create().withCommand(new RollingRebootWithHealthCheckCommand()).build() + // Delete the old cert from the ALB and from the Identity Management service and S3 + + // Revoke the Cert? todo + )); + + return commandList; + } + + @Override + public boolean isRunnable(RotateCertificatesCommand command) { + boolean isRunnable = true; + + if (! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getName())) { + log.error("The load-balancer stack must be present in order to rotate certificates"); + isRunnable = false; + } + + if (! cloudFormationService.isStackPresent(Stack.CMS.getName())) { + log.error("The cms stack must be present to rotate certificates"); + } + + return isRunnable; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java b/src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java new file mode 100644 index 00000000..1eaedf32 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 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.services.elasticloadbalancingv2.AmazonElasticLoadBalancing; +import com.amazonaws.services.elasticloadbalancingv2.model.Certificate; +import com.amazonaws.services.elasticloadbalancingv2.model.DescribeListenersRequest; +import com.amazonaws.services.elasticloadbalancingv2.model.DescribeListenersResult; +import com.amazonaws.services.elasticloadbalancingv2.model.RemoveListenerCertificatesRequest; +import com.google.inject.Inject; +import com.nike.cerberus.command.core.AddCertificateToAlbCommand; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.IdentityManagementService; +import com.nike.cerberus.store.ConfigStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AddCertificateToAlbOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final AmazonElasticLoadBalancing amazonElasticLoadBalancing; + private final IdentityManagementService identityManagementService; + private final ConfigStore configStore; + + @Inject + public AddCertificateToAlbOperation(AmazonElasticLoadBalancing amazonElasticLoadBalancing, + IdentityManagementService identityManagementService, + ConfigStore configStore) { + + this.amazonElasticLoadBalancing = amazonElasticLoadBalancing; + this.identityManagementService = identityManagementService; + this.configStore = configStore; + } + + @Override + public void run(AddCertificateToAlbCommand command) { + throw new RuntimeException("Not implemented yet"); + } + + @Override + public boolean isRunnable(AddCertificateToAlbCommand command) { + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java index 871c9395..33acfa81 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java @@ -16,8 +16,6 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.BaseParameters; @@ -72,7 +70,6 @@ public void run(final CreateBaseCommand command) { logger.info("Stack creation complete, initializing the configuration bucket."); environmentMetadata.setBucketName(configStore.getBaseStackOutputs().getConfigBucketName()); configStore.initEnvironmentData(); - configStore.initSecretsData(); logger.info("Initialization complete."); } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java deleted file mode 100644 index 4e64ac01..00000000 --- a/src/main/java/com/nike/cerberus/operation/core/CreateCerberusBackupOperation.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2017 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.auth.policy.Policy; -import com.amazonaws.auth.policy.Principal; -import com.amazonaws.auth.policy.Resource; -import com.amazonaws.auth.policy.Statement; -import com.amazonaws.auth.policy.actions.KMSActions; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.kms.AWSKMS; -import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.services.kms.model.CreateKeyRequest; -import com.amazonaws.services.kms.model.CreateKeyResult; -import com.amazonaws.services.kms.model.Tag; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.AmazonS3Encryption; -import com.amazonaws.services.s3.AmazonS3EncryptionClientBuilder; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.CryptoConfiguration; -import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.util.ISO8601DateFormat; -import com.google.common.collect.ImmutableMap; -import com.nike.cerberus.client.CerberusAdminClient; -import com.nike.cerberus.client.CerberusAdminClientFactory; -import com.nike.cerberus.command.core.CreateCerberusBackupCommand; -import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.backup.CerberusBackupMetadata; -import com.nike.cerberus.domain.backup.CerberusSdbMetadata; -import com.nike.cerberus.domain.cms.SafeDepositBox; -import com.nike.cerberus.domain.environment.BackupRegionInfo; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.MetricsService; -import com.nike.cerberus.service.S3StoreService; -import com.nike.cerberus.store.ConfigStore; -import com.nike.vault.client.model.VaultListResponse; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -import static com.nike.cerberus.module.CerberusModule.getAWSCredentialsProviderChain; - - -public class CreateCerberusBackupOperation implements Operation { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private static final String AWS_PROVIDER = "AWS"; - private static final int MAX_BUCKET_NAME_LENGTH = 63; - - private final ObjectMapper objectMapper; - private final ConfigStore configStore; - private final CerberusAdminClient cerberusAdminClient; - private final MetricsService metricsService; - private final EnvironmentMetadata environmentMetadata; - - private final Map regionToEncryptedStoreServiceMap = new HashMap<>(); - - @Inject - public CreateCerberusBackupOperation(CerberusAdminClientFactory cerberusAdminClientFactory, - ConfigStore configStore, - MetricsService metricsService, - EnvironmentMetadata environmentMetadata) { - - objectMapper = new ObjectMapper(); - objectMapper.findAndRegisterModules(); - objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); - objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - objectMapper.enable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS); - objectMapper.setDateFormat(new ISO8601DateFormat()); - - this.configStore = configStore; - this.metricsService = metricsService; - this.environmentMetadata = environmentMetadata; - - cerberusAdminClient = cerberusAdminClientFactory.createCerberusAdminClient( - configStore.getCerberusBaseUrl()); - } - - @Override - public void run(CreateCerberusBackupCommand command) { - log.info("Starting Cerberus Backup"); - - List regionsToStoreBackups = command.getBackupRegions(); - validateRegions(regionsToStoreBackups); - - Date now = new Date(); - String prefix = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(now); - List sdbMetadataList = cerberusAdminClient.getAllSdbMetadata(); - - int sdbCount = sdbMetadataList.size(); - if (sdbCount < 1) { - throw new RuntimeException("CMS returned no data when listing SDB metadata"); - } else { - log.info(String.format("CMS returned that there are %s SDBs to back up.", sdbMetadataList.size())); - } - - CerberusSdbMetadata cerberusSdbMetadata = new CerberusSdbMetadata(); - for (SafeDepositBox sdb : sdbMetadataList) { - log.info(String.format("Backing up %s", sdb.getName())); - Map> vaultData = recurseVault(sdb.getPath(), new HashMap<>()); - sdb.setData(vaultData); - String key = sdb.getName().toLowerCase().replaceAll("\\W+", "-"); - saveDataToS3(sdb, prefix, key, regionsToStoreBackups); - cerberusSdbMetadata = processMetadata(sdb, cerberusSdbMetadata); - } - - // save metadata - CerberusBackupMetadata metadata = new CerberusBackupMetadata() - .setCerberusUrl(configStore.getCerberusBaseUrl()) - .setBackupDate(now) - .setNumberOfSdbs(sdbMetadataList.size()) - .setNumberOfDataNodes(cerberusSdbMetadata.getNumberOfDataNodes()) - .setNumberOfKeyValuePairs(cerberusSdbMetadata.getNumberOfKeyValuePairs()) - .setNumberOfUniqueOwnerGroups(cerberusSdbMetadata.getUniqueOwnerGroups().size()) - .setNumberOfUniqueIamRoles(cerberusSdbMetadata.getUniqueIamRoles().size()) - .setNumberOfUniqueNonOwnerGroups(cerberusSdbMetadata.getUniqueNonOwnerGroups().size()); - - if (metadata.getNumberOfKeyValuePairs() < 1) { - throw new RuntimeException("The number of backed up key value pairs was less than 1, this probably means something bad is going on"); - } - - String key = "cerberus-backup-metadata.json"; - - saveDataToS3(metadata, prefix, key, regionsToStoreBackups); - - trackMetadataMetrics(metadata); - - regionsToStoreBackups.forEach(region -> { - String bucket = configStore.getBackupInfoForRegion(region).get().getS3Bucket(); - log.info("Backup for region: {} complete, bucket: {}, prefix: {}", region, bucket, prefix); - }); - } - - /** - * Vaildates that the user inputed regions are valid AWS regions - * - * @param regionsToStoreBackups The user inputed list of regions - */ - private void validateRegions(List regionsToStoreBackups) { - regionsToStoreBackups.forEach(region -> { - try { - Regions.fromName(region); - } catch (IllegalArgumentException e) { - throw new RuntimeException(String.format("The region %s is not a valid region", region)); - } - }); - } - - private void trackMetadataMetrics(CerberusBackupMetadata metadata) { - Map dimensions = ImmutableMap.of( - "cerberusUrl", metadata.getCerberusUrl(), - "env", "cerberus-" + environmentMetadata.getName(), - "region", environmentMetadata.getRegionName() - ); - - metricsService.trackGauge("numberOfSdbs", metadata.getNumberOfSdbs(), dimensions); - metricsService.trackGauge("numberOfDataNodes", metadata.getNumberOfDataNodes(), dimensions); - metricsService.trackGauge("numberOfKeyValuePairs", metadata.getNumberOfKeyValuePairs(), dimensions); - metricsService.trackGauge("numberOfUniqueOwnerGroups", metadata.getNumberOfUniqueOwnerGroups(), dimensions); - metricsService.trackGauge("numberOfUniqueIamRoles", metadata.getNumberOfUniqueIamRoles(), dimensions); - metricsService.trackGauge("numberOfUniqueNonOwnerGroups", metadata.getNumberOfUniqueNonOwnerGroups(), dimensions); - } - - /** - * Method to keep track of metadata - */ - private CerberusSdbMetadata processMetadata(SafeDepositBox sdb, final CerberusSdbMetadata currentMetadata) { - CerberusSdbMetadata newMetadata = new CerberusSdbMetadata( - currentMetadata.getNumberOfKeyValuePairs(), - currentMetadata.getNumberOfDataNodes(), - currentMetadata.getUniqueOwnerGroups(), - currentMetadata.getUniqueIamRoles(), - currentMetadata.getUniqueNonOwnerGroups() - ); - - newMetadata.getUniqueOwnerGroups().add(sdb.getOwner()); - - sdb.getIamRolePermissions().forEach((iamRole, permission) -> { - newMetadata.getUniqueIamRoles().add(iamRole); - }); - sdb.getUserGroupPermissions().forEach((userGroup, permission) -> { - newMetadata.getUniqueNonOwnerGroups().add(userGroup); - }); - - Map> vaultNodes = sdb.getData(); - newMetadata.setNumberOfDataNodes(newMetadata.getNumberOfDataNodes() + vaultNodes.size()); - vaultNodes.forEach((path, kvPairs) -> { - newMetadata.setNumberOfKeyValuePairs(newMetadata.getNumberOfKeyValuePairs() + kvPairs.size()); - }); - return newMetadata; - } - - /** - * Recurse a Vault path for data. - * - * @param path The path to recurse - * @return Map of Vault path Strings to Maps of String, String containing the secret kv pairs - */ - private Map> recurseVault(String path, Map> data) { - List keys = getKeys(path); - - keys.forEach(key -> { - String compositeKey = path + key; - if (key.endsWith("/")) { - recurseVault(compositeKey, data); - } else { - data.put(compositeKey, getData(compositeKey)); - } - }); - - return data; - } - - /** - * Lists keys for a vault path. - * - * @param path The path in Vault to list keys for - * @return List of keys, sub folders with have trailing / - */ - private List getKeys(String path) { - VaultListResponse vaultListResponse = cerberusAdminClient.list(path); - return vaultListResponse.getKeys(); - } - - /** - * Downloads Vault data for a given path. - * - * @param path The path of data to download - * @return The data map - */ - private Map getData(String path) { - CerberusAdminClient.GenericVaultResponse response = cerberusAdminClient.readDataGenerically(path); - return response.getData(); - } - - /** - * Using an S3 Encryption client saves the sdb data to the backup bucket / kms key - * - * @param object The sdb data to back up - * @param prefix The prefix / virtual folder to store the encrypted json - */ - private void saveDataToS3(Object object, String prefix, String key, List regions) { - String json = null; - try { - json = objectMapper.writeValueAsString(object); - } catch (JsonProcessingException e) { - throw new RuntimeException(String.format("Failed to serialized SDB data, Prefix: %s, Key: %s", prefix, key), e); - } - - for (String region : regions) { - S3StoreService storeService = regionToEncryptedStoreServiceMap.getOrDefault(region, - getEncryptedStoreServiceForRegion(region)); - - storeService.put(prefix + '/' + key, json); - } - } - - private S3StoreService getEncryptedStoreServiceForRegion(String region) { - Optional backupRegionInfo = configStore.getBackupInfoForRegion(region); - - if (!backupRegionInfo.isPresent()) { - String kmsCmkId = provisionKmsCmkForBackupRegion(region); - String backupBucket = provisionBackupBucketForRegion(region); - configStore.storeBackupInfoForRegion(region, backupBucket, kmsCmkId); - backupRegionInfo = Optional.of(new BackupRegionInfo(backupBucket, kmsCmkId)); - } - - KMSEncryptionMaterialsProvider materialProvider = - new KMSEncryptionMaterialsProvider(backupRegionInfo.get().getKmsCmkId()); - - AmazonS3Encryption encryptionClient = - AmazonS3EncryptionClientBuilder.standard() - .withCredentials(getAWSCredentialsProviderChain()) - .withEncryptionMaterials(materialProvider) - .withCryptoConfiguration(new CryptoConfiguration() - .withAwsKmsRegion(Region.getRegion(Regions.fromName(region)))) - .withRegion(region) - .build(); - - S3StoreService storeService = new S3StoreService(encryptionClient, backupRegionInfo.get().getS3Bucket(), ""); - regionToEncryptedStoreServiceMap.put(region, storeService); - return storeService; - } - - private String provisionBackupBucketForRegion(String region) { - AmazonS3 s3 = AmazonS3ClientBuilder.standard() - .withCredentials(getAWSCredentialsProviderChain()) - .withRegion(region) - .build(); - - Bucket bucket = s3.createBucket(getBackupBucketName(region)); - String bucketName = bucket.getName(); - log.info("Created bucket in region: {} with name: {}", region, bucketName); - return bucketName; - } - - private String getBackupBucketName(String region) { - String name = String.format("%s-%s-backup-%s", environmentMetadata.getName(), region, UUID.randomUUID().toString()); - StringUtils.truncate(name, MAX_BUCKET_NAME_LENGTH); - StringUtils.strip(name, "-"); - return name; - } - - private String provisionKmsCmkForBackupRegion(String region) { - Policy kmsPolicy = new Policy(); - final List statements = new LinkedList<>(); - // allow the configured admin iam principals all permissions - configStore.getBackupAdminIamPrincipals().forEach(principal -> { - log.debug("Adding principal: {} to the CMK Policy for region {}", principal, region); - statements.add(new Statement(Statement.Effect.Allow) - .withId("Principal " + principal + " Has All Actions") - .withPrincipals(new Principal(AWS_PROVIDER, principal, false)) - .withActions(KMSActions.AllKMSActions) - .withResources(new Resource("*"))); - }); - - kmsPolicy.setStatements(statements); - - String policyString = kmsPolicy.toJson(); - - log.debug("Creating key for region {} with policy {}", region, policyString); - - AWSKMS kms = AWSKMSClient.builder().withCredentials(getAWSCredentialsProviderChain()).withRegion(region).build(); - CreateKeyResult createKeyResult = kms.createKey( - new CreateKeyRequest() - .withPolicy(policyString) - .withBypassPolicyLockoutSafetyCheck(true) - .withDescription(String.format("Cerberus Backup Encryption key for env: %S region: %s", - environmentMetadata.getName(), region)) - .withTags( - new Tag().withTagKey("env").withTagValue(environmentMetadata.getName()), - new Tag().withTagKey("region").withTagValue(region), - new Tag().withTagKey("cerberus-backup-key").withTagValue("true") - - ) - ); - - String keyId = createKeyResult.getKeyMetadata().getKeyId(); - - log.info("Created new backup KMS CMK with id: {} for region: {}", keyId, region); - - return keyId; - } - - @Override - public boolean isRunnable(CreateCerberusBackupCommand command) { - if (configStore.getBackupAdminIamPrincipals().isEmpty()) { - log.error("Backup Admin Principals have not been set please run " + SetBackupAdminPrincipalsCommand.COMMAND_NAME); - return false; - } - return true; - } - -} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index fef25030..8d826611 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -16,8 +16,6 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; @@ -65,8 +63,9 @@ public void run(final CreateLoadBalancerCommand command) { final String environmentName = environmentMetadata.getName(); final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); - final String sslCertificateArn = configStore.getServerCertificateArn(Stack.CMS) - .orElseThrow(() -> new IllegalStateException("Could not retrieve SSL certificate ARN!")); + // Use latest cert, if there happens to be more than one for some reason + final String sslCertificateArn = configStore.getCertificationInformationList() + .getLast().getIdentityManagementCertificateArn(); final LoadBalancerParameters loadBalancerParameters = new LoadBalancerParameters() .setVpcId(vpcOutputs.getVpcId()) @@ -88,14 +87,24 @@ public void run(final CreateLoadBalancerCommand command) { @Override public boolean isRunnable(final CreateLoadBalancerCommand command) { + boolean isRunnable = true; String environmentName = environmentMetadata.getName(); - try { - cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); - } catch (IllegalArgumentException iae) { + + if (!cloudFormationService.isStackPresent(Stack.SECURITY_GROUPS.getFullName(environmentName))) { logger.error("The security group stack must exist to create the load balancer!"); - return false; + isRunnable = false; + } + + if (configStore.getCertificationInformationList().isEmpty()) { + logger.error("TLS Certificate files have not been uploaded for environment"); + isRunnable = false; + } + + if (cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { + logger.error("The load balancer stack already exists"); + isRunnable = false; } - return !cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName)); + return isRunnable; } } diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java similarity index 89% rename from src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java rename to src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java index 74aea5c7..fdbba4f2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java @@ -16,7 +16,7 @@ package com.nike.cerberus.operation.core; -import com.nike.cerberus.command.core.GenerateCertsCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CertificateService; @@ -37,7 +37,7 @@ /** * Operation that uses the cert service to generate the certificates needed to enable https in a Cerberus Env. */ -public class GenerateCertsOperation implements Operation { +public class GenerateCertificateFilesOperation implements Operation { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -46,9 +46,9 @@ public class GenerateCertsOperation implements Operation { private final ConsoleService consoleService; @Inject - public GenerateCertsOperation(CertificateService certService, - EnvironmentMetadata environmentMetadata, - ConsoleService consoleService) { + public GenerateCertificateFilesOperation(CertificateService certService, + EnvironmentMetadata environmentMetadata, + ConsoleService consoleService) { this.certService = certService; this.environmentMetadata = environmentMetadata; @@ -60,7 +60,7 @@ public GenerateCertsOperation(CertificateService certService, justification = "I do not want to catch 1000 individual exceptions" ) @Override - public void run(GenerateCertsCommand command) { + public void run(GenerateCertificateFilesCommand command) { // The common name ex: demo.example.com String commonName = StringUtils.isNotBlank(command.getEdgeDomainNameOverride()) ? command.getEdgeDomainNameOverride() : @@ -116,7 +116,7 @@ public void run(GenerateCertsCommand command) { } @Override - public boolean isRunnable(GenerateCertsCommand command) { + public boolean isRunnable(GenerateCertificateFilesCommand command) { return true; } } diff --git a/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java b/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java deleted file mode 100644 index 162758eb..00000000 --- a/src/main/java/com/nike/cerberus/operation/core/SetBackupAdminPrincipalsOperation.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2017 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.auth.policy.Policy; -import com.amazonaws.auth.policy.Principal; -import com.amazonaws.auth.policy.Resource; -import com.amazonaws.auth.policy.Statement; -import com.amazonaws.auth.policy.actions.KMSActions; -import com.amazonaws.services.kms.AWSKMS; -import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.services.kms.model.PutKeyPolicyRequest; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; -import com.nike.cerberus.command.core.SetBackupAdminPrincipalsCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.store.ConfigStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import static com.nike.cerberus.module.CerberusModule.getAWSCredentialsProviderChain; - -/** - * Operation to update which principals besides for the root account will have permissions to use the backup cmk, - * AKA create and restore backups - */ -public class SetBackupAdminPrincipalsOperation implements Operation { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private static final String AWS_PROVIDER = "AWS"; - - private final ConfigStore configStore; - - private final AWSSecurityTokenService sts; - - @Inject - public SetBackupAdminPrincipalsOperation(ConfigStore configStore, - AWSSecurityTokenService sts) { - - this.configStore = configStore; - this.sts = sts; - } - - @Override - public void run(SetBackupAdminPrincipalsCommand command) { - GetCallerIdentityResult identityResult = sts.getCallerIdentity(new GetCallerIdentityRequest()); - String accountId = identityResult.getAccount(); - String rootArn = String.format("arn:aws:iam::%s:root", accountId); - String adminRoleArn = configStore.getAccountAdminArn().get(); - - Set principals = new HashSet<>(); - principals.add(rootArn); - principals.add(adminRoleArn); - principals.addAll(command.getAdditionalPrincipals()); - - configStore.storeBackupAdminIamPrincipals(principals); - - if (!configStore.getRegionBackupBucketMap().isEmpty()) { - configStore.getRegionBackupBucketMap().forEach((region, backupRegionInfo) -> { - final List statements = new LinkedList<>(); - principals.forEach(principal -> { - log.debug("Adding principal: {} to the CMK Policy for region {}", principal, region); - statements.add(new Statement(Statement.Effect.Allow) - .withId("Principal " + principal + " Has All Actions") - .withPrincipals(new Principal(AWS_PROVIDER, principal, false)) - .withActions(KMSActions.AllKMSActions) - .withResources(new Resource("*"))); - }); - - Policy kmsPolicy = new Policy(); - kmsPolicy.setStatements(statements); - String policyString = kmsPolicy.toJson(); - - log.debug("Updating key {} for region {} with policy {}", backupRegionInfo.getKmsCmkId(), region, policyString); - - AWSKMS kms = AWSKMSClient.builder().withCredentials(getAWSCredentialsProviderChain()).withRegion(region).build(); - PutKeyPolicyRequest request = new PutKeyPolicyRequest() - .withKeyId(backupRegionInfo.getKmsCmkId()) - .withPolicyName("default") - .withBypassPolicyLockoutSafetyCheck(true) - .withPolicy(policyString); - - kms.putKeyPolicy(request); - - log.info("Successfully updated key {} in region {} to allow the following principals access {}", - backupRegionInfo.getKmsCmkId(), region, String.join(", ", principals)); - }); - } - } - - @Override - public boolean isRunnable(SetBackupAdminPrincipalsCommand command) { - Optional adminIamPrincipalArn = configStore.getAccountAdminArn(); - if (!adminIamPrincipalArn.isPresent()) { - log.error("The admin IAM principal must be set for this environment"); - return false; - } - return true; - } - -} diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java deleted file mode 100644 index 950daf72..00000000 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertFilesOperation.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2017 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.beust.jcommander.internal.Sets; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableSet; -import com.nike.cerberus.command.core.UploadCertFilesCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.environment.Stack; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.IdentityManagementService; -import com.nike.cerberus.store.ConfigStore; -import com.nike.cerberus.util.UuidSupplier; -import org.apache.commons.io.filefilter.RegexFileFilter; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Set; -import java.util.StringJoiner; - -import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_CHAIN_FILE; -import static com.nike.cerberus.service.CertificateService.DOMAIN_CERT_FILE; -import static com.nike.cerberus.service.CertificateService.DOMAIN_PKCS1_KEY_FILE; -import static com.nike.cerberus.service.CertificateService.DOMAIN_PKCS8_KEY_FILE; -import static com.nike.cerberus.service.CertificateService.DOMAIN_PUBLIC_KEY_FILE; - -/** - * Handles uploading of certificate files to IAM and the config store. - */ -public class UploadCertFilesOperation implements Operation { - - public static final Set EXPECTED_FILE_NAMES = ImmutableSet.of( - DOMAIN_CERT_CHAIN_FILE, - DOMAIN_CERT_FILE, - DOMAIN_PKCS1_KEY_FILE, - DOMAIN_PKCS8_KEY_FILE, - DOMAIN_PUBLIC_KEY_FILE - ); - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final EnvironmentMetadata environmentMetadata; - - private final UuidSupplier uuidSupplier; - - private final ConfigStore configStore; - - private final IdentityManagementService identityManagementService; - - @Inject - public UploadCertFilesOperation(final EnvironmentMetadata environmentMetadata, - final UuidSupplier uuidSupplier, - final ConfigStore configStore, - final IdentityManagementService identityManagementService) { - this.environmentMetadata = environmentMetadata; - this.uuidSupplier = uuidSupplier; - this.configStore = configStore; - this.identityManagementService = identityManagementService; - } - - @Override - public void run(final UploadCertFilesCommand command) { - - checkForRequiredFiles(command.getCertPath()); - - final Stack stack = command.getStack(); - final Path certPath = command.getCertPath(); - final String caContents = getFileContents(certPath, DOMAIN_CERT_CHAIN_FILE); - final String certContents = getFileContents(certPath, DOMAIN_CERT_FILE); - final String keyContents = getFileContents(certPath, DOMAIN_PKCS1_KEY_FILE); - final String pkcs8KeyContents = getFileContents(certPath, DOMAIN_PKCS8_KEY_FILE); - final String pubKeyContents = getFileContents(certPath, DOMAIN_PUBLIC_KEY_FILE); - final String certificateName = stack.getName() + "_" + uuidSupplier.get(); - - logger.info("Uploading certificate to IAM for {}, with name of {}.", stack.getName(), certificateName); - String id = identityManagementService.uploadServerCertificate(certificateName, getPath(), - certContents, caContents, keyContents); - - logger.info("Cert ID: {}", id); - - logger.info("Uploading certificate parts to the configuration bucket."); - configStore.storeCert(stack, certificateName, caContents, certContents, keyContents, pkcs8KeyContents, pubKeyContents); - - logger.info("Uploading certificate completed."); - } - - public void checkForRequiredFiles(final Path certDir) { - - final File certDirectory = certDir.toFile(); - final Set filenames = Sets.newHashSet(); - - final FilenameFilter filter = new RegexFileFilter("^.*\\.(pem|crt)$"); - final File[] files = certDirectory.listFiles(filter); - Arrays.stream(files).forEach(file -> filenames.add(file.getName())); - - if (!filenames.containsAll(EXPECTED_FILE_NAMES)) { - final StringJoiner sj = new StringJoiner(", ", "[", "]"); - EXPECTED_FILE_NAMES.stream().forEach(sj::add); - throw new RuntimeException("Not all expected files are present! Expected: " + sj.toString()); - } - } - - @Override - public boolean isRunnable(final UploadCertFilesCommand command) { - if (StringUtils.isBlank(environmentMetadata.getBucketName())) { - logger.error("Environment isn't initialized, unable to upload certificate."); - return false; - } - - final String serverCertificateName = configStore.getServerCertificateName(command.getStack()); - if (StringUtils.isNotBlank(serverCertificateName) && !command.isOverwrite()) { - logger.error("Certificate already uploaded for this stack and environment. Use --overwrite flag to force upload."); - return false; - } - - return true; - } - - private String getFileContents(final Path path, final String filename) { - Preconditions.checkNotNull(path); - Preconditions.checkNotNull(filename); - - File file = new File(path.toString(), filename); - if (file.exists() && file.canRead()) { - try { - return new String(Files.readAllBytes(file.toPath()), Charset.forName("UTF-8")); - } catch (IOException e) { - throw new IllegalStateException("Failed to read the following file: " + file.getAbsolutePath()); - } - } else { - throw new IllegalArgumentException("The file is not readable: " + file.getAbsolutePath()); - } - } - - private String getPath() { - return "/cerberus/" + environmentMetadata.getName() + "/"; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java new file mode 100644 index 00000000..e587835c --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CertificateService; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + +/** + * Handles uploading of certificate files to IAM and the config store. + */ +public class UploadCertificatesFilesOperation implements Operation { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final EnvironmentMetadata environmentMetadata; + private final CertificateService certificateService; + + @Inject + public UploadCertificatesFilesOperation(EnvironmentMetadata environmentMetadata, + CertificateService certificateService) { + + this.environmentMetadata = environmentMetadata; + this.certificateService = certificateService; + } + + @Override + public void run(final UploadCertificateFilesCommand command) { + certificateService.uploadCertFiles(command.getCertPath().toFile()); + } + + @Override + public boolean isRunnable(final UploadCertificateFilesCommand command) { + if (StringUtils.isBlank(environmentMetadata.getBucketName())) { + logger.error("Environment isn't initialized, unable to upload certificate."); + return false; + } + + return true; + } +} diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index bcbf3917..39e87aac 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -24,13 +24,24 @@ import com.amazonaws.services.route53.model.RRType; import com.amazonaws.services.route53.model.ResourceRecord; import com.amazonaws.services.route53.model.ResourceRecordSet; +import com.amazonaws.util.StringInputStream; +import com.beust.jcommander.internal.Sets; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.gson.Gson; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.environment.CertificateInformation; +import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.UuidSupplier; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.apache.commons.io.filefilter.RegexFileFilter; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator; import org.bouncycastle.util.io.pem.PemObject; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.shredzone.acme4j.Authorization; import org.shredzone.acme4j.Certificate; import org.shredzone.acme4j.Registration; @@ -55,11 +66,15 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; +import java.io.FilenameFilter; import java.io.IOException; import java.io.Writer; import java.net.URI; +import java.nio.charset.Charset; +import java.nio.file.Files; import java.security.KeyPair; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; @@ -67,6 +82,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -116,15 +132,35 @@ public class CertificateService { protected static final String CHALLENGE_ENTRY_TEMPLATE = "_acme-challenge.%s"; + protected static final Set EXPECTED_FILE_NAMES = ImmutableSet.of( + DOMAIN_CERT_CHAIN_FILE, + DOMAIN_CERT_FILE, + DOMAIN_PKCS1_KEY_FILE, + DOMAIN_PKCS8_KEY_FILE, + DOMAIN_PUBLIC_KEY_FILE + ); + private final ConsoleService console; private final AmazonRoute53 route53; + private final UuidSupplier uuidSupplier; + private final ConfigStore configStore; + private final IdentityManagementService identityManagementService; + private final EnvironmentMetadata environmentMetadata; @Inject public CertificateService(ConsoleService console, - AmazonRoute53 route53) { + AmazonRoute53 route53, + UuidSupplier uuidSupplier, + ConfigStore configStore, + IdentityManagementService identityManagementService, + EnvironmentMetadata environmentMetadata) { this.console = console; this.route53 = route53; + this.uuidSupplier = uuidSupplier; + this.configStore = configStore; + this.identityManagementService = identityManagementService; + this.environmentMetadata = environmentMetadata; } /** @@ -455,4 +491,97 @@ protected void createPKCS8PrivateKeyPemFileFromKeyPair(KeyPair keyPair, File cer throw new RuntimeException("Failed to create PKCS8 private key pem", e); } } + + /** + * Uploads the required files to enable tls to identity management and s3 + * + * @param certDir The directory containing the required files + */ + public void uploadCertFiles(File certDir) { + + checkForRequiredFiles(certDir); + + final String caContents = getFileContents(certDir, DOMAIN_CERT_CHAIN_FILE); + final String certContents = getFileContents(certDir, DOMAIN_CERT_FILE); + final String keyContents = getFileContents(certDir, DOMAIN_PKCS1_KEY_FILE); + final String pkcs8KeyContents = getFileContents(certDir, DOMAIN_PKCS8_KEY_FILE); + final String pubKeyContents = getFileContents(certDir, DOMAIN_PUBLIC_KEY_FILE); + final String certificateName = String.format("cerberus_%s_%s", environmentMetadata.getName(), uuidSupplier.get()); + + log.info("Uploading certificate files to IAM with name of {}.", certificateName); + String identityManagementCertificateName = identityManagementService.uploadServerCertificate(certificateName, getPath(), + certContents, caContents, keyContents); + log.info("Identity Management Cert Name: {}", identityManagementCertificateName); + + log.info("Uploading certificate parts to the configuration bucket."); + X509Certificate certificate; + try { + certificate = CertificateUtils.readX509Certificate(new StringInputStream(certContents)); + } catch (IOException e) { + throw new RuntimeException("Failed to parse x509 cert", e); + } + + List sans = new LinkedList<>(); + try { + certificate.getSubjectAlternativeNames().forEach(o -> sans.add(o.get(1).toString())); + } catch (Exception e) { + log.error("Failed to parse subject alternative names from x509 cert"); + } + + CertificateInformation certificateInformation = CertificateInformation.Builder.create() + .withIdentityManagementCertificateName(identityManagementCertificateName) + .withIdentityManagementCertificateArn(identityManagementService.getServerCertificateArn(identityManagementCertificateName).get()) + .withCommonName(StringUtils.removeStart(certificate.getSubjectX500Principal().getName(), "CN=")) + .withSubjectAlternateNames(sans) + .withNotBefore(new DateTime(certificate.getNotBefore(), DateTimeZone.UTC)) + .withNotAfter(new DateTime(certificate.getNotAfter(), DateTimeZone.UTC)) + .withUploaded(DateTime.now(DateTimeZone.UTC)) + .build(); + + configStore.storeCert(certificateInformation, caContents, certContents, keyContents, pkcs8KeyContents, pubKeyContents); + log.info("Successfully uploaded Certificate: {}", certificateInformation); + } + + /** + * Validates that the requires files to enable https for Cerberus are present see the constants at the top of this file. + * + * @param certDir The directory that contains the required files + */ + private void checkForRequiredFiles(File certDir) { + + final Set filenames = Sets.newHashSet(); + + final FilenameFilter filter = new RegexFileFilter("^.*\\.(pem|crt)$"); + final File[] files = certDir.listFiles(filter); + Arrays.stream(files).forEach(file -> filenames.add(file.getName())); + + if (!filenames.containsAll(EXPECTED_FILE_NAMES)) { + final StringJoiner sj = new StringJoiner(", ", "[", "]"); + EXPECTED_FILE_NAMES.forEach(sj::add); + throw new RuntimeException("Not all expected files are present! Expected: " + sj.toString()); + } + } + + /** + * Reads the data from the file for uploading + */ + private String getFileContents(final File certDir, final String filename) { + Preconditions.checkNotNull(certDir); + Preconditions.checkNotNull(filename); + + File file = new File(certDir.getAbsolutePath() + File.separator + filename); + if (file.exists() && file.canRead()) { + try { + return new String(Files.readAllBytes(file.toPath()), Charset.forName("UTF-8")); + } catch (IOException e) { + throw new IllegalStateException("Failed to read the following file: " + file.getAbsolutePath()); + } + } else { + throw new IllegalArgumentException("The file is not readable: " + file.getAbsolutePath()); + } + } + + private String getPath() { + return "/cerberus/" + environmentMetadata.getName() + "/"; + } } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 38692ed7..90d2eed4 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -41,18 +41,12 @@ import com.nike.cerberus.domain.cloudformation.Route53Parameters; import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; -import com.nike.cerberus.domain.cloudformation.VaultOutputs; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.cloudformation.VpcParameters; -import com.nike.cerberus.domain.environment.BackupRegionInfo; -import com.nike.cerberus.domain.environment.Environment; -import com.nike.cerberus.domain.environment.Secrets; +import com.nike.cerberus.domain.environment.CerberusEnvironmentData; +import com.nike.cerberus.domain.environment.CertificateInformation; import com.nike.cerberus.domain.environment.Stack; -import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.IdentityManagementService; -import com.nike.cerberus.service.S3StoreService; -import com.nike.cerberus.service.SaltGenerator; -import com.nike.cerberus.service.StoreService; +import com.nike.cerberus.service.*; import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -66,6 +60,7 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.LinkedList; import static com.nike.cerberus.ConfigConstants.ADMIN_ROLE_ARN_KEY; import static com.nike.cerberus.ConfigConstants.CERT_PART_CA; @@ -81,6 +76,7 @@ import static com.nike.cerberus.ConfigConstants.JDBC_USERNAME_KEY; import static com.nike.cerberus.ConfigConstants.ROOT_USER_ARN_KEY; import static com.nike.cerberus.ConfigConstants.SYSTEM_CONFIGURED_CMS_PROPERTIES; +import static com.nike.cerberus.ConfigConstants.CMS_CERTIFICATE_TO_USE; import static com.nike.cerberus.module.CerberusModule.CONFIG_OBJECT_MAPPER; /** @@ -98,17 +94,13 @@ public class ConfigStore { private final CloudFormationObjectMapper cloudFormationObjectMapper; - private final IdentityManagementService iamService; - private final AmazonS3 s3Client; private final EnvironmentMetadata environmentMetadata; private final SaltGenerator saltGenerator; - private final Object envDataLock = new Object(); - - private final Object secretsDataLock = new Object(); + private final Object cerberusEnvironmentDataLock = new Object(); private StoreService encryptedConfigStoreService; @@ -119,7 +111,6 @@ public class ConfigStore { @Inject public ConfigStore(final AmazonS3 s3Client, final CloudFormationService cloudFormationService, - final IdentityManagementService iamService, final AWSSecurityTokenService securityTokenService, final EnvironmentMetadata environmentMetadata, final SaltGenerator saltGenerator, @@ -127,7 +118,6 @@ public ConfigStore(final AmazonS3 s3Client, final CloudFormationObjectMapper cloudFormationObjectMapper) { this.cloudFormationService = cloudFormationService; - this.iamService = iamService; this.configObjectMapper = configObjectMapper; this.cloudFormationObjectMapper = cloudFormationObjectMapper; this.s3Client = s3Client; @@ -136,51 +126,15 @@ public ConfigStore(final AmazonS3 s3Client, this.securityTokenService = securityTokenService; } - /** - * Gets the server certificate name from the config store. - * - * @param stack Stack name - */ - public String getServerCertificateName(final Stack stack) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - return environment.getServerCertificateIdMap().get(stack); - } - } - - /** - * Gets the server certificate ARN for the stack name. - * - * @param stack Stack name - * @return ARN - */ - public Optional getServerCertificateArn(final Stack stack) { - final String certificateName = getServerCertificateName(stack); - return iamService.getServerCertificateArn(certificateName); - } - - /** - * Stores the CMS admin group. - * - * @param adminGroup Admin Group - */ - public void storeCmsAdminGroup(final String adminGroup) { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getCms().setAdminGroup(adminGroup); - saveSecretsData(secrets); - } - } - /** * Retrieves the CMS database password from the config store. * * @return CMS database password */ public Optional getCmsDatabasePassword() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - return Optional.ofNullable(secrets.getCms().getDatabasePassword()); + synchronized (cerberusEnvironmentDataLock) { + final CerberusEnvironmentData environmentData = getDecryptedEnvironmentData(); + return Optional.ofNullable(environmentData.getDatabasePassword()); } } @@ -190,68 +144,48 @@ public Optional getCmsDatabasePassword() { * @param databasePassword Database password */ public void storeCmsDatabasePassword(final String databasePassword) { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - secrets.getCms().setDatabasePassword(databasePassword); - saveSecretsData(secrets); - } - } - - /** - * Gets the Vault root token from the config store. - * - * @return Vault root token - */ - public String getVaultRootToken() { - synchronized (secretsDataLock) { - final Secrets secrets = getSecretsData(); - final String rootToken = secrets.getVault().getRootToken(); - return (rootToken != null) ? rootToken : ""; + synchronized (cerberusEnvironmentDataLock) { + final CerberusEnvironmentData environmentData = getDecryptedEnvironmentData(); + environmentData.setDatabasePassword(databasePassword); + saveEnvironmentData(environmentData); } } - /** - * Returns the contents of a specific certificate part that's been uploaded for a stack. - * - * @param stack - * @param part - * @return - */ - public Optional getCertPart(final Stack stack, final String part) { - return getEncryptedObject(buildCertFilePath(stack, part)); - } - /** * Stores the certificate files encrypted and adds the certificate name to the environment data. * - * @param stack Stack that the cert is for - * @param certificateName Certificate name in IAM - * @param caContents CA chain - * @param certContents Certificate body - * @param keyContents Certificate key - * @param pubKeyContents Certificate public key - */ - public void storeCert(final Stack stack, - final String certificateName, - final String caContents, - final String certContents, - final String keyContents, - final String pkcs8KeyContents, - final String pubKeyContents) { - - saveEncryptedObject(buildCertFilePath(stack, CERT_PART_CA), caContents); - saveEncryptedObject(buildCertFilePath(stack, CERT_PART_CERT), certContents); - saveEncryptedObject(buildCertFilePath(stack, CERT_PART_KEY), keyContents); - saveEncryptedObject(buildCertFilePath(stack, CERT_PART_PKCS8_KEY), pkcs8KeyContents); - saveEncryptedObject(buildCertFilePath(stack, CERT_PART_PUBKEY), pubKeyContents); - - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - environment.getServerCertificateIdMap().put(stack, certificateName); + * @param certificateInformation Certificate information for cert + * @param caContents CA chain + * @param certContents Certificate body + * @param keyContents Certificate key + * @param pubKeyContents Certificate public key + */ + public void storeCert(CertificateInformation certificateInformation, + String caContents, + String certContents, + String keyContents, + String pkcs8KeyContents, + String pubKeyContents) { + + String name = certificateInformation.getIdentityManagementCertificateName(); + saveEncryptedObject(buildCertFilePath(name, CERT_PART_CA), caContents); + saveEncryptedObject(buildCertFilePath(name, CERT_PART_CERT), certContents); + saveEncryptedObject(buildCertFilePath(name, CERT_PART_KEY), keyContents); + saveEncryptedObject(buildCertFilePath(name, CERT_PART_PKCS8_KEY), pkcs8KeyContents); + saveEncryptedObject(buildCertFilePath(name, CERT_PART_PUBKEY), pubKeyContents); + + synchronized (cerberusEnvironmentDataLock) { + final CerberusEnvironmentData environment = getDecryptedEnvironmentData(); + environment.addNewCertificateData(certificateInformation); + saveEnvironmentData(environment); } } + public LinkedList getCertificationInformationList() { + return getDecryptedEnvironmentData().getCertificateData(); + } + public void storeCmsEnvConfig(final Properties cmsConfigMap) { final StringBuilder cmsConfigContents = new StringBuilder(); @@ -297,7 +231,7 @@ private Properties generateBaseCmsSystemProperties() { final BaseOutputs baseOutputs = getBaseStackOutputs(); final DatabaseOutputs databaseOutputs = getDatabaseStackOutputs(); final BaseParameters baseParameters = getBaseStackParameters(); - final String cmsDatabasePassword = getSecretsData().getCms().getDatabasePassword(); + final String cmsDatabasePassword = getDecryptedEnvironmentData().getDatabasePassword(); final GetCallerIdentityResult callerIdentity = securityTokenService.getCallerIdentity( new GetCallerIdentityRequest()); @@ -311,19 +245,12 @@ private Properties generateBaseCmsSystemProperties() { properties.put(JDBC_USERNAME_KEY, ConfigConstants.DEFAULT_CMS_DB_NAME); properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword); properties.put(CMS_ENV_NAME, environmentMetadata.getName()); + // Ust the latest uploaded certificate + properties.put(CMS_CERTIFICATE_TO_USE, getCertificationInformationList().getLast()); return properties; } - public Optional getAccountAdminArn() { - final BaseParameters baseParameters = getBaseStackParameters(); - return Optional.ofNullable(baseParameters.getAccountAdminArn()); - } - - public String getCerberusBaseUrl() { - return String.format("https://%s", getEnvironmentData().getDomainName()); - } - /** * System properties not set with -P param */ @@ -380,7 +307,7 @@ public Optional getConfigProperties(String path) { /** * returns the complete stack name */ - public String getStackLogicalId(Stack stack) { + public String getCloudFormationStackName(Stack stack) { return stack.getFullName(environmentMetadata.getName()); } @@ -390,7 +317,7 @@ public String getStackLogicalId(Stack stack) { * @return Base parameters */ public BaseParameters getBaseStackParameters() { - return getStackParameters(getStackLogicalId(Stack.BASE), BaseParameters.class); + return getStackParameters(getCloudFormationStackName(Stack.BASE), BaseParameters.class); } /** @@ -399,7 +326,7 @@ public BaseParameters getBaseStackParameters() { * @return Base outputs */ public BaseOutputs getBaseStackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.BASE), BaseOutputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.BASE), BaseOutputs.class); } /** @@ -408,7 +335,7 @@ public BaseOutputs getBaseStackOutputs() { * @return Base parameters */ public VpcParameters getVpcStackParameters() { - return getStackParameters(getStackLogicalId(Stack.VPC), VpcParameters.class); + return getStackParameters(getCloudFormationStackName(Stack.VPC), VpcParameters.class); } /** @@ -417,7 +344,7 @@ public VpcParameters getVpcStackParameters() { * @return Base outputs */ public VpcOutputs getVpcStackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.VPC), VpcOutputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.VPC), VpcOutputs.class); } /** @@ -426,7 +353,7 @@ public VpcOutputs getVpcStackOutputs() { * @return Base parameters */ public SecurityGroupParameters getSecurityGroupStackParameters() { - return getStackParameters(getStackLogicalId(Stack.SECURITY_GROUPS), SecurityGroupParameters.class); + return getStackParameters(getCloudFormationStackName(Stack.SECURITY_GROUPS), SecurityGroupParameters.class); } /** @@ -435,7 +362,7 @@ public SecurityGroupParameters getSecurityGroupStackParameters() { * @return Base outputs */ public SecurityGroupOutputs getSecurityGroupStackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.SECURITY_GROUPS), SecurityGroupOutputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.SECURITY_GROUPS), SecurityGroupOutputs.class); } /** @@ -444,7 +371,7 @@ public SecurityGroupOutputs getSecurityGroupStackOutputs() { * @return Base outputs */ public LoadBalancerOutputs getLoadBalancerStackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.LOAD_BALANCER), LoadBalancerOutputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.LOAD_BALANCER), LoadBalancerOutputs.class); } /** @@ -453,7 +380,7 @@ public LoadBalancerOutputs getLoadBalancerStackOutputs() { * @return Base parameters */ public DatabaseParameters getDatabaseStackParameters() { - return getStackParameters(getStackLogicalId(Stack.DATABASE), DatabaseParameters.class); + return getStackParameters(getCloudFormationStackName(Stack.DATABASE), DatabaseParameters.class); } /** @@ -462,7 +389,7 @@ public DatabaseParameters getDatabaseStackParameters() { * @return Base parameters */ public Route53Parameters getRoute53Parameters() { - return getStackParameters(getStackLogicalId(Stack.ROUTE53), Route53Parameters.class); + return getStackParameters(getCloudFormationStackName(Stack.ROUTE53), Route53Parameters.class); } /** @@ -471,7 +398,7 @@ public Route53Parameters getRoute53Parameters() { * @return Base parameters */ public Route53Outputs getRoute53StackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.ROUTE53), Route53Outputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.ROUTE53), Route53Outputs.class); } /** @@ -480,16 +407,7 @@ public Route53Outputs getRoute53StackOutputs() { * @return Base outputs */ public DatabaseOutputs getDatabaseStackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.DATABASE), DatabaseOutputs.class); - } - - /** - * Get the Vault stack outputs. - * - * @return Vault outputs - */ - public VaultOutputs getVaultStackOutputs() { - return getStackOutputs(Stack.VAULT, VaultOutputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.DATABASE), DatabaseOutputs.class); } /** @@ -498,7 +416,7 @@ public VaultOutputs getVaultStackOutputs() { * @return CMS parameters */ public CmsParameters getCmsStackParamters() { - return getStackParameters(getStackLogicalId(Stack.CMS), CmsParameters.class); + return getStackParameters(getCloudFormationStackName(Stack.CMS), CmsParameters.class); } /** @@ -507,7 +425,7 @@ public CmsParameters getCmsStackParamters() { * @return CMS outputs */ public CmsOutputs getCmsStackOutputs() { - return getStackOutputs(getStackLogicalId(Stack.CMS), CmsOutputs.class); + return getStackOutputs(getCloudFormationStackName(Stack.CMS), CmsOutputs.class); } /** @@ -515,30 +433,8 @@ public CmsOutputs getCmsStackOutputs() { * * @return Gateway parameters */ - public GatewayParameters getGatewayStackParamters() { - return getStackParameters(Stack.GATEWAY, GatewayParameters.class); - } - - /** - * Get the stack outputs for a specific stack name. - * - * @param stack Stack name - * @param outputClass Outputs class - * @param Outputs type - * @return Outputs - */ - public M getStackOutputs(final Stack stack, final Class outputClass) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - final String stackId = environment.getStackMap().get(stack); - - if (!cloudFormationService.isStackPresent(stackId)) { - throw new IllegalStateException("The specified stack doesn't exist for specified environment."); - } - - final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); - return cloudFormationObjectMapper.convertValue(stackOutputs, outputClass); - } + public GatewayParameters getGatewayStackParameters() { + return getStackParameters(getCloudFormationStackName(Stack.GATEWAY), GatewayParameters.class); } /** @@ -558,27 +454,6 @@ public M getStackOutputs(final String stackName, final Class outputClass) return cloudFormationObjectMapper.convertValue(stackOutputs, outputClass); } - /** - * Get the stack parameters for a specific stack name. - * - * @param stack Stack name - * @param parameterClass Parameters class - * @param Parameters type - * @return Parameters - */ - public M getStackParameters(final Stack stack, final Class parameterClass) { - synchronized (envDataLock) { - final String stackId = stack.getFullName(environmentMetadata.getName()); - - if (!cloudFormationService.isStackPresent(stackId)) { - throw new IllegalStateException("The specified stack doesn't exist for the specified environment"); - } - - final Map stackParameters = cloudFormationService.getStackParameters(stackId); - return cloudFormationObjectMapper.convertValue(stackParameters, parameterClass); - } - } - /** * Get the stack parameters for a specific stack name. * @@ -597,72 +472,30 @@ public M getStackParameters(final String stackName, final Class parameter } /** - * Initializes the environment data in the config bucket. + * Initializes the environmentData data in the config bucket. */ public void initEnvironmentData() { - synchronized (envDataLock) { - try { - getEnvironmentData(); - final String errorMessage = "Attempting to initialize environment data, but it already exists!"; - logger.error(errorMessage); - throw new RuntimeException(errorMessage); - } catch (IllegalStateException ise) { - final Environment environment = new Environment(); - saveEnvironmentData(environment); - } - } - } - - /** - * Initializes the secrets data in the config bucket. - */ - public void initSecretsData() { - synchronized (secretsDataLock) { + synchronized (cerberusEnvironmentDataLock) { try { - getSecretsData(); - final String errorMessage = "Attempting to initialize secrets data, but it already exists!"; + getDecryptedEnvironmentData(); + final String errorMessage = "Attempting to initialize environmentData data, but it already exists!"; logger.error(errorMessage); throw new RuntimeException(errorMessage); } catch (IllegalStateException ise) { - final Secrets secrets = new Secrets(); - saveSecretsData(secrets); + final CerberusEnvironmentData environmentData = new CerberusEnvironmentData(); + saveEnvironmentData(environmentData); } } } - private Secrets getSecretsData() { + private CerberusEnvironmentData getDecryptedEnvironmentData() { initEncryptedConfigStoreService(); - final Optional secretsData = encryptedConfigStoreService.get(ConfigConstants.SECRETS_DATA_FILE); - - if (secretsData.isPresent()) { - try { - return configObjectMapper.readValue(secretsData.get(), Secrets.class); - } catch (IOException e) { - throw new IllegalStateException("Unable to read the secrets data!", e); - } - } else { - throw new IllegalStateException("No secrets data available!"); - } - } - - private void saveSecretsData(final Secrets secrets) { - try { - final String secretsData = configObjectMapper.writeValueAsString(secrets); - saveEncryptedObject(ConfigConstants.SECRETS_DATA_FILE, secretsData); - } catch (JsonProcessingException e) { - throw new RuntimeException("Unable to convert the environment data to JSON. Aborting save...", e); - } - } - - private Environment getEnvironmentData() { - initConfigStoreService(); - - final Optional envData = configStoreService.get(ConfigConstants.ENV_DATA_FILE); + final Optional environmentData = encryptedConfigStoreService.get(ConfigConstants.ENVIRONMENT_DATA_FILE); - if (envData.isPresent()) { + if (environmentData.isPresent()) { try { - return configObjectMapper.readValue(envData.get(), Environment.class); + return configObjectMapper.readValue(environmentData.get(), CerberusEnvironmentData.class); } catch (IOException e) { throw new IllegalStateException("Unable to read the environment data!", e); } @@ -671,27 +504,15 @@ private Environment getEnvironmentData() { } } - private void saveEnvironmentData(final Environment environment) { + private void saveEnvironmentData(final CerberusEnvironmentData environmentData) { try { - final String envData = configObjectMapper.writeValueAsString(environment); - saveObject(ConfigConstants.ENV_DATA_FILE, envData); + final String environmentDataData = configObjectMapper.writeValueAsString(environmentData); + saveEncryptedObject(ConfigConstants.ENVIRONMENT_DATA_FILE, environmentDataData); } catch (JsonProcessingException e) { throw new RuntimeException("Unable to convert the environment data to JSON. Aborting save...", e); } } - private void saveObject(final String path, final String value) { - initConfigStoreService(); - - configStoreService.put(path, value); - } - - private Optional getObject(final String path) { - initConfigStoreService(); - - return configStoreService.get(path); - } - /** * List under a path as-if it were a folder */ @@ -736,60 +557,13 @@ private void initConfigStoreService() { } } - private String buildCertFilePath(final Stack stack, final String suffix) { - return "data/" + stack.getName() + "/" + stack.getName() + "-" + suffix; - } - - /** - * Removes the final '.' from the CNAME. - * - * @param cname The cname to convert - * @return The host derived from the CNAME - */ - private String cnameToHost(final String cname) { - return cname.substring(0, cname.length() - 1); - } - - public Optional getBackupInfoForRegion(String region) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - return Optional.ofNullable(environment.getRegionBackupBucketMap().get(region)); - } - } - - public Map getRegionBackupBucketMap() { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - return environment.getRegionBackupBucketMap(); - } - } - - public void storeBackupInfoForRegion(String region, String bucket, String kmsCmkId) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - environment.getRegionBackupBucketMap().put(region, new BackupRegionInfo(bucket, kmsCmkId)); - saveEnvironmentData(environment); - } - } - - public Set getBackupAdminIamPrincipals() { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - return environment.getBackupAdminIamPrincipals(); - } - } - - public void storeBackupAdminIamPrincipals(Set principals) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); - environment.setBackupAdminIamPrincipals(principals); - saveEnvironmentData(environment); - } + private String buildCertFilePath(String identityManagementCertName, String filename) { + return String.format("certificates/%s/%s", identityManagementCertName, filename); } public Optional getMetricsTopicArn() { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); + synchronized (cerberusEnvironmentDataLock) { + CerberusEnvironmentData environment = getDecryptedEnvironmentData(); if (StringUtils.isNoneBlank(environment.getMetricsTopicArn())) { return Optional.of(environment.getMetricsTopicArn()); } @@ -802,11 +576,10 @@ public Optional getMetricsTopicArn() { } private void storeMetricsTopicArn(String arn) { - synchronized (envDataLock) { - final Environment environment = getEnvironmentData(); + synchronized (cerberusEnvironmentDataLock) { + CerberusEnvironmentData environment = getDecryptedEnvironmentData(); environment.setMetricsTopicArn(arn); saveEnvironmentData(environment); } } - } diff --git a/src/main/java/com/nike/cerberus/vault/VaultAdminClientFactory.java b/src/main/java/com/nike/cerberus/vault/VaultAdminClientFactory.java deleted file mode 100644 index 82ba4426..00000000 --- a/src/main/java/com/nike/cerberus/vault/VaultAdminClientFactory.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2016 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.vault; - -import com.google.common.collect.Lists; -import com.nike.cerberus.domain.cloudformation.VaultOutputs; -import com.nike.cerberus.service.AutoScalingService; -import com.nike.cerberus.store.ConfigStore; -import com.nike.vault.client.StaticVaultUrlResolver; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.auth.TokenVaultCredentials; -import com.nike.vault.client.auth.VaultCredentials; -import com.nike.vault.client.auth.VaultCredentialsProvider; -import com.nike.vault.client.model.VaultHealthResponse; -import okhttp3.CipherSuite; -import okhttp3.ConnectionSpec; -import okhttp3.OkHttpClient; -import okhttp3.TlsVersion; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.conn.ssl.NoopHostnameVerifier; - -import javax.inject.Inject; -import java.net.Proxy; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * Handles constructing a Vault admin client that can communicate with each Vault instance directly. - */ -public class VaultAdminClientFactory { - - private static final int DEFAULT_TIMEOUT = 15; - - private static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; - - private final Proxy proxy; - - private final AutoScalingService autoScalingService; - - private final ConfigStore configStore; - - @Inject - public VaultAdminClientFactory(final Proxy proxy, - final AutoScalingService autoScalingService, - final ConfigStore configStore) { - this.proxy = proxy; - this.autoScalingService = autoScalingService; - this.configStore = configStore; - } - - /** - * Looks up the running instances from the Vault AutoScaling group and attempts to determine who the leader is - * by using the health endpoint. - * - * @return Client for leader - */ - public Optional getClientForLeader() { - VaultAdminClient leaderClient = null; - final String vaultRootToken = configStore.getVaultRootToken(); - final VaultOutputs vaultOutputs = configStore.getVaultStackOutputs(); - final List instanceDnsNames = autoScalingService.getPublicDnsForAutoScalingGroup( - vaultOutputs.getAutoscalingGroupLogicalId()); - - for (final String instanceDnsName : instanceDnsNames) { - final VaultAdminClient vaultAdminClient = getClient(vaultRootToken, instanceDnsName); - - final VaultHealthResponse healthResponse = vaultAdminClient.health(); - - if (healthResponse.isInitialized() && !healthResponse.isSealed() && !healthResponse.isStandby()) { - leaderClient = vaultAdminClient; - break; - } - } - - return Optional.ofNullable(leaderClient); - } - - /** - * Creates clients for each Vault instance in the AutoScaling group. - * - * @return List of Vault clients - */ - public List getClientsForCluster() { - final List clients = Lists.newLinkedList(); - String vaultRootToken = configStore.getVaultRootToken(); - - final VaultOutputs vaultOutputs = configStore.getVaultStackOutputs(); - final List instanceDnsNames = autoScalingService.getPublicDnsForAutoScalingGroup( - vaultOutputs.getAutoscalingGroupLogicalId()); - instanceDnsNames.forEach(instanceDnsName -> { - if (StringUtils.isNotBlank(instanceDnsName)) { - clients.add(getClient(vaultRootToken, instanceDnsName)); - } - }); - return clients; - } - - /** - * Determines if any Vault instances are present in the AutoScaling group. - * - * @return If instances running - */ - public boolean hasVaultInstances() { - final VaultOutputs vaultOutputs = configStore.getVaultStackOutputs(); - final List instanceDnsNames = autoScalingService.getPublicDnsForAutoScalingGroup( - vaultOutputs.getAutoscalingGroupLogicalId()); - return !instanceDnsNames.isEmpty(); - } - - public VaultAdminClient getClient(final String vaultRootToken, final String hostname) { - final ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2) - .cipherSuites( - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) - .build(); - - final OkHttpClient httpClient = new OkHttpClient.Builder() - .hostnameVerifier(new NoopHostnameVerifier()) - .connectionSpecs(Lists.newArrayList(spec)) - .proxy(proxy) - .connectTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) - .writeTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) - .readTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) - .build(); - - return new VaultAdminClient( - new StaticVaultUrlResolver(toVaultUrl(hostname)), - new RootCredentialsProvider(vaultRootToken), - httpClient); - } - - private String toVaultUrl(final String hostname) { - return String.format("https://%s:8200", hostname); - } - - - public static class RootCredentialsProvider implements VaultCredentialsProvider { - - private final String rootToken; - - public RootCredentialsProvider(final String rootToken) { - this.rootToken = rootToken; - } - - @Override - public VaultCredentials getCredentials() { - return new TokenVaultCredentials(rootToken); - } - } -} diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/base.yaml index 4a984610..87a0648b 100644 --- a/src/main/resources/cloudformation/base.yaml +++ b/src/main/resources/cloudformation/base.yaml @@ -49,8 +49,9 @@ Resources: AWS: - !GetAtt 'CmsIamRole.Arn' Resource: - - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /data/cms/*]] - - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /data/cms]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /certificates/*]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', cms/*]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', cms]] Sid: Allow-Bucket-Access-For-CMS Version: '2012-10-17' Type: AWS::S3::BucketPolicy diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index 4e6e0e14..cc739278 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -13,10 +13,10 @@ Outputs: Value: !GetAtt 'ApplicationLoadBalancer.DNSName' Export: Name: !Sub "${AWS::StackName}-loadBalancerDnsName" - loadBalancerLogicalId: + loadBalancerPhysicalId: Value: !Ref 'ApplicationLoadBalancer' Export: - Name: !Sub "${AWS::StackName}-loadBalancerLogicalId" + Name: !Sub "${AWS::StackName}-loadBalancerPhysicalId" Parameters: sgStackName: Description: The name of the stack containing Cerberus IAM roles and SGs stack diff --git a/src/main/resources/cloudformation/web-app-firewall.yaml b/src/main/resources/cloudformation/web-app-firewall.yaml index d9f8b8dd..dd48b824 100644 --- a/src/main/resources/cloudformation/web-app-firewall.yaml +++ b/src/main/resources/cloudformation/web-app-firewall.yaml @@ -56,7 +56,7 @@ Resources: Type: "AWS::WAFRegional::WebACLAssociation" Properties: ResourceArn: - Fn::ImportValue: !Sub "${loadBalancerStackName}-loadBalancerLogicalId" + Fn::ImportValue: !Sub "${loadBalancerStackName}-loadBalancerPhysicalId" WebACLId: !Ref 'CerberusWAFWebAcl' CerberusWafSizeConstraintRule: diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index fea347c3..bd4f86fa 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -24,7 +24,7 @@ import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.command.core.UploadCertFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; @@ -106,7 +106,7 @@ public void test_create_base() { @Test public void test_upload_cert_without_overwrite() { - String commandName = UploadCertFilesCommand.COMMAND_NAME; + String commandName = UploadCertificateFilesCommand.COMMAND_NAME; String[] userInput = {"-f", "/path/to/environment.yaml", commandName, "--stack-name", "cms"}; @@ -124,7 +124,7 @@ public void test_upload_cert_without_overwrite() { @Test public void test_upload_cert_with_overwrite() { - String commandName = UploadCertFilesCommand.COMMAND_NAME; + String commandName = UploadCertificateFilesCommand.COMMAND_NAME; String[] userInput = {"-f", "/path/to/environment.yaml", commandName, "--stack-name", "cms", "--overwrite"}; diff --git a/src/test/java/com/nike/cerberus/domain/StackTest.java b/src/test/java/com/nike/cerberus/domain/StackTest.java index 02fedc4c..f9bd3c3e 100644 --- a/src/test/java/com/nike/cerberus/domain/StackTest.java +++ b/src/test/java/com/nike/cerberus/domain/StackTest.java @@ -18,16 +18,13 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nike.cerberus.domain.environment.Environment; import com.nike.cerberus.domain.environment.Stack; import org.junit.Test; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; public class StackTest { @@ -41,17 +38,4 @@ public void test_that_stack_can_go_into_serialized_map() throws JsonProcessingEx assertEquals("{\"cms\":\"foo\"}", json); } - - @Test - public void test_that_stack_can_deserialize_from_map() throws IOException { - ObjectMapper objectMapper = new ObjectMapper(); - - Environment environment = new Environment(); - environment.getServerCertificateIdMap().put(Stack.CMS, "foo"); - String json = objectMapper.writeValueAsString(environment); - Environment actual = objectMapper.readValue(json, Environment.class); - - assertTrue(actual.getServerCertificateIdMap().size() == 1); - } - } From d5007e2f36d6af9bb86b409257c51bee063b7520 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Mon, 27 Nov 2017 17:25:19 -0800 Subject: [PATCH 43/69] Certificate Rotation working with yaml only (#104) * rotation working with yaml only * print -> log statment * PR clean up * move cert cleanup logic into command and operation * Add more comments * add proxy to client factory * Move admin client into it's own factory as requested during the PR --- ...omS3BackupOperationUserAcceptanceTest.java | 10 +- .../com/nike/cerberus/cli/CerberusRunner.java | 10 +- .../client/CerberusAdminClientFactory.java | 33 +- .../cerberus/client/HttpClientFactory.java | 161 ++++++++ .../composite/RotateCertificatesCommand.java | 4 +- ...a => DeleteOldestCertificatesCommand.java} | 12 +- ...heckCommand.java => RebootCmsCommand.java} | 21 +- .../command/core/UpdateStackCommand.java | 3 +- .../environment/CerberusEnvironmentData.java | 2 +- .../environment/CertificateInformation.java | 20 +- .../composite/CompositeOperation.java | 21 +- .../composite/CreateEnvironmentOperation.java | 7 +- .../composite/DeleteEnvironmentOperation.java | 2 +- .../PrintAllStackInformationOperation.java | 2 +- .../RotateCertificatesOperation.java | 42 ++- .../core/AddCertificateToAlbOperation.java | 59 --- .../DeleteOldestCertificatesOperation.java | 73 ++++ .../operation/core/RebootCmsOperation.java | 344 ++++++++++++++++++ ...RollingRebootWithHealthCheckOperation.java | 253 ------------- .../operation/core/UpdateStackOperation.java | 21 +- .../cerberus/service/CertificateService.java | 28 +- .../com/nike/cerberus/store/ConfigStore.java | 30 +- src/main/resources/cloudformation/base.yaml | 5 +- ...CerberusDataFromS3BackupOperationTest.java | 1 + 24 files changed, 739 insertions(+), 425 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/client/HttpClientFactory.java rename src/main/java/com/nike/cerberus/command/core/{AddCertificateToAlbCommand.java => DeleteOldestCertificatesCommand.java} (68%) rename src/main/java/com/nike/cerberus/command/core/{RollingRebootWithHealthCheckCommand.java => RebootCmsCommand.java} (62%) delete mode 100644 src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java diff --git a/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java b/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java index b8eaf7b1..e90f6719 100644 --- a/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java +++ b/src/integration-test/java/com/nike/cerberus/operation/RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest.java @@ -33,8 +33,8 @@ import java.io.IOException; -import static com.nike.cerberus.client.CerberusAdminClientFactory.DEFAULT_TIMEOUT; -import static com.nike.cerberus.client.CerberusAdminClientFactory.DEFAULT_TIMEOUT_UNIT; +import static com.nike.cerberus.client.HttpClientFactory.DEFAULT_TIMEOUT; +import static com.nike.cerberus.client.HttpClientFactory.DEFAULT_TIMEOUT_UNIT; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; @@ -45,7 +45,7 @@ public class RestoreCompleteCerberusDataFromS3BackupOperationUserAcceptanceTest { @Spy - private CerberusAdminClientFactory vaultAdminClientFactory; + private CerberusAdminClientFactory cerberusAdminClientFactory; @Mock private ConsoleService consoleService; @@ -64,7 +64,7 @@ public void before() { operation = new RestoreCerberusBackupOperation( CerberusModule.configObjectMapper(), consoleService, - vaultAdminClientFactory); + cerberusAdminClientFactory); when(command.getCerberusUrl()).thenReturn(TestUtils.getRequiredEnvVar("CERBERUS_URL", "The Cerberus API to restore against")); @@ -94,7 +94,7 @@ public void before() { CerberusModule.configObjectMapper() ); - when(vaultAdminClientFactory.createCerberusAdminClient(anyString())).thenReturn(adminClient); + when(cerberusAdminClientFactory.createCerberusAdminClient(anyString())).thenReturn(adminClient); operation.run(command); } diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 4ef24a68..a2ee037c 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -31,10 +31,10 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; -import com.nike.cerberus.command.core.AddCertificateToAlbCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; +import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; @@ -43,11 +43,12 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; +import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; import com.nike.cerberus.command.core.DeleteStackCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; import com.nike.cerberus.command.core.RestoreCerberusBackupCommand; -import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; +import com.nike.cerberus.command.core.RebootCmsCommand; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.ViewConfigCommand; @@ -180,7 +181,7 @@ private void registerAllCommands() { registerCommand(new RestoreCerberusBackupCommand()); registerCommand(new ViewConfigCommand()); registerCommand(new UpdateCmsConfigCommand()); - registerCommand(new RollingRebootWithHealthCheckCommand()); + registerCommand(new RebootCmsCommand()); registerCommand(new GenerateCertificateFilesCommand()); registerCommand(new CreateVpcCommand()); registerCommand(new CreateWafCommand()); @@ -192,7 +193,8 @@ private void registerAllCommands() { registerCommand(new CreateEnvironmentCommand()); registerCommand(new DeleteStackCommand()); registerCommand(new DeleteEnvironmentCommand()); - registerCommand(new AddCertificateToAlbCommand()); + registerCommand(new RotateCertificatesCommand()); + registerCommand(new DeleteOldestCertificatesCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java b/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java index 619c582f..a20177e5 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java +++ b/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java @@ -18,40 +18,31 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.Inject; -import com.nike.cerberus.module.CerberusModule; import com.nike.vault.client.StaticVaultUrlResolver; -import okhttp3.OkHttpClient; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Named; -import java.util.concurrent.TimeUnit; +import com.nike.vault.client.auth.DefaultVaultCredentialsProviderChain; public class CerberusAdminClientFactory { - private final Logger logger = LoggerFactory.getLogger(getClass()); - - public static final int DEFAULT_TIMEOUT = 60; - public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; - private final ObjectMapper objectMapper; + private final HttpClientFactory httpClientFactory; @Inject - public CerberusAdminClientFactory(@Named(CerberusModule.CONFIG_OBJECT_MAPPER) ObjectMapper objectMapper) { + public CerberusAdminClientFactory(ObjectMapper objectMapper, + HttpClientFactory httpClientFactory) { + this.objectMapper = objectMapper; + this.httpClientFactory = httpClientFactory; } + + /** + * Admin client for doing admin cms tasks + */ public CerberusAdminClient createCerberusAdminClient(String url) { return new CerberusAdminClient( new StaticVaultUrlResolver(url), - null, - new OkHttpClient.Builder() - .hostnameVerifier(new NoopHostnameVerifier()) - .connectTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) - .writeTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) - .readTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) - .build(), + new DefaultVaultCredentialsProviderChain(), + httpClientFactory.getGenericClient(), objectMapper ); } diff --git a/src/main/java/com/nike/cerberus/client/HttpClientFactory.java b/src/main/java/com/nike/cerberus/client/HttpClientFactory.java new file mode 100644 index 00000000..8e0d2559 --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/HttpClientFactory.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2017 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; + +import com.google.inject.Inject; +import com.nike.cerberus.ConfigConstants; +import com.nike.cerberus.domain.environment.CertificateInformation; +import com.nike.cerberus.store.ConfigStore; +import okhttp3.OkHttpClient; +import okio.Buffer; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.InputStream; +import java.net.Proxy; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class HttpClientFactory { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + public static final int DEFAULT_TIMEOUT = 15; + public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS; + + private final ConfigStore configStore; + private final Proxy proxy; + + @Inject + public HttpClientFactory(ConfigStore configStore, + Proxy proxy) { + + this.configStore = configStore; + this.proxy = proxy; + } + + + /** + * @return Generic default client with timeouts for making manual http calls + */ + public OkHttpClient getGenericClient() { + return new OkHttpClient.Builder() + .hostnameVerifier(new NoopHostnameVerifier()) + .proxy(proxy) + .connectTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) + .writeTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) + .readTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) + .build(); + } + + /** + * Downloads the CA Chains from S3 for the Cerberus certs and creates a client that can talk to the + * individual CMS instances without going through the alb, or manually having to add the chain to the trust store. + */ + public OkHttpClient getGenericClientWithCustomTruststore() { + try { + List caChains = new LinkedList<>(); + for (CertificateInformation certInfo : configStore.getCertificationInformationList()) { + String certificateName = certInfo.getCertificateName(); + caChains.add(configStore.getCertPart(certificateName, ConfigConstants.CERT_PART_CA) + .orElseThrow(() -> new RuntimeException("Failed to download ca chain"))); + } + + Buffer buffer = new Buffer(); + caChains.forEach(buffer::writeUtf8); + + X509TrustManager trustManager = trustManagerForCertificates(buffer.inputStream()); + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] { trustManager }, null); + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + return new OkHttpClient.Builder() + .hostnameVerifier(new NoopHostnameVerifier()) + .proxy(proxy) + .sslSocketFactory(sslSocketFactory, trustManager) + .connectTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) + .writeTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) + .readTimeout(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT) + .build(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Failed to create ok http client with custom trust manager from ca certs downloaded from S3"); + } + } + + /** + * https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java + * + * Returns a trust manager that trusts {@code certificates} and none other. HTTPS services whose + * certificates have not been signed by these certificates will fail with a {@code + * SSLHandshakeException}. + */ + private X509TrustManager trustManagerForCertificates(InputStream in) + throws GeneralSecurityException { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection certificates = certificateFactory.generateCertificates(in); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + + // Put the certificates a key store. + char[] password = "password".toCharArray(); // Any password will work. + KeyStore keyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = Integer.toString(index++); + keyStore.setCertificateEntry(certificateAlias, certificate); + } + + // Use it to build an X509 trust manager. + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, password); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(keyStore); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + + Arrays.toString(trustManagers)); + } + return (X509TrustManager) trustManagers[0]; + } + + /** + * https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java + */ + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + InputStream in = null; // By convention, 'null' creates an empty key store. + keyStore.load(in, password); + return keyStore; + } catch (Exception e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java index cafd7649..0c36de67 100644 --- a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java +++ b/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java @@ -31,7 +31,9 @@ public class RotateCertificatesCommand implements Command { public static final String COMMAND_NAME = "rotate-certificates"; - public static final String COMMAND_DESCRIPTION = "rotates certs"; // TODO + public static final String COMMAND_DESCRIPTION = "Rotates the certificates used by the ALB and CMS. " + + "Generates new certs optionally, uploads the certs to IAM and S3, updating the ALB to use them, " + + "generates new cms config, followed by a rolling reboot of CMS finalized by the deletion the old certificates."; @Override public String getCommandName() { diff --git a/src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java b/src/main/java/com/nike/cerberus/command/core/DeleteOldestCertificatesCommand.java similarity index 68% rename from src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java rename to src/main/java/com/nike/cerberus/command/core/DeleteOldestCertificatesCommand.java index 29180c0c..96867cf3 100644 --- a/src/main/java/com/nike/cerberus/command/core/AddCertificateToAlbCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/DeleteOldestCertificatesCommand.java @@ -19,19 +19,19 @@ import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.AddCertificateToAlbOperation; +import com.nike.cerberus.operation.core.DeleteOldestCertificatesOperation; -import static com.nike.cerberus.command.core.AddCertificateToAlbCommand.COMMAND_NAME; +import static com.nike.cerberus.command.core.DeleteOldestCertificatesCommand.COMMAND_NAME; @Parameters( commandNames = { COMMAND_NAME }, - commandDescription = "Adds a new cert to the alb" + commandDescription = "Deletes the oldest certificates from S3 and the identity management service" ) -public class AddCertificateToAlbCommand implements Command { +public class DeleteOldestCertificatesCommand implements Command { - public static final String COMMAND_NAME = "add-certificate-to-alb"; + public static final String COMMAND_NAME = "delete-oldest-certificates"; @Override public String getCommandName() { @@ -40,6 +40,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return AddCertificateToAlbOperation.class; + return DeleteOldestCertificatesOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java b/src/main/java/com/nike/cerberus/command/core/RebootCmsCommand.java similarity index 62% rename from src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java rename to src/main/java/com/nike/cerberus/command/core/RebootCmsCommand.java index 1d384a72..adb4f26d 100644 --- a/src/main/java/com/nike/cerberus/command/core/RollingRebootWithHealthCheckCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/RebootCmsCommand.java @@ -16,33 +16,24 @@ package com.nike.cerberus.command.core; -import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.nike.cerberus.command.Command; -import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.RollingRebootWithHealthCheckOperation; +import com.nike.cerberus.operation.core.RebootCmsOperation; -import static com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand.COMMAND_NAME; +import static com.nike.cerberus.command.core.RebootCmsCommand.COMMAND_NAME; /** * Command to reboot the CMS cluster. */ @Parameters( commandNames = COMMAND_NAME, - commandDescription = "Performs a safe rolling reboot on instances in the given cluster, checking that " + + commandDescription = "Performs a safe rolling reboot on instances in the cms ASG, checking that " + "the previous instance is healthy before rebooting the next one." ) -public class RollingRebootWithHealthCheckCommand implements Command { +public class RebootCmsCommand implements Command { - public static final String COMMAND_NAME = "rolling-reboot"; - - @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to reboot.") - private Stack stack = Stack.CMS; - - public Stack getStack() { - return stack; - } + public static final String COMMAND_NAME = "reboot-cms"; @Override public String getCommandName() { @@ -51,6 +42,6 @@ public String getCommandName() { @Override public Class> getOperationClass() { - return RollingRebootWithHealthCheckOperation.class; + return RebootCmsOperation.class; } } diff --git a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java index a5226bcc..e6be80e0 100644 --- a/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UpdateStackCommand.java @@ -40,10 +40,11 @@ public class UpdateStackCommand implements Command { public static final String COMMAND_NAME = "update-stack"; + public static final String STACK_NAME_LONG_ARG = "--stack-name"; public static final String OVERWRITE_TEMPLATE_LONG_ARG = "--overwrite-template"; public static final String PARAMETER_SHORT_ARG = "-P"; - @Parameter(names = {"--stack-name"}, required = true, description = "The stack name to update.", converter = StackConverter.class) + @Parameter(names = {STACK_NAME_LONG_ARG}, required = true, description = "The stack name to update.", converter = StackConverter.class) private Stack stack; @ParametersDelegate diff --git a/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java b/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java index 544886e1..5ddff789 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java +++ b/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java @@ -54,7 +54,7 @@ public void removeCertificateInformationByName(String identityManagementCertific List certificateInformationList = getCertificateData(); List certsInfoToBeDeleted = certificateInformationList.stream() .filter(certificateInformation -> - certificateInformation.getIdentityManagementCertificateName().equals(identityManagementCertificateName)) + certificateInformation.getCertificateName().equals(identityManagementCertificateName)) .collect(Collectors.toList()); certificateInformationList.removeAll(certsInfoToBeDeleted); diff --git a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java index 4e43b4ab..842a080f 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java +++ b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java @@ -24,7 +24,7 @@ public class CertificateInformation { - private String identityManagementCertificateName; + private String certificateName; private String identityManagementCertificateArn; private DateTime notBefore; private DateTime notAfter; @@ -32,12 +32,12 @@ public class CertificateInformation { private String commonName; private List subjectAlternateNames; - public String getIdentityManagementCertificateName() { - return identityManagementCertificateName; + public String getCertificateName() { + return certificateName; } - public void setIdentityManagementCertificateName(String identityManagementCertificateName) { - this.identityManagementCertificateName = identityManagementCertificateName; + public void setCertificateName(String certificateName) { + this.certificateName = certificateName; } public DateTime getNotBefore() { @@ -92,7 +92,7 @@ public void setIdentityManagementCertificateArn(String identityManagementCertifi public String toString() { DateTimeFormatter fmt = DateTimeFormat.fullDateTime(); return "CertificateInformation{" + - "identityManagementCertificateName='" + identityManagementCertificateName + '\'' + + "certificateName='" + certificateName + '\'' + ", identityManagementCertificateArn='" + identityManagementCertificateArn + '\'' + ", notBefore=" + fmt.print(notBefore) + ", notAfter=" + fmt.print(notAfter) + @@ -103,7 +103,7 @@ public String toString() { } public static final class Builder { - private String identityManagementCertificateName; + private String certificateName; private String identityManagementCertificateArn; private DateTime notBefore; private DateTime notAfter; @@ -118,8 +118,8 @@ public static Builder create() { return new Builder(); } - public Builder withIdentityManagementCertificateName(String identityManagementCertificateName) { - this.identityManagementCertificateName = identityManagementCertificateName; + public Builder certificateName(String certificateName) { + this.certificateName = certificateName; return this; } @@ -155,7 +155,7 @@ public Builder withSubjectAlternateNames(List subjectAlternateNames) { public CertificateInformation build() { CertificateInformation certificateInformation = new CertificateInformation(); - certificateInformation.setIdentityManagementCertificateName(identityManagementCertificateName); + certificateInformation.setCertificateName(certificateName); certificateInformation.setIdentityManagementCertificateArn(identityManagementCertificateArn); certificateInformation.setNotBefore(notBefore); certificateInformation.setNotAfter(notAfter); diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java index a83e895d..b456e64c 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -65,14 +65,12 @@ private Operation getOperationInstance(Command command) { */ @SuppressWarnings("unchecked") public void run(T compositeCommand) { - beforeChain(); - if (isEnvironmentConfigRequired() && environmentConfig == null) { throw new RuntimeException(String.format("The %s command requires that -f or --file must be supplied as a global option with " + "a path to a valid environment yaml", compositeCommand.getCommandName())); } - for (ChainableCommand chainableCommand : getCompositeCommandChain()) { + for (ChainableCommand chainableCommand : getCompositeCommandChain(compositeCommand)) { Command chainedCommand = chainableCommand.getCommand(); String[] additionalArgs = chainableCommand.getAdditionalArgs(); @@ -115,28 +113,15 @@ public void run(T compositeCommand) { } log.info("Finished command: {}\n", chainedCommand.getCommandName()); } - - afterChain(); - } - - /** - * A method that can be overridden that is run before the chain of commands - */ - private void beforeChain() { - } - - /** - * A method that can be overridden that is run after the chain of commands - */ - private void afterChain() { } /** * Implement this method to define the ordered list of chained commands that will get executed * * @return An ordered list of ChainableCommand's + * @param compositeCommand */ - protected abstract List getCompositeCommandChain(); + protected abstract List getCompositeCommandChain(T compositeCommand); /** * If you command doesn't require that the environment yaml be supplied, you can override this to false. diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java index 0ebe98b0..352e54e2 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java @@ -31,6 +31,7 @@ import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import java.util.List; @@ -41,9 +42,10 @@ public class CreateEnvironmentOperation extends CompositeOperation getCompositeCommandChain() { + protected List getCompositeCommandChain(CreateEnvironmentCommand compositeCommand) { List list = Lists.newArrayList( // Step 1 Create the Base Cloud Formation Stack that creates S3 Buckets, Iam Roles and KMS keys needed for config new ChainableCommand(new CreateBaseCommand()), @@ -54,6 +56,9 @@ protected List getCompositeCommandChain() { // Step 3 Create the Security Group Cloud Formation Stack new ChainableCommand(new CreateSecurityGroupsCommand()), + // Step 3.5 Add the vpc whitelist CIDRs + new ChainableCommand(new WhitelistCidrForVpcAccessCommand()), + // Step 4 Create the RDS Database Cloud Formation Stack new ChainableCommand(new CreateDatabaseCommand()) ); diff --git a/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java index c04239a4..ac1ba76b 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java @@ -27,7 +27,7 @@ public DeleteEnvironmentOperation(ConsoleService consoleService, } @Override - protected List getCompositeCommandChain() { + protected List getCompositeCommandChain(DeleteEnvironmentCommand compositeCommand) { List chainableCommandList = new LinkedList<>(); ImmutableList.of( diff --git a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java index db64b52a..318fe73c 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/PrintAllStackInformationOperation.java @@ -29,7 +29,7 @@ public class PrintAllStackInformationOperation extends CompositeOperation { @Override - protected List getCompositeCommandChain() { + protected List getCompositeCommandChain(PrintAllStackInformationCommand compositeCommand) { List commandList = new LinkedList<>(); for (Stack stack : Stack.ALL_STACKS) { diff --git a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java index 945e04d2..bd9e022f 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java @@ -18,29 +18,38 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; -import com.nike.cerberus.command.cms.CreateCmsConfigCommand; -import com.nike.cerberus.command.core.AddCertificateToAlbCommand; +import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; +import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; -import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; +import com.nike.cerberus.command.core.RebootCmsCommand; +import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.service.CloudFormationService; import java.util.LinkedList; import java.util.List; +/** + * Operation for the certificate rotation command + */ public class RotateCertificatesOperation extends CompositeOperation { private final CloudFormationService cloudFormationService; + private final EnvironmentMetadata environmentMetadata; @Inject - public RotateCertificatesOperation(CloudFormationService cloudFormationService) { + public RotateCertificatesOperation(CloudFormationService cloudFormationService, + EnvironmentMetadata environmentMetadata) { + this.cloudFormationService = cloudFormationService; + this.environmentMetadata = environmentMetadata; } @Override - protected List getCompositeCommandChain() { + protected List getCompositeCommandChain(RotateCertificatesCommand compositeCommand) { List commandList = new LinkedList<>(); if (environmentConfig.isGenerateKeysAndCerts()) { @@ -50,31 +59,34 @@ protected List getCompositeCommandChain() { commandList.addAll(Lists.newArrayList( // Add the cert and key files to S3 ChainableCommand.Builder.create().withCommand(new UploadCertificateFilesCommand()).build(), - // Add the new cert to the ALB - ChainableCommand.Builder.create().withCommand(new AddCertificateToAlbCommand()).build(), + // Update the Load Balancer stack to use the new cert + ChainableCommand.Builder.create().withCommand(new UpdateStackCommand()) + .withAdditionalArg(UpdateStackCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(Stack.LOAD_BALANCER.getName()) + .build(), // Generate new CMS config that points to the new cert - ChainableCommand.Builder.create().withCommand(new CreateCmsConfigCommand()).build(), + ChainableCommand.Builder.create().withCommand(new UpdateCmsConfigCommand()).build(), // Do a rolling reboot of the management service - ChainableCommand.Builder.create().withCommand(new RollingRebootWithHealthCheckCommand()).build() - // Delete the old cert from the ALB and from the Identity Management service and S3 - - // Revoke the Cert? todo + ChainableCommand.Builder.create().withCommand(new RebootCmsCommand()).build(), + // Delete all certs except the latest (there should just be the one) + ChainableCommand.Builder.create().withCommand(new DeleteOldestCertificatesCommand()).build() )); - return commandList; } @Override public boolean isRunnable(RotateCertificatesCommand command) { boolean isRunnable = true; + String environmentName = environmentMetadata.getName(); - if (! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getName())) { + if (! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { log.error("The load-balancer stack must be present in order to rotate certificates"); isRunnable = false; } - if (! cloudFormationService.isStackPresent(Stack.CMS.getName())) { + if (! cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName))) { log.error("The cms stack must be present to rotate certificates"); + isRunnable = false; } return isRunnable; diff --git a/src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java b/src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java deleted file mode 100644 index 1eaedf32..00000000 --- a/src/main/java/com/nike/cerberus/operation/core/AddCertificateToAlbOperation.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2017 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.services.elasticloadbalancingv2.AmazonElasticLoadBalancing; -import com.amazonaws.services.elasticloadbalancingv2.model.Certificate; -import com.amazonaws.services.elasticloadbalancingv2.model.DescribeListenersRequest; -import com.amazonaws.services.elasticloadbalancingv2.model.DescribeListenersResult; -import com.amazonaws.services.elasticloadbalancingv2.model.RemoveListenerCertificatesRequest; -import com.google.inject.Inject; -import com.nike.cerberus.command.core.AddCertificateToAlbCommand; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.IdentityManagementService; -import com.nike.cerberus.store.ConfigStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AddCertificateToAlbOperation implements Operation { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private final AmazonElasticLoadBalancing amazonElasticLoadBalancing; - private final IdentityManagementService identityManagementService; - private final ConfigStore configStore; - - @Inject - public AddCertificateToAlbOperation(AmazonElasticLoadBalancing amazonElasticLoadBalancing, - IdentityManagementService identityManagementService, - ConfigStore configStore) { - - this.amazonElasticLoadBalancing = amazonElasticLoadBalancing; - this.identityManagementService = identityManagementService; - this.configStore = configStore; - } - - @Override - public void run(AddCertificateToAlbCommand command) { - throw new RuntimeException("Not implemented yet"); - } - - @Override - public boolean isRunnable(AddCertificateToAlbCommand command) { - return true; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java new file mode 100644 index 00000000..114480a6 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 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.google.inject.Inject; +import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; +import com.nike.cerberus.domain.environment.CertificateInformation; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CertificateService; +import com.nike.cerberus.store.ConfigStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Operation for the Delete Oldest Certificates Command + * + * Deletes the oldest certificates from S3 and the identity management service. + */ +public class DeleteOldestCertificatesOperation implements Operation { + + protected final Logger log = LoggerFactory.getLogger(getClass()); + + private final ConfigStore configStore; + private final CertificateService certificateService; + + @Inject + public DeleteOldestCertificatesOperation(ConfigStore configStore, + CertificateService certificateService) { + + this.configStore = configStore; + this.certificateService = certificateService; + } + + @Override + public void run(DeleteOldestCertificatesCommand command) { + List certificateInformationList = configStore.getCertificationInformationList(); + int indexBeforeLast = certificateInformationList.size() - 1; + certificateInformationList.subList(0, indexBeforeLast).forEach(certificateInformation -> { + certificateService.deleteCertificate(certificateInformation.getCertificateName()); + }); + } + + @Override + public boolean isRunnable(DeleteOldestCertificatesCommand command) { + List certificateInformationList = configStore.getCertificationInformationList(); + + boolean isRunnable = true; + + if (certificateInformationList.size() < 2) { + log.error("The certificate list did not have at least 2 certs, " + + "cannot delete oldest certs, aborting..."); + isRunnable = false; + } + + return isRunnable; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java b/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java new file mode 100644 index 00000000..9eba1fd4 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2017 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.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.model.*; +import com.github.tomaslanger.chalk.Chalk; +import com.google.inject.Inject; +import com.nike.cerberus.client.HttpClientFactory; +import com.nike.cerberus.command.core.RebootCmsCommand; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.AutoScalingService; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.Ec2Service; +import com.nike.cerberus.store.ConfigStore; +import com.nike.vault.client.http.HttpStatus; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.net.util.SubnetUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static com.nike.cerberus.service.CloudFormationService.MIN_INSTANCES_STACK_PARAMETER_KEY; +import static com.nike.cerberus.service.Ec2Service.EC2_ASG_GROUP_NAME_TAG_KEY; +import static com.nike.cerberus.service.Ec2Service.INSTANCE_STATE_FILTER_NAME; +import static com.nike.cerberus.service.Ec2Service.INSTANCE_STATE_RUNNING_FILTER_VALUE; + +/** + * Reboots all EC2 instances in the given cluster. + */ +public class RebootCmsOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final static String CMS_HEALTH_CHECK_URL = "https://%s:%s/healthcheck"; + + private final static Integer CMS_HEALTH_CHECK_PORT = 8443; + + private final static int NUM_SECS_BETWEEN_HEALTH_CHECKS = 5; + + private final static int EXPECTED_NUM_SUCCESSES_AFTER_REBOOT = 10; + + private final static int EXPECTED_NUM_FAILURES_AFTER_REBOOT = 3; + + private final static int EXPECTED_NUM_SUCCESSES_BEFORE_REBOOT = 1; + + private final static int HEALTH_CHECK_FAILED_CODE = -1; + + private final ConfigStore configStore; + + private final CloudFormationService cloudFormationService; + + private final Ec2Service ec2Service; + + private final AutoScalingService autoScalingService; + + private final EnvironmentMetadata environmentMetadata; + + private final AmazonEC2 ec2Client; + + private final HttpClientFactory httpClientFactory; + + @Inject + public RebootCmsOperation(ConfigStore configStore, + CloudFormationService cloudFormationService, + Ec2Service ec2Service, + AutoScalingService autoScalingService, + EnvironmentMetadata environmentMetadata, + AmazonEC2 ec2Client, + HttpClientFactory httpClientFactory) { + + this.configStore = configStore; + this.cloudFormationService = cloudFormationService; + this.ec2Service = ec2Service; + this.autoScalingService = autoScalingService; + this.environmentMetadata = environmentMetadata; + this.ec2Client = ec2Client; + this.httpClientFactory = httpClientFactory; + } + + @Override + public void run(final RebootCmsCommand command) { + log.warn(Chalk.on( + "If this command fails: the minimum instance size may need to be increased and an EC2 instance" + + " may need to be set to 'in-service' state on the auto scaling group").yellow().toString()); + + log.warn(Chalk.on("This command will lookup the current public ip of the system it is running on temporary add it to the " + + "vpc whitelist security group if it is not already on the list. If this command fails you may need to " + + "ensure that the white list security group get cleaned via the whitelist-cidr-for-vpc-access command").red().toString()); + + // white list the current public ip if needed + Optional tempCidrToWhitelist = getCidrIfWhitelistNeeded(); + tempCidrToWhitelist.ifPresent(this::whitelistCurrentIpCidr); + + try { + final Stack stack = Stack.CMS; + final String stackId = stack.getFullName(environmentMetadata.getName()); + final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); + + final Map stackParameters = cloudFormationService.getStackParameters(stackId); + final int minInstances = Integer.parseInt(stackParameters.get(MIN_INSTANCES_STACK_PARAMETER_KEY)); + + final String autoScalingGroupId = stackOutputs.get(CloudFormationService.AUTO_SCALING_GROUP_LOGICAL_ID_OUTPUT_KEY); + log.debug("Found auto scaling group id for stack: {}", stackId); + + final Filter isRunningFilter = new Filter(INSTANCE_STATE_FILTER_NAME).withValues(INSTANCE_STATE_RUNNING_FILTER_VALUE); + final List instances = ec2Service.getInstancesByTag(EC2_ASG_GROUP_NAME_TAG_KEY, autoScalingGroupId, isRunningFilter); + log.debug("Found {} instances by tag: '{}:{}'", instances.size(), EC2_ASG_GROUP_NAME_TAG_KEY, autoScalingGroupId); + + log.info("Temporarily decreasing min instances for ASG: {}", autoScalingGroupId); + autoScalingService.updateMinInstancesForAutoScalingGroup(autoScalingGroupId, minInstances - 1); + + instances.forEach(instance -> rebootInstance(autoScalingGroupId, instance)); + + log.info("Increasing min instances for ASG: {}", autoScalingGroupId); + autoScalingService.updateMinInstancesForAutoScalingGroup(autoScalingGroupId, minInstances); + } catch (Throwable e) { + throw new RuntimeException("failed to do rolling reboot, will attempt to remove current ip from whitelist if needed", e); + } finally { + // if we whitelisted the current ip then remove it since we are done + tempCidrToWhitelist.ifPresent(this::removeCurrentIpCidrFromWhitelist); + } + } + + /** + * Reboot an instance and make sure it comes back healthy + */ + private void rebootInstance(String autoScalingGroupId, Instance instance) { + + final String healthCheckUrl = String.format(CMS_HEALTH_CHECK_URL, instance.getPublicIpAddress(), CMS_HEALTH_CHECK_PORT); + + log.info("Checking that instance health check is reachable..."); + waitForHealthCheckStatusCode(healthCheckUrl, HttpStatus.OK, EXPECTED_NUM_SUCCESSES_BEFORE_REBOOT); + + final String instanceId = instance.getInstanceId(); + log.info("Setting instance state to standby: {}", instanceId); + autoScalingService.setInstanceStateToStandby(autoScalingGroupId, instanceId); + + log.info("Rebooting instance: {}", instanceId); + ec2Service.rebootEc2Instance(instanceId); + + // wait for health check fail to confirm box reboot + log.info("Waiting for health check failure to confirm reboot..."); + waitForHealthCheckStatusCode(healthCheckUrl, HEALTH_CHECK_FAILED_CODE, EXPECTED_NUM_FAILURES_AFTER_REBOOT); + + log.info("Waiting for health check to pass again to confirm instance is healthy..."); + waitForHealthCheckStatusCode(healthCheckUrl, HttpStatus.OK, EXPECTED_NUM_SUCCESSES_AFTER_REBOOT); + + log.info("Setting instance state to in-service: {}", instanceId); + autoScalingService.setInstanceStateToInService(autoScalingGroupId, instanceId); + } + + /** + * Poll the health check 'n' times, looking for the given response + * + * @param healthCheckUrl - The health check URL + * @param numConsecutiveResponsesExpected - The number of times to poll health check + */ + private void waitForHealthCheckStatusCode(final String healthCheckUrl, + final long expectedStatusCode, + final int numConsecutiveResponsesExpected) { + + int responseCode; + int consecutiveResponses = 0; + + OkHttpClient healthCheckClient = httpClientFactory.getGenericClientWithCustomTruststore(); + + while (consecutiveResponses < numConsecutiveResponsesExpected) { + + responseCode = executeHealthCheck(healthCheckUrl, healthCheckClient); + + if (responseCode == expectedStatusCode) { + consecutiveResponses++; + } else if (consecutiveResponses > 0) { + final String message = Chalk.on("Instance health check did not repeat response code ({}), {} times").red().bold().toString(); + log.debug(message, expectedStatusCode, numConsecutiveResponsesExpected); + consecutiveResponses = 0; + } + + try { + TimeUnit.SECONDS.sleep(NUM_SECS_BETWEEN_HEALTH_CHECKS); + } catch (InterruptedException ie) { + log.error(Chalk.on("Timeout between health checks has been interrupted").red().bold().toString()); + return; + } + } + } + + /** + * Execute the given health check + * + * @param healthCheckUrl - Name of that EC2 instance belongs to + * @return - Response code of the health check + */ + private int executeHealthCheck(String healthCheckUrl, OkHttpClient okHttpClient) { + + final Request request = new Request.Builder() + .url(healthCheckUrl) + .get() + .build(); + + log.debug("Performing to the following request: {}", request); + + final Call healthCheckCall = okHttpClient.newCall(request); + + try (final Response response = healthCheckCall.execute()) { + log.debug("Health check returned status: {}, URL: {}", response.code(), healthCheckUrl); + return response.code(); + } catch (Exception e) { + if (StringUtils.contains(e.getMessage(), "PKIX path building failed")) { + throw new RuntimeException("Failed to validate certificate, this shouldn't happen because we use a" + + " custom trust store for this call using the ca chain you uploaded, so you ca chain might be malformed"); + } + final String message = Chalk.on("Health check failed, Cause: \"{}\", URL: {}").red().toString(); + log.debug(message, e.getMessage(), healthCheckUrl); + } + + return HEALTH_CHECK_FAILED_CODE; + } + + private String getCurrentPublicIpAddress() { + String whatIsMyIp = "http://checkip.amazonaws.com"; + try (Response response = httpClientFactory.getGenericClient() + .newCall(new Request.Builder().url(whatIsMyIp).get().build()).execute()) { + return StringUtils.trim(response.body().string()); + } catch (IOException e) { + throw new RuntimeException("Failed to lookup current ip", e); + } + } + + @Override + public boolean isRunnable(final RebootCmsCommand command) { + final Stack stack = Stack.CMS; + final String stackId = stack.getFullName(environmentMetadata.getName()); + final Map stackParameters = cloudFormationService.getStackParameters(stackId); + + if (!stackParameters.containsKey(MIN_INSTANCES_STACK_PARAMETER_KEY)) { + log.error("Could not find parameter 'minInstances' on stack: {}", stackId); + return false; + } else { + return true; + } + } + + /** + * Looks up the current ip converts it to a CIDR and checks the vpc whitelist sg created by the whitelist vpc for ingress command + * to see if the current ip is already whitelisted to have access to the CMS Health Check Port. + * + * @return an optional of a the current public ip in CIDR form if it needs to be whitelisted. + */ + private Optional getCidrIfWhitelistNeeded() { + String currentIp = getCurrentPublicIpAddress(); + + String vpcIngressWhitelistSecurityGroup = configStore.getSecurityGroupStackOutputs().getWhitelistIngressSgId(); + + DescribeSecurityGroupsResult securityGroupsResult = ec2Client.describeSecurityGroups( + new DescribeSecurityGroupsRequest().withGroupIds(vpcIngressWhitelistSecurityGroup)); + + for (SecurityGroup securityGroup : securityGroupsResult.getSecurityGroups()) { + for (IpPermission ipPermission : securityGroup.getIpPermissions()) { + for (IpRange ipRange : ipPermission.getIpv4Ranges()) { + SubnetUtils subnetUtils = new SubnetUtils(ipRange.getCidrIp()); + if (isIpInCidrRange(currentIp, subnetUtils) && + Objects.equals(ipPermission.getFromPort(), CMS_HEALTH_CHECK_PORT) && + Objects.equals(ipPermission.getToPort(), CMS_HEALTH_CHECK_PORT)) { + + log.info("detected that ip: '{}' already has permission to ingress on port: {}, no need to whitelist", + currentIp, CMS_HEALTH_CHECK_PORT); + return Optional.empty(); + } + } + } + } + return Optional.of(currentIp + "/32"); + } + + private boolean isIpInCidrRange(String ip, SubnetUtils subnetUtils) { + String cidr = subnetUtils.getInfo().getCidrSignature(); + if (cidr.endsWith("/32") && StringUtils.removeEnd(cidr, "/32").equals(ip)) { + return true; + } + + return subnetUtils.getInfo().isInRange(ip); + } + + private void whitelistCurrentIpCidr(String cidr) { + log.info("Adding new ip permission to the vpc ingress sg, IP: '{}' From and To Port: {}", cidr, CMS_HEALTH_CHECK_PORT); + + String vpcIngressWhitelistSecurityGroup = configStore.getSecurityGroupStackOutputs().getWhitelistIngressSgId(); + AuthorizeSecurityGroupIngressRequest ingressRequest = new AuthorizeSecurityGroupIngressRequest() + .withGroupId(vpcIngressWhitelistSecurityGroup) + .withIpPermissions(getIpPermissionForCidr(cidr)); + ec2Client.authorizeSecurityGroupIngress(ingressRequest); + + try { + log.info("Sleeping for 1 minute to let sg changes be eventually consistent"); + Thread.sleep(TimeUnit.MINUTES.toMillis(1)); + } catch (InterruptedException e) { + log.error("Failed to wait for sg to be eventually consistent"); + } + } + + private void removeCurrentIpCidrFromWhitelist(String cidr) { + log.info("Removing new ip permission to the vpc ingress sg, IP: '{}' From and To Port: {}", cidr, CMS_HEALTH_CHECK_PORT); + + String vpcIngressWhitelistSecurityGroup = configStore.getSecurityGroupStackOutputs().getWhitelistIngressSgId(); + RevokeSecurityGroupIngressRequest revokeIngressRequest = new RevokeSecurityGroupIngressRequest() + .withGroupId(vpcIngressWhitelistSecurityGroup) + .withIpPermissions(getIpPermissionForCidr(cidr)); + ec2Client.revokeSecurityGroupIngress(revokeIngressRequest); + } + + private IpPermission getIpPermissionForCidr(String cidr) { + return new IpPermission() + .withIpv4Ranges(new IpRange().withCidrIp(cidr)) + .withIpProtocol("tcp") + .withFromPort(CMS_HEALTH_CHECK_PORT) + .withToPort(CMS_HEALTH_CHECK_PORT); + } + +} diff --git a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java b/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java deleted file mode 100644 index d8feabd2..00000000 --- a/src/main/java/com/nike/cerberus/operation/core/RollingRebootWithHealthCheckOperation.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2017 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.services.ec2.model.Filter; -import com.amazonaws.services.ec2.model.Instance; -import com.github.tomaslanger.chalk.Chalk; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Inject; -import com.nike.cerberus.command.core.RollingRebootWithHealthCheckCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.environment.Stack; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.AutoScalingService; -import com.nike.cerberus.service.CloudFormationService; -import com.nike.cerberus.service.Ec2Service; -import com.nike.cerberus.store.ConfigStore; -import com.nike.vault.client.http.HttpStatus; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.Proxy; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static com.nike.cerberus.service.CloudFormationService.MIN_INSTANCES_STACK_PARAMETER_KEY; -import static com.nike.cerberus.service.Ec2Service.EC2_ASG_GROUP_NAME_TAG_KEY; -import static com.nike.cerberus.service.Ec2Service.INSTANCE_STATE_FILTER_NAME; -import static com.nike.cerberus.service.Ec2Service.INSTANCE_STATE_RUNNING_FILTER_VALUE; - -/** - * Reboots all EC2 instances in the given cluster. - */ -public class RollingRebootWithHealthCheckOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final static ImmutableMap HEALTH_CHECK_MAP = ImmutableMap.of( - Stack.CMS.getName(), "http://%s:8080/healthcheck" - ); - - private final static int DEFAULT_HTTP_TIMEOUT = 15; - - private final static TimeUnit DEFAULT_HTTP_TIMEOUT_UNIT = TimeUnit.SECONDS; - - private final static int NUM_SECS_BETWEEN_HEALTH_CHECKS = 5; - - private final static int EXPECTED_NUM_SUCCESSES_AFTER_REBOOT = 10; - - private final static int EXPECTED_NUM_FAILURES_AFTER_REBOOT = 3; - - private final static int EXPECTED_NUM_SUCCESSES_BEFORE_REBOOT = 1; - - private final static int HEALTH_CHECK_FAILED_CODE = -1; - - private final ConfigStore configStore; - - private final CloudFormationService cloudFormationService; - - private final Ec2Service ec2Service; - - private final AutoScalingService autoScalingService; - - private final Proxy proxy; - - private final EnvironmentMetadata environmentMetadata; - - @Inject - public RollingRebootWithHealthCheckOperation(final ConfigStore configStore, - final CloudFormationService cloudFormationService, - final Ec2Service ec2Service, - final AutoScalingService autoScalingService, - final Proxy proxy, EnvironmentMetadata environmentMetadata) { - this.configStore = configStore; - this.cloudFormationService = cloudFormationService; - this.ec2Service = ec2Service; - this.autoScalingService = autoScalingService; - this.proxy = proxy; - this.environmentMetadata = environmentMetadata; - } - - @Override - public void run(final RollingRebootWithHealthCheckCommand command) { - - logger.warn(Chalk.on( - "If this command fails: the minimum instance size may need to be increased and an EC2 instance" + - " may need to be set to 'in-service' state on the auto scaling group").yellow().toString()); - - final Stack stack = command.getStack(); - final String stackId = stack.getFullName(environmentMetadata.getName()); - final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); - - final Map stackParameters = cloudFormationService.getStackParameters(stackId); - final int minInstances = Integer.parseInt(stackParameters.get(MIN_INSTANCES_STACK_PARAMETER_KEY)); - - final String autoScalingGroupId = stackOutputs.get(CloudFormationService.AUTO_SCALING_GROUP_LOGICAL_ID_OUTPUT_KEY); - logger.debug("Found auto scaling group id for stack: {}", stackId); - - final Filter isRunningFilter = new Filter(INSTANCE_STATE_FILTER_NAME).withValues(INSTANCE_STATE_RUNNING_FILTER_VALUE); - final List instances = ec2Service.getInstancesByTag(EC2_ASG_GROUP_NAME_TAG_KEY, autoScalingGroupId, isRunningFilter); - logger.debug("Found {} instances by tag: '{}:{}'", instances.size(), EC2_ASG_GROUP_NAME_TAG_KEY, autoScalingGroupId); - - logger.info("Temporarily decreasing min instances for ASG: {}", autoScalingGroupId); - autoScalingService.updateMinInstancesForAutoScalingGroup(autoScalingGroupId, minInstances - 1); - - instances.forEach(instance -> { - rebootInstance(stack, autoScalingGroupId, instance); - }); - - logger.info("Increasing min instances for ASG: {}", autoScalingGroupId); - autoScalingService.updateMinInstancesForAutoScalingGroup(autoScalingGroupId, minInstances); - } - - /** - * Reboot an instance and make sure it comes back healthy - */ - private void rebootInstance(Stack stack, String autoScalingGroupId, Instance instance) { - - final String healthCheckUrlTmpl = HEALTH_CHECK_MAP.get(stack.getName()); - final String healthCheckUrl = String.format(healthCheckUrlTmpl, instance.getPublicDnsName()); - - logger.info("Checking that instance health check is reachable..."); - waitForHealthCheckStatusCode(healthCheckUrl, HttpStatus.OK, EXPECTED_NUM_SUCCESSES_BEFORE_REBOOT); - - final String instanceId = instance.getInstanceId(); - logger.info("Setting instance state to standby: {}", instanceId); - autoScalingService.setInstanceStateToStandby(autoScalingGroupId, instanceId); - - logger.info("Rebooting instance: {}", instanceId); - ec2Service.rebootEc2Instance(instanceId); - - // wait for health check fail to confirm box reboot - logger.info("Waiting for health check failure to confirm reboot..."); - waitForHealthCheckStatusCode(healthCheckUrl, HEALTH_CHECK_FAILED_CODE, EXPECTED_NUM_FAILURES_AFTER_REBOOT); - - // wait for health check pass to confirm instance is healthy after reboot - logger.warn(Chalk.on( - "If a proxy is required to talk to the EC2 instance, then make sure it is set up." + - " Otherwise this command will never succeed.").yellow().toString()); - logger.info("Waiting for health check to pass again to confirm instance is healthy..."); - waitForHealthCheckStatusCode(healthCheckUrl, HttpStatus.OK, EXPECTED_NUM_SUCCESSES_AFTER_REBOOT); - - logger.info("Setting instance state to in-service: {}", instanceId); - autoScalingService.setInstanceStateToInService(autoScalingGroupId, instanceId); - } - - /** - * Poll the health check 'n' times, looking for the given response - * - * @param healthCheckUrl - The health check URL - * @param numConsecutiveResponsesExpected - The number of times to poll health check - */ - private void waitForHealthCheckStatusCode(final String healthCheckUrl, - final long expectedStatusCode, - final int numConsecutiveResponsesExpected) { - - int responseCode; - int consecutiveResponses = 0; - while (consecutiveResponses < numConsecutiveResponsesExpected) { - - responseCode = executeHealthCheck(healthCheckUrl); - - if (responseCode == expectedStatusCode) { - consecutiveResponses++; - } else if (consecutiveResponses > 0) { - final String message = Chalk.on("Instance health check did not repeat response code ({}), {} times").red().bold().toString(); - logger.debug(message, expectedStatusCode, numConsecutiveResponsesExpected); - consecutiveResponses = 0; - } - - try { - TimeUnit.SECONDS.sleep(NUM_SECS_BETWEEN_HEALTH_CHECKS); - } catch (InterruptedException ie) { - logger.error(Chalk.on("Timeout between health checks has been interrupted").red().bold().toString()); - return; - } - } - } - - /** - * Execute the given health check - * - * @param healthCheckUrl - Name of that EC2 instance belongs to - * @return - Response code of the health check - */ - private int executeHealthCheck(final String healthCheckUrl) { - - final OkHttpClient okHttpClient = new OkHttpClient.Builder() - .hostnameVerifier(new NoopHostnameVerifier()) - .proxy(proxy) - .connectTimeout(DEFAULT_HTTP_TIMEOUT, DEFAULT_HTTP_TIMEOUT_UNIT) - .writeTimeout(DEFAULT_HTTP_TIMEOUT, DEFAULT_HTTP_TIMEOUT_UNIT) - .readTimeout(DEFAULT_HTTP_TIMEOUT, DEFAULT_HTTP_TIMEOUT_UNIT) - .build(); - - final Request requestBuilder = new Request.Builder() - .url(healthCheckUrl) - .get() - .build(); - - final Call healthCheckCall = okHttpClient.newCall(requestBuilder); - - try (final Response response = healthCheckCall.execute()) { - logger.debug("Health check returned status: {}, URL: {}", response.code(), healthCheckUrl); - return response.code(); - } catch (IOException ioe) { - final String message = Chalk.on("Health check failed, Cause: \"{}\", URL: {}").red().toString(); - logger.debug(message, ioe.getMessage(), healthCheckUrl); - } - - return HEALTH_CHECK_FAILED_CODE; - } - - @Override - public boolean isRunnable(final RollingRebootWithHealthCheckCommand command) { - - final Stack stack = command.getStack(); - final String stackNameStr = stack.getName(); - final String stackId = stack.getFullName(environmentMetadata.getName()); - final Map stackParameters = cloudFormationService.getStackParameters(stackId); - - if (!HEALTH_CHECK_MAP.containsKey(stackNameStr)) { - logger.error("Cannot reboot cluster: {}. Allowed stacks: {}", stack, HEALTH_CHECK_MAP.keySet()); - return false; - } else if (!stackParameters.containsKey(MIN_INSTANCES_STACK_PARAMETER_KEY)) { - logger.error("Could not find parameter 'minInstances' on stack: {}", stackId); - return false; - } else { - return true; - } - } -} diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 7cfe5492..37089426 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -23,6 +23,7 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; +import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,12 +31,6 @@ import javax.inject.Inject; import java.util.Map; -import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_COMPLETE; -import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_COMPLETE_CLEANUP_IN_PROGRESS; -import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_COMPLETE; -import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS; -import static com.amazonaws.services.cloudformation.model.StackStatus.UPDATE_ROLLBACK_FAILED; - /** * Operation for updating stacks. */ @@ -47,16 +42,20 @@ public class UpdateStackOperation implements Operation { private final CloudFormationService cloudFormationService; private final Ec2UserDataService ec2UserDataService; private final EnvironmentMetadata environmentMetadata; + private final ConfigStore configStore; @Inject public UpdateStackOperation(final CloudFormationService cloudFormationService, final Ec2UserDataService ec2UserDataService, - final EnvironmentMetadata environmentMetadata) { + final EnvironmentMetadata environmentMetadata, + final ConfigStore configStore) { + this.cloudFormationService = cloudFormationService; this.ec2UserDataService = ec2UserDataService; this.environmentMetadata = environmentMetadata; + this.configStore = configStore; } @Override @@ -75,6 +74,9 @@ public void run(final UpdateStackCommand command) { } else if (Stack.DATABASE.equals(command.getStack())) { // TODO: implement storing password if it was changed //configStore.storeCmsDatabasePassword(databasePassword); + } else if (Stack.LOAD_BALANCER.equals(command.getStack())) { + parameters.put("sslCertificateArn", configStore.getCertificationInformationList() + .getLast().getIdentityManagementCertificateArn()); } parameters.putAll(command.getDynamicParameters()); @@ -105,6 +107,11 @@ public boolean isRunnable(final UpdateStackCommand command) { isRunnable = false; } + if (command.getStack().equals(Stack.LOAD_BALANCER) && configStore.getCertificationInformationList().isEmpty()) { + logger.error("Updating the load balancer requires that a cert has been uploaded by the upload-certificate-files command"); + isRunnable = false; + } + return isRunnable; } } \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index 39e87aac..3c909868 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -16,6 +16,7 @@ package com.nike.cerberus.service; +import com.amazonaws.AmazonServiceException; import com.amazonaws.services.route53.AmazonRoute53; import com.amazonaws.services.route53.model.Change; import com.amazonaws.services.route53.model.ChangeAction; @@ -513,6 +514,13 @@ public void uploadCertFiles(File certDir) { certContents, caContents, keyContents); log.info("Identity Management Cert Name: {}", identityManagementCertificateName); + log.info("Sleeping to let iam cert become eventually consistent"); + try { + Thread.sleep(TimeUnit.MINUTES.toMillis(1)); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to wait for iam cert to become eventually consistent"); + } + log.info("Uploading certificate parts to the configuration bucket."); X509Certificate certificate; try { @@ -529,8 +537,8 @@ public void uploadCertFiles(File certDir) { } CertificateInformation certificateInformation = CertificateInformation.Builder.create() - .withIdentityManagementCertificateName(identityManagementCertificateName) - .withIdentityManagementCertificateArn(identityManagementService.getServerCertificateArn(identityManagementCertificateName).get()) + .certificateName(identityManagementCertificateName) + .withIdentityManagementCertificateArn(identityManagementService.getServerCertificateArn(certificateName).get()) .withCommonName(StringUtils.removeStart(certificate.getSubjectX500Principal().getName(), "CN=")) .withSubjectAlternateNames(sans) .withNotBefore(new DateTime(certificate.getNotBefore(), DateTimeZone.UTC)) @@ -584,4 +592,20 @@ private String getFileContents(final File certDir, final String filename) { private String getPath() { return "/cerberus/" + environmentMetadata.getName() + "/"; } + + /** + * Deletes a certificate by name from S3 and from the identity management service and from the environment config + * + * @param certificateName the certificate to delete + */ + public void deleteCertificate(String certificateName) { + try { + identityManagementService.deleteServerCertificate(certificateName); + } catch (AmazonServiceException e) { + log.error("Failed to delete the certificate: {} from the identity management service," + + " you may need to manually delete. MSG: {}", certificateName, e.getMessage()); + } + + configStore.deleteCertificate(certificateName); + } } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 90d2eed4..1b8f9f71 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -167,7 +167,7 @@ public void storeCert(CertificateInformation certificateInformation, String pkcs8KeyContents, String pubKeyContents) { - String name = certificateInformation.getIdentityManagementCertificateName(); + String name = certificateInformation.getCertificateName(); saveEncryptedObject(buildCertFilePath(name, CERT_PART_CA), caContents); saveEncryptedObject(buildCertFilePath(name, CERT_PART_CERT), certContents); saveEncryptedObject(buildCertFilePath(name, CERT_PART_KEY), keyContents); @@ -186,6 +186,32 @@ public LinkedList getCertificationInformationList() { return getDecryptedEnvironmentData().getCertificateData(); } + /** + * Deletes a set of cert and key files by certificate name + * @param certificateName the name of the cert file bundle to delete + */ + public void deleteCertificate(String certificateName) { + initEncryptedConfigStoreService(); + + encryptedConfigStoreService.deleteAllKeysOnPartialPath("certificates/" + certificateName); + synchronized (cerberusEnvironmentDataLock) { + final CerberusEnvironmentData environment = getDecryptedEnvironmentData(); + environment.removeCertificateInformationByName(certificateName); + + saveEnvironmentData(environment); + } + } + + /** + * Returns the contents of a specific certificate part that's been uploaded for a stack. + * + * @param part + * @return + */ + public Optional getCertPart(String certName, String part) { + return getEncryptedObject(buildCertFilePath(certName, part)); + } + public void storeCmsEnvConfig(final Properties cmsConfigMap) { final StringBuilder cmsConfigContents = new StringBuilder(); @@ -246,7 +272,7 @@ private Properties generateBaseCmsSystemProperties() { properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword); properties.put(CMS_ENV_NAME, environmentMetadata.getName()); // Ust the latest uploaded certificate - properties.put(CMS_CERTIFICATE_TO_USE, getCertificationInformationList().getLast()); + properties.put(CMS_CERTIFICATE_TO_USE, getCertificationInformationList().getLast().getCertificateName()); return properties; } diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/base.yaml index 87a0648b..405efae5 100644 --- a/src/main/resources/cloudformation/base.yaml +++ b/src/main/resources/cloudformation/base.yaml @@ -50,8 +50,9 @@ Resources: - !GetAtt 'CmsIamRole.Arn' Resource: - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /certificates/*]] - - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', cms/*]] - - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', cms]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /certificates]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /cms/*]] + - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /cms]] Sid: Allow-Bucket-Access-For-CMS Version: '2012-10-17' Type: AWS::S3::BucketPolicy diff --git a/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java b/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java index 4bc59199..1c65e1a3 100644 --- a/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java +++ b/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java @@ -18,6 +18,7 @@ import com.nike.cerberus.client.CerberusAdminClient; import com.nike.cerberus.client.CerberusAdminClientFactory; +import com.nike.cerberus.client.HttpClientFactory; import com.nike.cerberus.module.CerberusModule; import com.nike.cerberus.service.ConsoleService; import com.nike.vault.client.VaultAdminClient; From 64675fbb206f839b9e65e35513965546008fbc39 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 28 Nov 2017 10:44:11 -0800 Subject: [PATCH 44/69] fix setter name --- .../cerberus/domain/environment/CertificateInformation.java | 2 +- src/main/java/com/nike/cerberus/service/CertificateService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java index 842a080f..a5459a69 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java +++ b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java @@ -118,7 +118,7 @@ public static Builder create() { return new Builder(); } - public Builder certificateName(String certificateName) { + public Builder withCertificateName(String certificateName) { this.certificateName = certificateName; return this; } diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index 3c909868..7d1675db 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -537,7 +537,7 @@ public void uploadCertFiles(File certDir) { } CertificateInformation certificateInformation = CertificateInformation.Builder.create() - .certificateName(identityManagementCertificateName) + .withCertificateName(identityManagementCertificateName) .withIdentityManagementCertificateArn(identityManagementService.getServerCertificateArn(certificateName).get()) .withCommonName(StringUtils.removeStart(certificate.getSubjectX500Principal().getName(), "CN=")) .withSubjectAlternateNames(sans) From aba81021acb3ddf4ea0436ca5a7b61ea1cb6cf2f Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Tue, 28 Nov 2017 11:55:15 -0800 Subject: [PATCH 45/69] Fix bug where CMS cannot tag KMS keys --- src/main/resources/cloudformation/base.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/base.yaml index 405efae5..21a97ea6 100644 --- a/src/main/resources/cloudformation/base.yaml +++ b/src/main/resources/cloudformation/base.yaml @@ -97,6 +97,7 @@ Resources: - kms:ListKeyPolicies - kms:ListKeys - kms:PutKeyPolicy + - kms:TagResource - kms:UpdateAlias - kms:UpdateKeyDescription Effect: Allow From 873165a944ff15a2fbf75e38cbee7b4b190783b8 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Thu, 30 Nov 2017 09:31:37 -0800 Subject: [PATCH 46/69] enable rotation for non-tty environments and with manual option params (#106) * enable rotation for non-tty environments and with manual option params * PR Feedback --- .../com/nike/cerberus/cli/ArgsBuilder.java | 27 +++ .../cli/EnvironmentConfigToArgsMapper.java | 39 ++-- .../GenerateAndRotateCertificatesCommand.java | 56 +++++ .../composite/RotateCertificatesCommand.java | 14 +- .../core/GenerateCertificateFilesCommand.java | 165 +-------------- ...ificateFilesCommandParametersDelegate.java | 199 ++++++++++++++++++ .../core/UploadCertificateFilesCommand.java | 19 +- ...ificateFilesCommandParametersDelegate.java | 38 ++++ .../operation/composite/ChainableCommand.java | 6 + ...enerateAndRotateCertificatesOperation.java | 126 +++++++++++ .../RotateCertificatesOperation.java | 24 +-- .../GenerateCertificateFilesOperation.java | 52 +++-- .../UploadCertificatesFilesOperation.java | 2 +- .../cerberus/service/CertificateService.java | 24 ++- 14 files changed, 562 insertions(+), 229 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java create mode 100644 src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java create mode 100644 src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java diff --git a/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java b/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java index 96f062f5..808f96d6 100644 --- a/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java +++ b/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java @@ -52,6 +52,33 @@ public ArgsBuilder addAll(List argsToAdd) { return this; } + /** + * Adds an argument option to the args list using the supplied value unless passed args contains the option and a value. + * @param optionKey The option to add to the args + * @param optionValue The option value to use if passed args doesn't already contain the option key and value + * @param passedArgs The passed args from the user + * + * @return The builder + */ + public ArgsBuilder addOptionUsingPassedArgIfPresent(String optionKey, String optionValue, String [] passedArgs) { + int index = -1; + for (int i = 0; i < passedArgs.length; i++) { + if (optionKey.equals(passedArgs[i])) { + index = i; + break; + } + } + + args.add(optionKey); + if (index > -1 && index < passedArgs.length - 2) { + args.add(passedArgs[index + 1]); + } else { + args.add(optionValue); + } + + return this; + } + public List build() { return args; } diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index b0f0b227..a0b38d56 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -21,6 +21,8 @@ import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; +import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; +import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.CreateBaseCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; @@ -30,7 +32,9 @@ import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; @@ -84,7 +88,7 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig case CreateBaseCommand.COMMAND_NAME: return getCreateBaseCommandArgs(environmentConfig); case UploadCertificateFilesCommand.COMMAND_NAME: - return getUploadCertFilesCommandArgs(environmentConfig); + return getUploadCertFilesCommandArgs(environmentConfig, passedArgs); case CreateCmsClusterCommand.COMMAND_NAME: return getCreateCmsClusterCommandArgs(environmentConfig); case WhitelistCidrForVpcAccessCommand.COMMAND_NAME: @@ -111,6 +115,10 @@ public static List getArgsForCommand(EnvironmentConfig environmentConfig return getCreateCmsCmkCommandArgs(environmentConfig); case CreateEdgeDomainRecordCommand.COMMAND_NAME: return getCreateEdgeDomainRecordCommandArgs(environmentConfig); + case GenerateAndRotateCertificatesCommand.COMMAND_NAME: + return getGenerateCertificatesCommandArgs(environmentConfig); + case RotateCertificatesCommand.COMMAND_NAME: + return getUploadCertFilesCommandArgs(environmentConfig, passedArgs); default: return new LinkedList<>(); } @@ -183,10 +191,13 @@ private static List getGlobalTags(EnvironmentConfig environmentConfig) { return args.build(); } - private static List getUploadCertFilesCommandArgs(EnvironmentConfig environmentConfig) { + private static List getUploadCertFilesCommandArgs(EnvironmentConfig environmentConfig, String[] passedArgs) { return ArgsBuilder.create() - .addOption(UploadCertificateFilesCommand.CERT_PATH_LONG_ARG, - environmentConfig.getManagementService().getCertPath()) + .addOptionUsingPassedArgIfPresent( + UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG, + environmentConfig.getManagementService().getCertPath(), + passedArgs + ) .build(); } @@ -237,22 +248,22 @@ private static List getCreateWafCommandArgs(EnvironmentConfig config) { private static List getGenerateCertificatesCommandArgs(EnvironmentConfig config) { ArgsBuilder argsBuilder = ArgsBuilder.create() - .addOption(GenerateCertificateFilesCommand.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) - .addOption(GenerateCertificateFilesCommand.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()) - .addOption(GenerateCertificateFilesCommand.ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getOriginDomainNameOverride()) - .addOption(GenerateCertificateFilesCommand.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getLoadBalancerDomainNameOverride()) - .addOption(GenerateCertificateFilesCommand.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) - .addOption(GenerateCertificateFilesCommand.ACME_API_LONG_ARG, config.getAcmeApiUrl()) - .addOption(GenerateCertificateFilesCommand.CONTACT_EMAIL_LONG_ARG, config.getAcmeContactEmail()) - .addOption(GenerateCertificateFilesCommand.CERT_FOLDER_LONG_ARG, config.getLocalFolderToStoreCerts()); + .addOption(GenerateCertificateFilesCommandParametersDelegate.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getOriginDomainNameOverride()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getLoadBalancerDomainNameOverride()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.ACME_API_LONG_ARG, config.getAcmeApiUrl()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.CONTACT_EMAIL_LONG_ARG, config.getAcmeContactEmail()) + .addOption(GenerateCertificateFilesCommandParametersDelegate.CERT_FOLDER_LONG_ARG, config.getLocalFolderToStoreCerts()); if (config.isEnableLeCertFix()) { - argsBuilder.addFlag(GenerateCertificateFilesCommand.ENABLE_LE_CERTFIX_LONG_ARG); + argsBuilder.addFlag(GenerateCertificateFilesCommandParametersDelegate.ENABLE_LE_CERTFIX_LONG_ARG); } if (config.getAdditionalSubjectNames() != null) { config.getAdditionalSubjectNames().forEach(sn -> { - argsBuilder.addOption(GenerateCertificateFilesCommand.SUBJECT_ALT_NAME_LONG_ARG, sn); + argsBuilder.addOption(GenerateCertificateFilesCommandParametersDelegate.SUBJECT_ALT_NAME_LONG_ARG, sn); }); } diff --git a/src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java new file mode 100644 index 00000000..642fd72b --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.beust.jcommander.Parameters; +import com.beust.jcommander.ParametersDelegate; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.composite.GenerateAndRotateCertificatesOperation; + +import static com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand.COMMAND_NAME; + +@Parameters( + commandNames = { + COMMAND_NAME + }, + commandDescription = "Generates certificates with an ACME certificate provider and performs the necessary " + + "steps to rotate the certificates used by the ALB and CMS" +) +public class GenerateAndRotateCertificatesCommand implements Command { + + public static final String COMMAND_NAME = "generate-and-rotate-certificates"; + + @ParametersDelegate + private GenerateCertificateFilesCommandParametersDelegate generateCertificateFilesCommandParametersDelegate = + new GenerateCertificateFilesCommandParametersDelegate(); + + public GenerateCertificateFilesCommandParametersDelegate getGenerateCertificateFilesCommandParametersDelegate() { + return generateCertificateFilesCommandParametersDelegate; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return GenerateAndRotateCertificatesOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java index 0c36de67..7f56e381 100644 --- a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java +++ b/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java @@ -17,7 +17,9 @@ package com.nike.cerberus.command.composite; import com.beust.jcommander.Parameters; +import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; +import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.composite.RotateCertificatesOperation; @@ -31,9 +33,15 @@ public class RotateCertificatesCommand implements Command { public static final String COMMAND_NAME = "rotate-certificates"; - public static final String COMMAND_DESCRIPTION = "Rotates the certificates used by the ALB and CMS. " + - "Generates new certs optionally, uploads the certs to IAM and S3, updating the ALB to use them, " + - "generates new cms config, followed by a rolling reboot of CMS finalized by the deletion the old certificates."; + public static final String COMMAND_DESCRIPTION = "Rotates the certificates used by the ALB and CMS."; + + @ParametersDelegate + private UploadCertificateFilesCommandParametersDelegate uploadCertificateFilesCommandParametersDelegate + = new UploadCertificateFilesCommandParametersDelegate(); + + public UploadCertificateFilesCommandParametersDelegate getUploadCertificateFilesCommandParametersDelegate() { + return uploadCertificateFilesCommandParametersDelegate; + } @Override public String getCommandName() { diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java index 71da94a6..bf7418f4 100644 --- a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommand.java @@ -16,15 +16,12 @@ 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.operation.Operation; import com.nike.cerberus.operation.core.GenerateCertificateFilesOperation; -import java.util.ArrayList; -import java.util.List; - import static com.nike.cerberus.command.core.GenerateCertificateFilesCommand.COMMAND_DESCRIPTION; import static com.nike.cerberus.command.core.GenerateCertificateFilesCommand.COMMAND_NAME; @@ -38,162 +35,12 @@ public class GenerateCertificateFilesCommand implements Command { public static final String COMMAND_DESCRIPTION = "Generates the TLS certificates needed to enable https " + "through out the system, using an ACME provider such as LetsEncrypt"; - public static final String BASE_DOMAIN_LONG_ARG = "--base-domain"; - - public static final String EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--edge-domain-name-override"; - - public static final String ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--origin-domain-name-override"; - - public static final String LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--load-balancer-domain-name-override"; - - public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; - - public static final String SUBJECT_ALT_NAME_LONG_ARG = "--subject-alternative-name"; - - public static final String ENABLE_LE_CERTFIX_LONG_ARG = "--enable-letsecrypt-certfix"; - - public static final String CERT_FOLDER_LONG_ARG = "--local-certificate-directory"; - - public static final String ACME_API_LONG_ARG = "--acme-api-url"; - - public static final String CONTACT_EMAIL_LONG_ARG = "--contact-email"; - - public static final String LETS_ENCRYPT_ACME_API_URI = "acme://letsencrypt.org"; - - - @Parameter( - names = { - BASE_DOMAIN_LONG_ARG - }, - description = "The base domain for the environment that this command will use to generate the following " + - "subject name {env}.{base-domain} and with the following subject alternative name {env}.{region}.{base-domain}\n" + - "ex: cerberus -e demo -r us-west-2 generate-certificates --base-domain cerberus.example would make a cert for demo.cerberus.example " + - "with a sans of demo.us-west-2.cerberus.example so that we can create a CNAME record for " + - "demo.cerberus.example that will point to an ALB in us-west-2 with a CNAME record of " + - "demo.us-west-2.cerberus.example and the cert will be valid for both", - required = true - ) - private String baseDomainName; - - public String getBaseDomainName() { - return baseDomainName; - } - - @Parameter( - names = { - EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG - }, - description = "This command uses {environment}.{base-domain} as the common name, override it with this option" - ) - private String edgeDomainNameOverride; - - public String getEdgeDomainNameOverride() { - return edgeDomainNameOverride; - } - - @Parameter( - names = { - ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG - }, - description = "origin domain name defaults to origin.{environment-name}.{base-domain}, " + - "this command automatically creates a subject alternate name for this, override it with this option" - ) - private String originDomainNameOverride; - - public String getOriginDomainNameOverride() { - return originDomainNameOverride; - } - - @Parameter( - names = { - LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG - }, - description = "the load balancer domain name defaults to {environment-name}.{primary-primaryRegion}.{base-domain}, " + - "this command automatically creates a subject alternate name for this, override it with this option" - ) - private String loadBalancerDomainNameOverride; - - public String getLoadBalancerDomainNameOverride() { - return loadBalancerDomainNameOverride; - } - - @Parameter( - names = { - HOSTED_ZONE_ID_LONG_ARG - }, - description = "The AWS Route 53 hosted zone id that is configured to create records for the base domain", - required = true - ) - private String hostedZoneId; - - public String getHostedZoneId() { - return hostedZoneId; - } - - @Parameter( - names = { - SUBJECT_ALT_NAME_LONG_ARG - }, - description = "Alternative subject names, this should be any additional cnames that need to be secured. such as " - ) - private List subjectAlternativeNames = new ArrayList<>(); - - public List getSubjectAlternativeNames() { - return subjectAlternativeNames; - } - - @Parameter( - names = { - ENABLE_LE_CERTFIX_LONG_ARG - }, - description = "This command uses the acme4j client to communicate with the ACME server, " + - "it supports uses a hardcoded letsencrypt cert to get around ssl errors, you can use this " + - "flag if your truststore is not configured to trust LE certificates, which can be found " + - " here: https://letsencrypt.org/certificates/" - ) - private boolean EnableLetsEncryptCertfix = false; - - public boolean enableLetsEncryptCertfix() { - return EnableLetsEncryptCertfix; - } - - @Parameter( - names = { - CERT_FOLDER_LONG_ARG - }, - description = "A local writable folder to store the generated key and cert files", - required = true - ) - private String certDir; - - public String getCertDir() { - return certDir; - } - - @Parameter( - names = { - ACME_API_LONG_ARG - }, - description = "The ACME provider API URL to use, e.g. Let's Encrypt: " + LETS_ENCRYPT_ACME_API_URI, - required = true - ) - private String acmeApiUrl; - - public String getAcmeApiUrl() { - return acmeApiUrl; - } - - @Parameter( - names = { - CONTACT_EMAIL_LONG_ARG - }, - description = "The email contact to use when creating the certificates", - required = true - ) - private String contactEmail; + @ParametersDelegate + private GenerateCertificateFilesCommandParametersDelegate generateCertificateFilesCommandParametersDelegate = + new GenerateCertificateFilesCommandParametersDelegate(); - public String getContactEmail() { - return contactEmail; + public GenerateCertificateFilesCommandParametersDelegate getGenerateCertificateFilesCommandParametersDelegate() { + return generateCertificateFilesCommandParametersDelegate; } @Override diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java new file mode 100644 index 00000000..2a91462e --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2017 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 java.util.ArrayList; +import java.util.List; + +public class GenerateCertificateFilesCommandParametersDelegate { + + public static final String BASE_DOMAIN_LONG_ARG = "--base-domain"; + public static final String EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--edge-domain-name-override"; + public static final String ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--origin-domain-name-override"; + public static final String LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG = "--load-balancer-domain-name-override"; + public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; + public static final String SUBJECT_ALT_NAME_LONG_ARG = "--subject-alternative-name"; + public static final String ENABLE_LE_CERTFIX_LONG_ARG = "--enable-letsecrypt-certfix"; + public static final String CERT_FOLDER_LONG_ARG = "--local-certificate-directory"; + public static final String ACME_API_LONG_ARG = "--acme-api-url"; + public static final String CONTACT_EMAIL_LONG_ARG = "--contact-email"; + public static final String NO_TTY_LONG_ARG = "--no-tty"; + public static final String ACCEPT_ACME_TOS = "--no-tty-force-acme-tos-accept"; + + public static final String LETS_ENCRYPT_ACME_API_URI = "acme://letsencrypt.org"; + + @Parameter( + names = { + BASE_DOMAIN_LONG_ARG + }, + description = "The base domain for the environment that this command will use to generate the following " + + "subject name {env}.{base-domain} and with the following subject alternative name {env}.{region}.{base-domain}\n" + + "ex: cerberus -e demo -r us-west-2 generate-certificates --base-domain cerberus.example would make a cert for demo.cerberus.example " + + "with a sans of demo.us-west-2.cerberus.example so that we can create a CNAME record for " + + "demo.cerberus.example that will point to an ALB in us-west-2 with a CNAME record of " + + "demo.us-west-2.cerberus.example and the cert will be valid for both" + ) + private String baseDomainName; + + @Parameter( + names = { + EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG + }, + description = "This command uses {environment}.{base-domain} as the common name, override it with this option" + ) + private String edgeDomainNameOverride; + + @Parameter( + names = { + ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG + }, + description = "Origin domain name defaults to origin.{environment-name}.{base-domain}, " + + "this command automatically creates a subject alternate name for this, override it with this option" + ) + private String originDomainNameOverride; + + @Parameter( + names = { + LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG + }, + description = "The load balancer domain name defaults to {environment-name}.{primary-primaryRegion}.{base-domain}, " + + "this command automatically creates a subject alternate name for this, override it with this option" + ) + private String loadBalancerDomainNameOverride; + + @Parameter( + names = { + HOSTED_ZONE_ID_LONG_ARG + }, + description = "The AWS Route 53 hosted zone id that is configured to create records for the base domain", + required = true + ) + private String hostedZoneId; + + @Parameter( + names = { + SUBJECT_ALT_NAME_LONG_ARG + }, + description = "Alternative subject names, this should be any additional cnames that need to be secured. such as " + ) + private List subjectAlternativeNames = new ArrayList(); + + @Parameter( + names = { + ENABLE_LE_CERTFIX_LONG_ARG + }, + description = "This command uses the acme4j client to communicate with the ACME server, " + + "it supports uses a hardcoded Let'sEncrypt cert to get around SSL errors, you can use this " + + "flag if your truststore is not configured to trust LE certificates, which can be found " + + " here: https://letsencrypt.org/certificates/" + ) + private boolean enableLetsEncryptCertfix = false; + + @Parameter( + names = { + CERT_FOLDER_LONG_ARG + }, + description = "A local writable folder to store the generated key and cert files", + required = true + ) + private String certDir; + + @Parameter( + names = { + ACME_API_LONG_ARG + }, + description = "The ACME provider API URL to use, e.g. Let's Encrypt: " + LETS_ENCRYPT_ACME_API_URI, + required = true + ) + private String acmeApiUrl; + + @Parameter( + names = { + CONTACT_EMAIL_LONG_ARG + }, + description = "The email contact to use when creating the certificates", + required = true + ) + private String contactEmail; + + @Parameter( + names = { + NO_TTY_LONG_ARG + }, + description = "Flag to supply when running command in an environment without TTY, such as a build job" + ) + private boolean tty = false; + + @Parameter( + names = { + ACCEPT_ACME_TOS + }, + description = "If supplying --no-tty and creating a new ACME account you must go read the TOS and supply " + + "this flag to indicate that you accept the TOS" + ) + private boolean autoAcceptAcmeTos = false; + + public String getBaseDomainName() { + return baseDomainName; + } + + public String getEdgeDomainNameOverride() { + return edgeDomainNameOverride; + } + + public String getOriginDomainNameOverride() { + return originDomainNameOverride; + } + + public String getLoadBalancerDomainNameOverride() { + return loadBalancerDomainNameOverride; + } + + public String getHostedZoneId() { + return hostedZoneId; + } + + public List getSubjectAlternativeNames() { + return subjectAlternativeNames; + } + + public boolean enableLetsEncryptCertfix() { + return enableLetsEncryptCertfix; + } + + public String getCertDir() { + return certDir; + } + + public String getAcmeApiUrl() { + return acmeApiUrl; + } + + public String getContactEmail() { + return contactEmail; + } + + public boolean isTty() { + return tty; + } + + public boolean isAutoAcceptAcmeTos() { + return autoAcceptAcmeTos; + } +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java index 05e24548..c052778d 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java @@ -16,15 +16,12 @@ 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.command.validator.UploadCertFilesPathValidator; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.operation.core.UploadCertificatesFilesOperation; -import java.nio.file.Path; - import static com.nike.cerberus.command.core.UploadCertificateFilesCommand.COMMAND_NAME; /** @@ -34,17 +31,13 @@ public class UploadCertificateFilesCommand implements Command { public static final String COMMAND_NAME = "upload-certificate-files"; - public static final String CERT_PATH_LONG_ARG = "--cert-dir-path"; - @Parameter( - names = {CERT_PATH_LONG_ARG}, - required = true, - description = "Path to the directory that contains the certificate files.", - validateValueWith = UploadCertFilesPathValidator.class) - private Path certPath; + @ParametersDelegate + private final UploadCertificateFilesCommandParametersDelegate uploadCertificatesPathParametersDelegate = + new UploadCertificateFilesCommandParametersDelegate(); - public Path getCertPath() { - return certPath; + public UploadCertificateFilesCommandParametersDelegate getUploadCertificatesPathParametersDelegate() { + return uploadCertificatesPathParametersDelegate; } @Override diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java new file mode 100644 index 00000000..4e1e92bc --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 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.nike.cerberus.command.validator.UploadCertFilesPathValidator; + +import java.nio.file.Path; + +public class UploadCertificateFilesCommandParametersDelegate { + + public static final String CERT_PATH_LONG_ARG = "--cert-dir-path"; + + @Parameter( + names = {CERT_PATH_LONG_ARG}, + required = true, + description = "Path to the directory that contains the certificate files.", + validateValueWith = UploadCertFilesPathValidator.class) + private Path certPath; + + public Path getCertPath() { + return certPath; + } +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java b/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java index ca738017..23482d94 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java +++ b/src/main/java/com/nike/cerberus/operation/composite/ChainableCommand.java @@ -79,6 +79,12 @@ public Builder withAdditionalArg(String additionalArg) { return this; } + public Builder withOption(String key, String value) { + additionalArgs.add(key); + additionalArgs.add(value); + return this; + } + public ChainableCommand build() { ChainableCommand chainableCommand = new ChainableCommand(); chainableCommand.command = this.command; diff --git a/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java new file mode 100644 index 00000000..d96d417b --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017 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.composite; + +import com.google.common.collect.Lists; +import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; +import com.nike.cerberus.command.composite.RotateCertificatesCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; +import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.service.CloudFormationService; + +import javax.inject.Inject; +import java.util.List; + +import static com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG; +import static com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate.*; + +public class GenerateAndRotateCertificatesOperation extends CompositeOperation { + + private final CloudFormationService cloudFormationService; + private final EnvironmentMetadata environmentMetadata; + + @Inject + public GenerateAndRotateCertificatesOperation(CloudFormationService cloudFormationService, + EnvironmentMetadata environmentMetadata) { + + this.cloudFormationService = cloudFormationService; + this.environmentMetadata = environmentMetadata; + } + + + @Override + protected List getCompositeCommandChain(GenerateAndRotateCertificatesCommand compositeCommand) { + GenerateCertificateFilesCommandParametersDelegate parameters = compositeCommand + .getGenerateCertificateFilesCommandParametersDelegate(); + + ChainableCommand.Builder generateCertificateFilesCommandBuilder = ChainableCommand.Builder.create() + .withCommand(new GenerateCertificateFilesCommand()) + .withOption( + BASE_DOMAIN_LONG_ARG, + parameters.getBaseDomainName() + ) + .withOption( + EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, + parameters.getEdgeDomainNameOverride() + ) + .withOption( + ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, + parameters.getOriginDomainNameOverride() + ) + .withOption( + LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, + parameters.getLoadBalancerDomainNameOverride() + ) + .withOption( + HOSTED_ZONE_ID_LONG_ARG, + parameters.getHostedZoneId() + ) + .withOption( + ENABLE_LE_CERTFIX_LONG_ARG, + String.valueOf(parameters.enableLetsEncryptCertfix()) + ) + .withOption( + CERT_FOLDER_LONG_ARG, + parameters.getCertDir() + ) + .withOption( + ACME_API_LONG_ARG, + parameters.getAcmeApiUrl() + ) + .withOption( + CONTACT_EMAIL_LONG_ARG, + parameters.getContactEmail() + ); + + parameters.getSubjectAlternativeNames().forEach(name -> { + generateCertificateFilesCommandBuilder.withOption(SUBJECT_ALT_NAME_LONG_ARG, name); + }); + + return Lists.newArrayList( + // Generate the Certificates + generateCertificateFilesCommandBuilder.build(), + + // Rotate the certificate files + ChainableCommand.Builder.create().withCommand(new RotateCertificatesCommand()) + .withOption( + CERT_PATH_LONG_ARG, + parameters.getCertDir()) + .build() + ); + } + + @Override + public boolean isRunnable(GenerateAndRotateCertificatesCommand command) { + boolean isRunnable = true; + String environmentName = environmentMetadata.getName(); + + if (! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { + log.error("The load-balancer stack must be present in order to rotate certificates"); + isRunnable = false; + } + + if (! cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName))) { + log.error("The cms stack must be present to rotate certificates"); + isRunnable = false; + } + + return isRunnable; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java index bd9e022f..f83bbf9e 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java @@ -21,15 +21,14 @@ import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; -import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.RebootCmsCommand; import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.service.CloudFormationService; -import java.util.LinkedList; import java.util.List; /** @@ -50,28 +49,29 @@ public RotateCertificatesOperation(CloudFormationService cloudFormationService, @Override protected List getCompositeCommandChain(RotateCertificatesCommand compositeCommand) { - List commandList = new LinkedList<>(); - - if (environmentConfig.isGenerateKeysAndCerts()) { - commandList.add(ChainableCommand.Builder.create().withCommand(new GenerateCertificateFilesCommand()).build()); - } - - commandList.addAll(Lists.newArrayList( + return Lists.newArrayList( // Add the cert and key files to S3 - ChainableCommand.Builder.create().withCommand(new UploadCertificateFilesCommand()).build(), + ChainableCommand.Builder.create().withCommand(new UploadCertificateFilesCommand()) + .withAdditionalArg(UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG) + .withAdditionalArg(compositeCommand.getUploadCertificateFilesCommandParametersDelegate() + .getCertPath().toString()) + .build(), + // Update the Load Balancer stack to use the new cert ChainableCommand.Builder.create().withCommand(new UpdateStackCommand()) .withAdditionalArg(UpdateStackCommand.STACK_NAME_LONG_ARG) .withAdditionalArg(Stack.LOAD_BALANCER.getName()) .build(), + // Generate new CMS config that points to the new cert ChainableCommand.Builder.create().withCommand(new UpdateCmsConfigCommand()).build(), + // Do a rolling reboot of the management service ChainableCommand.Builder.create().withCommand(new RebootCmsCommand()).build(), + // Delete all certs except the latest (there should just be the one) ChainableCommand.Builder.create().withCommand(new DeleteOldestCertificatesCommand()).build() - )); - return commandList; + ); } @Override diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java index fdbba4f2..f1f154fd 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java @@ -17,6 +17,7 @@ package com.nike.cerberus.operation.core; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; +import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CertificateService; @@ -61,54 +62,67 @@ public GenerateCertificateFilesOperation(CertificateService certService, ) @Override public void run(GenerateCertificateFilesCommand command) { + GenerateCertificateFilesCommandParametersDelegate parameters = + command.getGenerateCertificateFilesCommandParametersDelegate(); + // The common name ex: demo.example.com - String commonName = StringUtils.isNotBlank(command.getEdgeDomainNameOverride()) ? - command.getEdgeDomainNameOverride() : - String.format("%s.%s", environmentMetadata.getName(), command.getBaseDomainName()); + String commonName = StringUtils.isNotBlank(parameters.getEdgeDomainNameOverride()) ? + parameters.getEdgeDomainNameOverride() : + String.format("%s.%s", environmentMetadata.getName(), parameters.getBaseDomainName()); // origin name san ex: origin.demo.example.com - String originName = StringUtils.isNotBlank(command.getOriginDomainNameOverride()) ? - command.getOriginDomainNameOverride() : - String.format("origin.%s.%s", environmentMetadata.getName(), command.getBaseDomainName()); + String originName = StringUtils.isNotBlank(parameters.getOriginDomainNameOverride()) ? + parameters.getOriginDomainNameOverride() : + String.format("origin.%s.%s", environmentMetadata.getName(), parameters.getBaseDomainName()); // The region specific subject alternate name for the load balancer. EX demo.us-west-2.example.com - String loadBalancerName = StringUtils.isNotBlank(command.getLoadBalancerDomainNameOverride()) ? - command.getLoadBalancerDomainNameOverride() : - String.format("%s.%s.%s", environmentMetadata.getName(), environmentMetadata.getRegionName(), command.getBaseDomainName()); + String loadBalancerName = StringUtils.isNotBlank(parameters.getLoadBalancerDomainNameOverride()) ? + parameters.getLoadBalancerDomainNameOverride() : + String.format("%s.%s.%s", environmentMetadata.getName(), environmentMetadata.getRegionName(), parameters.getBaseDomainName()); Set subjectAlternativeNames = new HashSet<>(); - subjectAlternativeNames.addAll(command.getSubjectAlternativeNames()); + subjectAlternativeNames.addAll(parameters.getSubjectAlternativeNames()); subjectAlternativeNames.add(originName); subjectAlternativeNames.add(loadBalancerName); // Enable the use of the hard coded lets encrypt cert if enabled - if (command.enableLetsEncryptCertfix()) { - log.warn("Setting acme4j.le.certfix system property to 'true', this only works if you use the special acme:// lets encrypt addr. See: https://shredzone.org/maven/acme4j/usage/session.html and https://shredzone.org/maven/acme4j/provider.html"); + if (parameters.enableLetsEncryptCertfix()) { + log.warn("Setting acme4j.le.certfix system property to 'true', this only works if you use the special " + + "acme:// lets encrypt addr. See: https://shredzone.org/maven/acme4j/usage/session.html" + + " and https://shredzone.org/maven/acme4j/provider.html"); + System.setProperty("acme4j.le.certfix", "true"); } try { // Use a temp dir or local dir if provided - File certDir = new File(command.getCertDir()); + File certDir = new File(parameters.getCertDir()); // check that we can write to the provided dir FileUtils.forceMkdir(certDir); if (!certDir.isDirectory() || !certDir.canWrite()) { - throw new RuntimeException("The certificate directory is not a directory or is not writable, path: " + certDir.getAbsolutePath()); + throw new RuntimeException("The certificate directory is not a directory or is not writable, path: " + + certDir.getAbsolutePath()); } // confirm with user - consoleService.askUserToProceed(String.format("Preparing to generate certs in %s with Common Name: %s and Subject Alternative Names: %s", - certDir.getAbsolutePath(), commonName, String.join(", ", subjectAlternativeNames)), NO); + String msg = String.format("Preparing to generate certs in %s with Common Name: %s and Subject Alternative Names: %s", + certDir.getAbsolutePath(), commonName, String.join(", ", subjectAlternativeNames)); + if (command.getGenerateCertificateFilesCommandParametersDelegate().isTty()) { + consoleService.askUserToProceed(msg, NO); + } else { + log.info(msg); + } // generate the certs certService.generateCerts( certDir, - command.getAcmeApiUrl(), + parameters.getAcmeApiUrl(), commonName, subjectAlternativeNames, - command.getHostedZoneId(), - command.getContactEmail() + parameters.getHostedZoneId(), + parameters.getContactEmail(), + command.getGenerateCertificateFilesCommandParametersDelegate().isAutoAcceptAcmeTos() ); } catch (Exception e) { throw new RuntimeException("Failed to generate certs", e); diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java index e587835c..0fa0ad85 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java @@ -46,7 +46,7 @@ public UploadCertificatesFilesOperation(EnvironmentMetadata environmentMetadata, @Override public void run(final UploadCertificateFilesCommand command) { - certificateService.uploadCertFiles(command.getCertPath().toFile()); + certificateService.uploadCertFiles(command.getUploadCertificatesPathParametersDelegate().getCertPath().toFile()); } @Override diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index 7d1675db..a4611560 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -177,7 +177,7 @@ public CertificateService(ConsoleService console, * @param session {@link Session} to bind with * @return {@link Registration} connected to your account */ - protected Registration findOrRegisterAccount(Session session, String contactEmail) throws AcmeException, IOException { + protected Registration findOrRegisterAccount(Session session, String contactEmail, boolean autoAcceptTos) throws AcmeException, IOException { Registration reg; try { // Try to create a new Registration. @@ -189,7 +189,7 @@ protected Registration findOrRegisterAccount(Session session, String contactEmai URI agreement = reg.getAgreement(); if (!(agreement == null)) { - acceptAgreement(reg, agreement); + acceptAgreement(reg, agreement, autoAcceptTos); } } catch (AcmeConflictException ex) { // The Key Pair is already registered. getLocation() contains the @@ -206,13 +206,20 @@ protected Registration findOrRegisterAccount(Session session, String contactEmai * @param reg The registration object * @param agreement The link to the TOS */ - protected void acceptAgreement(Registration reg, URI agreement) { + protected void acceptAgreement(Registration reg, URI agreement, boolean autoAcceptTos) { try { log.info("Please download and review the Terms of Service: " + agreement); - String userResponse = console.readLine("Type \"I Accept\" to accept the Terms of Service, anything else will exit: "); - if (!StringUtils.equalsIgnoreCase("I Accept", userResponse)) { - throw new RuntimeException("User did not accept the Terms of Service"); + + if (! autoAcceptTos) { + String userResponse = console.readLine("Type \"I Accept\" to accept the Terms of Service, anything else will exit: "); + if (!StringUtils.equalsIgnoreCase("I Accept", userResponse)) { + throw new RuntimeException("User did not accept the Terms of Service"); + } + } else { + log.info("The auto accept flag was passed with the understanding that you have downloaded, " + + "reviewed and agree to the TOS: {},", agreement); } + reg.modify().setAgreement(agreement).commit(); log.info("Updated user's ToS"); } catch (AcmeException | IOException e) { @@ -293,7 +300,8 @@ public void generateCerts(File certDir, String commonName, Set subjectAlternativeNames, String hostedZoneId, - String contactEmail) { + String contactEmail, + boolean autoAcceptTos) { try { List names = new LinkedList<>(); @@ -303,7 +311,7 @@ public void generateCerts(File certDir, KeyPair userKeyPair = loadOrCreateKeyPair(new File(certDir.getAbsolutePath() + File.separator + USER_KEY_FILE)); Session session = new Session(acmeServerUrl, userKeyPair); - Registration registration = findOrRegisterAccount(session, contactEmail); + Registration registration = findOrRegisterAccount(session, contactEmail, autoAcceptTos); Map> domainChallengeCollectionMap = new HashMap<>(); for (String name : names) { From 7113bd8b5f8d098d19c84d6ca997d10b9d1be71a Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Wed, 6 Dec 2017 17:06:31 -0800 Subject: [PATCH 47/69] Allow restore from non-highlander Cerberus environment --- .../cerberus/client/CerberusAdminClient.java | 157 ++---------------- .../client/CerberusAdminClientFactory.java | 3 +- .../core/RestoreCerberusBackupOperation.java | 85 +--------- ...CerberusDataFromS3BackupOperationTest.java | 71 -------- 4 files changed, 22 insertions(+), 294 deletions(-) delete mode 100644 src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java diff --git a/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java b/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java index e8caa040..0ca2df95 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java +++ b/src/main/java/com/nike/cerberus/client/CerberusAdminClient.java @@ -17,75 +17,51 @@ package com.nike.cerberus.client; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; -import com.nike.cerberus.domain.cms.SafeDepositBox; -import com.nike.cerberus.domain.cms.SdbMetadataResult; -import com.nike.vault.client.UrlResolver; -import com.nike.vault.client.VaultAdminClient; import com.nike.vault.client.VaultClientException; -import com.nike.vault.client.VaultServerException; import com.nike.vault.client.auth.VaultCredentialsProvider; import com.nike.vault.client.http.HttpHeader; import com.nike.vault.client.http.HttpMethod; -import com.nike.vault.client.http.HttpStatus; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import okhttp3.ResponseBody; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLException; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; /** * A Cerberus admin client with the ability to restore metadata */ -public class CerberusAdminClient extends VaultAdminClient { +public class CerberusAdminClient { private final Logger log = LoggerFactory.getLogger(getClass()); + public static final MediaType DEFAULT_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); + protected OkHttpClient httpClient; - protected VaultCredentialsProvider credentialsProvider; - protected UrlResolver vaultUrlResolver; - protected ObjectMapper objectMapper; - protected final Gson gson = new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .disableHtmlEscaping() - .create(); + protected VaultCredentialsProvider credentialsProvider; /** * Explicit constructor that allows for full control over construction of the Vault client. * - * @param vaultUrlResolver URL resolver for Vault * @param credentialsProvider Credential provider for acquiring a token for interacting with Vault * @param httpClient HTTP client for calling Vault */ - public CerberusAdminClient(UrlResolver vaultUrlResolver, - VaultCredentialsProvider credentialsProvider, + public CerberusAdminClient(VaultCredentialsProvider credentialsProvider, OkHttpClient httpClient, ObjectMapper objectMapper) { - - super(vaultUrlResolver, credentialsProvider, httpClient); this.httpClient = httpClient; this.credentialsProvider = credentialsProvider; - this.vaultUrlResolver = vaultUrlResolver; - this.objectMapper = objectMapper; } - public void restoreMetadata(String jsonPayload) { - HttpUrl url = buildUrl("v1/", "metadata"); + public void restoreMetadata(String baseUrl, String jsonPayload) { + HttpUrl url = buildUrl(baseUrl, "v1/", "restore-sdb"); Response response = execute(url, HttpMethod.PUT, jsonPayload); if (!response.isSuccessful()) { String body; @@ -98,55 +74,6 @@ public void restoreMetadata(String jsonPayload) { } } - public SdbMetadataResult getSDBMetaData(int offset, int limit) { - URL baseUrl = null; - try { - baseUrl = new URL(vaultUrlResolver.resolve()); - } catch (MalformedURLException e) { - throw new RuntimeException("Failed to process cerberus base url", e); - } - - HttpUrl url = new HttpUrl.Builder() - .scheme(baseUrl.getProtocol()) - .host(baseUrl.getHost()) - .addPathSegments("v1/metadata") - .addQueryParameter("limit", String.valueOf(limit)) - .addQueryParameter("offset", String.valueOf(offset)) - .build(); - - Response response = execute(url, HttpMethod.GET, null); - if (!response.isSuccessful()) { - throw new RuntimeException(String.format("Failed to get metadata from Cerberus. Code: %s, Msg: %s", - response.code(), response.message())); - } - - return parseCmsResponseBody(response, SdbMetadataResult.class); - } - - public List getAllSdbMetadata() { - List sdbMetadataList = new LinkedList<>(); - SdbMetadataResult currentResult = null; - int offset = 0; - int limit = 100; - do { - currentResult = getSDBMetaData(offset, limit); - sdbMetadataList.addAll(currentResult.getSafeDepositBoxMetadata()); - offset = currentResult.getNextOffset(); - log.info("Retrieved metadata for {} SDBs", currentResult.getSdbCountInResult()); - } while (currentResult.hasNext()); - - return sdbMetadataList; - } - - public void writeJson(final String path, final Map data) { - final HttpUrl url = buildUrl(SECRET_PATH_PREFIX, path); - final Response response = execute(url, HttpMethod.POST, data); - - if (response.code() != HttpStatus.NO_CONTENT) { - parseAndThrowErrorResponse(response); - } - } - protected Response execute(final HttpUrl url, final String method, final String json) { try { Request.Builder requestBuilder = new Request.Builder() @@ -162,8 +89,6 @@ protected Response execute(final HttpUrl url, final String method, final String if (e instanceof SSLException && e.getMessage() != null && e.getMessage().contains("Unrecognized SSL message, plaintext connection?")) { - // AnyConnect web security proxy can be disabled with: - // `sudo /opt/cisco/anyconnect/bin/acwebsecagent -disablesvc -websecurity` throw new VaultClientException("I/O error while communicating with vault. Unrecognized SSL message may be due to a web proxy e.g. AnyConnect", e); } else { throw new VaultClientException("I/O error while communicating with vault.", e); @@ -172,64 +97,18 @@ protected Response execute(final HttpUrl url, final String method, final String } /** - * Convenience method for parsing the HTTP response and mapping it to a class. + * Builds the full URL for preforming an operation against Vault. * - * @param response The HTTP response object - * @param responseClass The class to map the response body to - * @param Represents the type to map to - * @return Deserialized object from the response body + * @param baseUrl Base URL for Cerberus + * @param prefix Prefix between the environment URL and specified path + * @param path Path for the requested operation + * @return Full URL to execute a request against */ - protected M parseCmsResponseBody(final Response response, final Class responseClass) { - try (ResponseBody body = response.body()) { - return objectMapper.readValue(body.bytes(), responseClass); - } catch (IOException e) { - throw new VaultClientException("Error parsing the response body from CMS", e); - } - } - - /** - * Read operation for a specified path. Will return a {@link Map} of the data stored at the specified path. - * If Vault returns an unexpected response code, a {@link VaultServerException} will be thrown with the code - * and error details. If an unexpected I/O error is encountered, a {@link VaultClientException} will be thrown - * wrapping the underlying exception. - * - * @param path Path to the data - * @return Map of the data - */ - public GenericVaultResponse readDataGenerically(final String path) { - final HttpUrl url = buildUrl(SECRET_PATH_PREFIX, path); - log.debug("read: requestUrl={}", url); - - final Response response = execute(url, HttpMethod.GET, null); - - if (response.code() != HttpStatus.OK) { - parseAndThrowErrorResponse(response); + protected HttpUrl buildUrl(String baseUrl, String prefix, String path) { + if (!StringUtils.endsWith(baseUrl, "/")) { + baseUrl += "/"; } - return parseResponseBody(response, GenericVaultResponse.class); - } - - public class GenericVaultResponse { - private Map data; - - public Map getData() { - return data; - } - - public GenericVaultResponse setData(Map data) { - this.data = data; - return this; - } - } - - protected M parseResponseBody(final Response response, final Class responseClass) { - final String responseBodyStr = responseBodyAsString(response); - try { - return gson.fromJson(responseBodyStr, responseClass); - } catch (JsonSyntaxException e) { - log.error("parseResponseBody: responseCode={}, requestUrl={}", - response.code(), response.request().url()); - throw new VaultClientException("Error parsing the response body from vault, response code: " + response.code(), e); - } + return HttpUrl.parse(baseUrl + prefix + path); } } diff --git a/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java b/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java index a20177e5..687474ab 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java +++ b/src/main/java/com/nike/cerberus/client/CerberusAdminClientFactory.java @@ -38,9 +38,8 @@ public CerberusAdminClientFactory(ObjectMapper objectMapper, /** * Admin client for doing admin cms tasks */ - public CerberusAdminClient createCerberusAdminClient(String url) { + public CerberusAdminClient createCerberusAdminClient() { return new CerberusAdminClient( - new StaticVaultUrlResolver(url), new DefaultVaultCredentialsProviderChain(), httpClientFactory.getGenericClient(), objectMapper diff --git a/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java b/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java index 1877e391..1fb5afaa 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RestoreCerberusBackupOperation.java @@ -25,7 +25,6 @@ import com.amazonaws.services.s3.model.CryptoConfiguration; import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.tomaslanger.chalk.Chalk; import com.google.inject.Inject; @@ -36,10 +35,6 @@ import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.ConsoleService; import com.nike.cerberus.service.S3StoreService; -import com.nike.vault.client.VaultAdminClient; -import com.nike.vault.client.VaultClientException; -import com.nike.vault.client.model.VaultListResponse; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +42,6 @@ import java.io.IOException; import java.text.DecimalFormat; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -112,7 +106,7 @@ public void run(RestoreCerberusBackupCommand command) { String kmsCustomerMasterKeyId = getKmsCmkId(CERBERUS_BACKUP_METADATA_JSON_FILE_KEY, s3StoreService); S3StoreService s3EncryptionStoreService = getS3EncryptionStoreService(kmsCustomerMasterKeyId, command); - CerberusAdminClient cerberusAdminClient = cerberusAdminClientFactory.createCerberusAdminClient(command.getCerberusUrl()); + CerberusAdminClient cerberusAdminClient = cerberusAdminClientFactory.createCerberusAdminClient(); validateRestore(s3EncryptionStoreService, command); @@ -123,8 +117,8 @@ public void run(RestoreCerberusBackupCommand command) { try { Double percent = i / keys.size() * 100; System.out.print(String.format("Restoring backups %s%% complete\r", df.format(percent))); - String json = getDecryptedJson(sdbBackupKey, s3EncryptionStoreService); - processBackup(json, cerberusAdminClient); + String sdbBackupJson = getDecryptedJson(sdbBackupKey, s3EncryptionStoreService); + cerberusAdminClient.restoreMetadata(command.getCerberusUrl(), sdbBackupJson); } catch (Throwable t) { logger.error("Failed to process backup json for {}", Chalk.on(sdbBackupKey).red().toString(), t); } @@ -238,79 +232,6 @@ private String getDecryptedJson(String sdbBackupKey, S3StoreService s3StoreServi return json.get(); } - /** - * Process the stored backup json - * Step 1: Restores the metadata to CMS - * Step 2: Restores the secrete data to Vault - * - * @param sdbBackupJson the json string from s3 - */ - protected void processBackup(String sdbBackupJson, CerberusAdminClient cerberusAdminClient) throws IOException { - - JsonNode sdb = objectMapper.readTree(sdbBackupJson); - - // restore metadata to cms - cerberusAdminClient.restoreMetadata(sdbBackupJson); - deleteAllSecrets(sdb.get("path").asText(), cerberusAdminClient); - // restore secret vault data - JsonNode data = sdb.get("data"); - Map> kvPairs = objectMapper.convertValue(data, - new TypeReference>>() { - }); - - kvPairs.forEach((String path, Map secretData) -> { - Map genericDataMap = new HashMap<>(); - secretData.forEach((String key, JsonNode valueNode) -> { - if (valueNode.isObject()) { - genericDataMap.put(key, objectMapper.convertValue(valueNode, - new TypeReference>() { - }) - ); - } else if (valueNode.isTextual()) { - genericDataMap.put(key, valueNode.textValue()); - } else if (valueNode.isBoolean()) { - genericDataMap.put(key, valueNode.booleanValue()); - } else { - throw new RuntimeException("Unexpected value type for secret value. Type: " + valueNode.getClass()); - } - }); - cerberusAdminClient.writeJson(path, genericDataMap); - }); - } - - /** - * Deletes all of the secrets from Vault stored at the safe deposit box's path. - * - * @param path path to start deleting at. - */ - protected void deleteAllSecrets(final String path, VaultAdminClient vaultAdminClient) { - try { - String fixedPath = path; - - if (StringUtils.endsWith(path, "/")) { - fixedPath = StringUtils.substring(path, 0, StringUtils.lastIndexOf(path, "/")); - } - - final VaultListResponse listResponse = vaultAdminClient.list(fixedPath); - final List keys = listResponse.getKeys(); - - if (keys == null || keys.isEmpty()) { - return; - } - - for (final String key : keys) { - if (StringUtils.endsWith(key, "/")) { - final String fixedKey = StringUtils.substring(key, 0, key.lastIndexOf("/")); - deleteAllSecrets(fixedPath + "/" + fixedKey, vaultAdminClient); - } else { - vaultAdminClient.delete(fixedPath + "/" + key); - } - } - } catch (VaultClientException vce) { - throw new RuntimeException("Failed to delete secrets from Vault. for path: " + path); - } - } - @Override public boolean isRunnable(RestoreCerberusBackupCommand command) { return true; diff --git a/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java b/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java deleted file mode 100644 index 1c65e1a3..00000000 --- a/src/test/java/com/nike/cerberus/operation/core/RestoreCompleteCerberusDataFromS3BackupOperationTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2017 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.nike.cerberus.client.CerberusAdminClient; -import com.nike.cerberus.client.CerberusAdminClientFactory; -import com.nike.cerberus.client.HttpClientFactory; -import com.nike.cerberus.module.CerberusModule; -import com.nike.cerberus.service.ConsoleService; -import com.nike.vault.client.VaultAdminClient; -import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; - -import java.io.IOException; - -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.MockitoAnnotations.initMocks; - -public class RestoreCompleteCerberusDataFromS3BackupOperationTest { - - private RestoreCerberusBackupOperation operation; - - @Mock - private CerberusAdminClientFactory adminClientFactory; - - @Mock - private ConsoleService consoleService; - - @Before - public void before() { - initMocks(this); - operation = new RestoreCerberusBackupOperation(CerberusModule.configObjectMapper(), consoleService, adminClientFactory); - } - - @Test - public void test_that_process_backup_can_handle_nested_maps() throws IOException { - String serializedDecryptedBackupJson = IOUtils.toString( - getClass().getClassLoader() - .getResourceAsStream("com/nike/cerberus/operation/core/nested-map-sdb-backup.json") - ); - - CerberusAdminClient client = mock(CerberusAdminClient.class); - RestoreCerberusBackupOperation operationSpy = spy(operation); - doNothing().when(operationSpy).deleteAllSecrets(anyString(), any(VaultAdminClient.class)); - operationSpy.processBackup(serializedDecryptedBackupJson, client); - - - } - - -} From b47f3785b3476be22283dbedec0677ff2e9f5216 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Fri, 8 Dec 2017 12:31:39 -0800 Subject: [PATCH 48/69] Multi-region friendly CLI Config and Services (#108) * Make the cli store config in multiple regions and make services and commands more MR friendly * revert name change * revert classname change EnvironmentInput -> EnvironmentConfig * list buckets is not region specific * update policy and tags * fix error with auto calculating region * PR Feedback * better functional style for getting primary region * change name of setter * flip logic * fix broken build * convert getPrimaryEntry to match functional style * rename stack to better represent what it is * remove 2 unused files * change language and env arg mapper to reflect that regions is a list * rename region base back to original base name * rename iam role stack to be generic * fixed bad IDE refactor and remove unused methods * IntelliJ failed me in its refactor of CMS_IAM_ROLE -> BASE_IAM_ROLES * pr feed back --- gradle/dependencies.gradle | 20 +- .../com/nike/cerberus/cli/ArgsBuilder.java | 3 + .../com/nike/cerberus/cli/CerberusRunner.java | 6 +- .../cli/EnvironmentConfigToArgsMapper.java | 178 +++--- .../cerberus/command/CerberusCommand.java | 38 +- .../command/cms/CreateCmsCmkCommand.java | 69 --- .../command/core/CreateBaseCommand.java | 66 --- .../command/core/CreateDatabaseCommand.java | 12 + .../core/CreateEdgeDomainRecordCommand.java | 2 +- .../command/core/DeleteStackCommand.java | 12 + ...ificateFilesCommandParametersDelegate.java | 4 +- .../core/InitializeEnvironmentCommand.java | 100 ++++ .../core/UploadCertificateFilesCommand.java | 2 +- .../UploadCertFilesPathValidator.java | 4 +- .../cerberus/domain/EnvironmentMetadata.java | 57 -- .../domain/cloudformation/BaseOutputs.java | 89 --- .../domain/cloudformation/ConfigOutputs.java | 62 +++ ...eParameters.java => ConfigParameters.java} | 47 +- .../cloudformation/DatabaseParameters.java | 10 +- .../cloudformation/DeprecatedBaseOutputs.java | 444 --------------- .../cloudformation/IamRolesOutputs.java | 45 ++ .../environment/CerberusEnvironmentData.java | 70 --- .../environment/CertificateInformation.java | 17 + .../domain/environment/EnvironmentData.java | 143 +++++ .../domain/environment/RegionData.java | 60 ++ .../environment/RegionDeserializer.java | 42 ++ .../environment/RegionKeyDeserializer.java | 17 + .../environment/RegionKeySerializer.java | 33 ++ .../domain/environment/RegionSerializer.java | 40 ++ .../cerberus/domain/environment/Stack.java | 23 +- .../domain/input/EnvironmentConfig.java | 96 ++-- .../domain/input/ManagementServiceInput.java | 46 ++ ...ManagementServiceRegionSpecificInput.java} | 44 +- .../RdsRegionSpecificInput.java} | 19 +- .../RegionSpecificConfigurationInput.java | 62 +++ ...list.java => VpcAccessWhitelistInput.java} | 2 +- .../nike/cerberus/module/CerberusModule.java | 155 ++---- .../cms/CreateCmsClusterOperation.java | 61 +- .../operation/cms/CreateCmsCmkOperation.java | 179 ------ .../composite/CompositeOperation.java | 4 +- .../composite/CreateEnvironmentOperation.java | 17 +- .../composite/DeleteEnvironmentOperation.java | 37 +- ...enerateAndRotateCertificatesOperation.java | 26 +- .../RotateCertificatesOperation.java | 19 +- .../operation/core/CreateBaseOperation.java | 82 --- .../core/CreateDatabaseOperation.java | 57 +- .../core/CreateEdgeDomainRecordOperation.java | 35 +- .../core/CreateLoadBalancerOperation.java | 43 +- .../core/CreateRoute53Operation.java | 65 ++- .../core/CreateSecurityGroupsOperation.java | 38 +- .../operation/core/CreateVpcOperation.java | 42 +- .../operation/core/CreateWafOperation.java | 50 +- .../operation/core/DeleteStackOperation.java | 48 +- .../GenerateCertificateFilesOperation.java | 48 +- .../core/InitializeEnvironmentOperation.java | 124 +++++ .../core/PrintStackInfoOperation.java | 35 +- .../operation/core/RebootCmsOperation.java | 27 +- .../operation/core/UpdateStackOperation.java | 43 +- .../UploadCertificatesFilesOperation.java | 18 +- .../WhitelistCidrForVpcAccessOperation.java | 35 +- .../cerberus/service/AmiTagCheckService.java | 31 +- .../cerberus/service/AutoScalingService.java | 123 +++- .../cerberus/service/AwsClientFactory.java | 98 ++++ .../cerberus/service/CertificateService.java | 33 +- .../service/CloudFormationService.java | 222 ++++---- .../com/nike/cerberus/service/Ec2Service.java | 112 +++- .../cerberus/service/Ec2UserDataService.java | 43 +- .../cerberus/service/EncryptionService.java | 93 ++++ .../service/IdentityManagementService.java | 68 +-- .../cerberus/service/KmsClientFactory.java | 51 -- .../cerberus/service/KmsPolicyGenerator.java | 131 ----- .../com/nike/cerberus/service/KmsService.java | 133 ----- .../nike/cerberus/service/MetricsService.java | 138 ----- .../nike/cerberus/service/Route53Service.java | 22 +- .../com/nike/cerberus/store/ConfigStore.java | 527 ++++++++++-------- .../nike/cerberus/util/CiphertextUtils.java | 88 +++ .../cloudformation/{base.yaml => config.yaml} | 145 ++--- .../resources/cloudformation/database.yaml | 6 +- .../resources/cloudformation/iam-roles.yaml | 58 ++ .../EnvironmentConfigToArgsMapperTest.java | 100 +--- .../environment/EnvironmentDataTest.java | 58 ++ .../service/AmiTagCheckServiceTest.java | 30 +- .../service/AutoScalingServiceTest.java | 31 +- .../service/CertificateServiceTest.java | 82 +++ .../nike/cerberus/service/Ec2ServiceTest.java | 20 +- src/test/resources/environment.yaml | 85 +-- 86 files changed, 2972 insertions(+), 2828 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java delete mode 100644 src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java create mode 100644 src/main/java/com/nike/cerberus/command/core/InitializeEnvironmentCommand.java delete mode 100644 src/main/java/com/nike/cerberus/domain/EnvironmentMetadata.java delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java rename src/main/java/com/nike/cerberus/domain/cloudformation/{DeprecatedBaseParameters.java => ConfigParameters.java} (52%) delete mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java create mode 100644 src/main/java/com/nike/cerberus/domain/cloudformation/IamRolesOutputs.java delete mode 100644 src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/RegionData.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/RegionDeserializer.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/RegionKeyDeserializer.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/RegionKeySerializer.java create mode 100644 src/main/java/com/nike/cerberus/domain/environment/RegionSerializer.java create mode 100644 src/main/java/com/nike/cerberus/domain/input/ManagementServiceInput.java rename src/main/java/com/nike/cerberus/domain/input/{ManagementService.java => ManagementServiceRegionSpecificInput.java} (63%) rename src/main/java/com/nike/cerberus/domain/{cloudformation/BaseParameters.java => input/RdsRegionSpecificInput.java} (62%) create mode 100644 src/main/java/com/nike/cerberus/domain/input/RegionSpecificConfigurationInput.java rename src/main/java/com/nike/cerberus/domain/input/{VpcAccessWhitelist.java => VpcAccessWhitelistInput.java} (96%) delete mode 100644 src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java delete mode 100644 src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java create mode 100644 src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java create mode 100644 src/main/java/com/nike/cerberus/service/AwsClientFactory.java create mode 100644 src/main/java/com/nike/cerberus/service/EncryptionService.java delete mode 100644 src/main/java/com/nike/cerberus/service/KmsClientFactory.java delete mode 100644 src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java delete mode 100644 src/main/java/com/nike/cerberus/service/KmsService.java delete mode 100644 src/main/java/com/nike/cerberus/service/MetricsService.java create mode 100644 src/main/java/com/nike/cerberus/util/CiphertextUtils.java rename src/main/resources/cloudformation/{base.yaml => config.yaml} (52%) create mode 100644 src/main/resources/cloudformation/iam-roles.yaml create mode 100644 src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java create mode 100644 src/test/java/com/nike/cerberus/service/CertificateServiceTest.java diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 7952f1dd..ca56bf97 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -36,9 +36,12 @@ allprojects { compile group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-elasticloadbalancingv2', version: awsSDKVersion + // https://mvnrepository.com/artifact/com.amazonaws/aws-encryption-sdk-java + compile group: 'com.amazonaws', name: 'aws-encryption-sdk-java', version: '1.3.1' + compile 'com.nike:vault-client:1.4.1' compile 'com.squareup.okhttp3:okhttp:3.3.1' - compile 'com.beust:jcommander:1.72' + compile 'com.beust:jcommander:1.55' // newer version up to at least 1.71 have broken variableArity, which breaks create cms cmk command compile 'com.fasterxml.jackson.core:jackson-core:2.7.+' compile 'com.fasterxml.jackson.core:jackson-databind:2.7.+' @@ -46,11 +49,16 @@ allprojects { compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7+' compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.5' compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-joda', version: '2.4.3' + compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jdk8', version: '2.9.2' + compile 'org.slf4j:slf4j-api:1.7.+' compile 'ch.qos.logback:logback-classic:1.1.+' compile 'commons-cli:commons-cli:1.3.1' - compile 'com.google.inject:guice:4.0' + compile 'com.google.inject:guice:4.1.0' + compile group: 'com.google.inject.extensions', name: 'guice-multibindings', version: '4.1.0' + compile group: 'com.google.guava', name: 'guava', version: '23.5-jre' + compile 'com.google.code.findbugs:jsr305:3.0.+' compile 'com.github.tomas-langer:chalk:1.0.2' compile 'commons-net:commons-net:3.4' @@ -76,7 +84,13 @@ allprojects { configurations.all { // check for updates every build - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + resolutionStrategy { + cacheChangingModulesFor 0, 'seconds' + + // aws-encryption-sdk-java brings in a slightly older version with some constants missing + def bouncyVersion = 1.58 + force "org.bouncycastle:bcpkix-jdk15on:$bouncyVersion", "org.bouncycastle:bcprov-ext-jdk15on:$bouncyVersion" + } } } diff --git a/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java b/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java index 808f96d6..20cd350b 100644 --- a/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java +++ b/src/main/java/com/nike/cerberus/cli/ArgsBuilder.java @@ -61,6 +61,9 @@ public ArgsBuilder addAll(List argsToAdd) { * @return The builder */ public ArgsBuilder addOptionUsingPassedArgIfPresent(String optionKey, String optionValue, String [] passedArgs) { + if (passedArgs == null || passedArgs.length == 0) { + return addOption(optionKey, optionValue); + } int index = -1; for (int i = 0; i < passedArgs.length; i++) { if (optionKey.equals(passedArgs[i])) { diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index a2ee037c..2a200689 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -28,14 +28,13 @@ import com.nike.cerberus.command.CerberusCommand; import com.nike.cerberus.command.Command; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; -import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; -import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.InitializeEnvironmentCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; @@ -169,11 +168,10 @@ private void printCliVersion() { * Convenience method for registering all top level commands. */ private void registerAllCommands() { - registerCommand(new CreateBaseCommand()); + registerCommand(new InitializeEnvironmentCommand()); registerCommand(new UploadCertificateFilesCommand()); registerCommand(new CreateCmsConfigCommand()); registerCommand(new CreateCmsClusterCommand()); - registerCommand(new CreateCmsCmkCommand()); registerCommand(new UpdateStackCommand()); registerCommand(new PrintStackInfoCommand()); registerCommand(new PrintAllStackInformationCommand()); diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index a0b38d56..0a39ca62 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -18,12 +18,11 @@ import com.nike.cerberus.command.StackDelegate; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; -import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; -import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.InitializeEnvironmentCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; @@ -38,9 +37,12 @@ import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; -import com.nike.cerberus.domain.input.ManagementService; -import com.nike.cerberus.domain.input.VpcAccessWhitelist; +import com.nike.cerberus.domain.input.ManagementServiceInput; +import com.nike.cerberus.domain.input.ManagementServiceRegionSpecificInput; +import com.nike.cerberus.domain.input.RegionSpecificConfigurationInput; +import com.nike.cerberus.domain.input.VpcAccessWhitelistInput; import org.apache.commons.lang3.StringUtils; +import org.slf4j.LoggerFactory; import java.util.LinkedList; import java.util.List; @@ -84,65 +86,73 @@ public static String[] getArgs(EnvironmentConfig environmentConfig, String[] pas } public static List getArgsForCommand(EnvironmentConfig environmentConfig, String commandName, String[] passedArgs) { + List args = new LinkedList<>(); switch (commandName) { - case CreateBaseCommand.COMMAND_NAME: - return getCreateBaseCommandArgs(environmentConfig); + case InitializeEnvironmentCommand.COMMAND_NAME: + args = getInitializeEnvironmentCommandArgs(environmentConfig); + break; case UploadCertificateFilesCommand.COMMAND_NAME: - return getUploadCertFilesCommandArgs(environmentConfig, passedArgs); + args = getUploadCertFilesCommandArgs(environmentConfig, passedArgs); + break; case CreateCmsClusterCommand.COMMAND_NAME: - return getCreateCmsClusterCommandArgs(environmentConfig); + args = getCreateCmsClusterCommandArgs(environmentConfig); + break; case WhitelistCidrForVpcAccessCommand.COMMAND_NAME: - return getWhitelistCidrForVpcAccessCommandArgs(environmentConfig); + args = getWhitelistCidrForVpcAccessCommandArgs(environmentConfig); + break; case CreateCmsConfigCommand.COMMAND_NAME: - return getCreateCmsConfigCommandArgs(environmentConfig); + args = getCreateCmsConfigCommandArgs(environmentConfig); + break; case UpdateCmsConfigCommand.COMMAND_NAME: - return getCreateCmsConfigCommandArgs(environmentConfig); + args = getCreateCmsConfigCommandArgs(environmentConfig); + break; case CreateVpcCommand.COMMAND_NAME: - return getCreateVpcCommandArgs(environmentConfig); + args = getCreateVpcCommandArgs(environmentConfig); + break; case CreateSecurityGroupsCommand.COMMAND_NAME: - return getCreateSecurityGroupsCommandArgs(environmentConfig); + args = getCreateSecurityGroupsCommandArgs(environmentConfig); + break; case CreateDatabaseCommand.COMMAND_NAME: - return getCreateDatabaseCommandArgs(environmentConfig); + args = getCreateDatabaseCommandArgs(environmentConfig); + break; case CreateLoadBalancerCommand.COMMAND_NAME: - return getCreateLoadBalancerCommandArgs(environmentConfig); + args = getCreateLoadBalancerCommandArgs(environmentConfig); + break; case CreateRoute53Command.COMMAND_NAME: - return getCreateRoute53CommandArgs(environmentConfig); + args = getCreateRoute53CommandArgs(environmentConfig); + break; case CreateWafCommand.COMMAND_NAME: - return getCreateWafCommandArgs(environmentConfig); + args = getCreateWafCommandArgs(environmentConfig); + break; case GenerateCertificateFilesCommand.COMMAND_NAME: - return getGenerateCertificatesCommandArgs(environmentConfig); - case CreateCmsCmkCommand.COMMAND_NAME: - return getCreateCmsCmkCommandArgs(environmentConfig); + args = getGenerateCertificatesCommandArgs(environmentConfig); + break; case CreateEdgeDomainRecordCommand.COMMAND_NAME: - return getCreateEdgeDomainRecordCommandArgs(environmentConfig); + args = getCreateEdgeDomainRecordCommandArgs(environmentConfig); + break; case GenerateAndRotateCertificatesCommand.COMMAND_NAME: - return getGenerateCertificatesCommandArgs(environmentConfig); + args = getGenerateCertificatesCommandArgs(environmentConfig); + break; case RotateCertificatesCommand.COMMAND_NAME: - return getUploadCertFilesCommandArgs(environmentConfig, passedArgs); + args = getUploadCertFilesCommandArgs(environmentConfig, passedArgs); + break; default: - return new LinkedList<>(); + break; } + + LoggerFactory.getLogger("com.nike.cerberus.cli.EnvironmentConfigToArgsMapper") + .debug("Mapped the following args from the provided YAML\n" + String.join("\n", args)); + + return args; } private static List getCreateEdgeDomainRecordCommandArgs(EnvironmentConfig environmentConfig) { - return ArgsBuilder.create() + ArgsBuilder args = ArgsBuilder.create() .addOption(CreateEdgeDomainRecordCommand.BASE_DOMAIN_NAME_LONG_ARG, environmentConfig.getBaseDomainName()) - .addOption(CreateEdgeDomainRecordCommand.HOSTED_ZONE_ID_LONG_ARG, environmentConfig.getHostedZoneId()) - .addOption(CreateEdgeDomainRecordCommand.EDGE_DOMAIN_NAME_OVERRIDE, environmentConfig.getEdgeDomainNameOverride()) - .build(); - } - - private static List getCreateCmsCmkCommandArgs(EnvironmentConfig environmentConfig) { - ArgsBuilder args = ArgsBuilder.create(); + .addOption(CreateEdgeDomainRecordCommand.HOSTED_ZONE_ID_LONG_ARG, environmentConfig.getHostedZoneId()); - if (environmentConfig.getManagementService().getAdditionalEncryptionCmkRegions() != null - && environmentConfig.getManagementService().getAdditionalEncryptionCmkRegions().size() >= 1) { - environmentConfig.getManagementService().getAdditionalEncryptionCmkRegions().forEach(region -> { - args.addOption(CreateCmsCmkCommand.ADDITIONAL_REGIONS_ARG, region); - }); - } else { - throw new RuntimeException(String.format("%s requires at least 1 additional region be specified for high " + - "availability, add at least 1 region to 'encryption-cmk-regions'", CreateCmsCmkCommand.COMMAND_NAME)); + if (StringUtils.isNotBlank(environmentConfig.getEdgeDomainNameOverride())) { + args.addOption(CreateEdgeDomainRecordCommand.EDGE_DOMAIN_NAME_OVERRIDE, environmentConfig.getEdgeDomainNameOverride()); } return args.build(); @@ -150,7 +160,7 @@ private static List getCreateCmsCmkCommandArgs(EnvironmentConfig environ private static List getCreateCmsConfigCommandArgs(EnvironmentConfig environmentConfig) { ArgsBuilder args = ArgsBuilder.create(); - ManagementService managementService = environmentConfig.getManagementService(); + ManagementServiceInput managementService = environmentConfig.getManagementService(); args.addOption(CreateCmsConfigCommand.ADMIN_GROUP_LONG_ARG, managementService.getAdminGroup()); managementService.getProperties().forEach(property -> { args.addOption(CreateCmsConfigCommand.PROPERTY_SHORT_ARG, property); @@ -161,7 +171,7 @@ private static List getCreateCmsConfigCommandArgs(EnvironmentConfig envi private static List getWhitelistCidrForVpcAccessCommandArgs(EnvironmentConfig environmentConfig) { ArgsBuilder args = ArgsBuilder.create(); - VpcAccessWhitelist vpcAccessWhitelist = environmentConfig.getVpcAccessWhitelist(); + VpcAccessWhitelistInput vpcAccessWhitelist = environmentConfig.getVpcAccessWhitelist(); vpcAccessWhitelist.getCidrs().forEach(cidr -> { args.addOption(WhitelistCidrForVpcAccessCommand.CIDR_LONG_ARG, cidr); @@ -174,12 +184,16 @@ private static List getWhitelistCidrForVpcAccessCommandArgs(EnvironmentC return args.build(); } - private static List getCreateCmsClusterCommandArgs(EnvironmentConfig environmentConfig) { + private static List getCreateCmsClusterCommandArgs(EnvironmentConfig config) { + RegionSpecificConfigurationInput primaryRegion = config.getPrimaryRegionConfig(); + ManagementServiceRegionSpecificInput cmsConfig = primaryRegion.getManagementService().orElseThrow(() -> + new RuntimeException("management service config not defined in primary region config")); + return ArgsBuilder.create() - .addOption(StackDelegate.AMI_ID_LONG_ARG, environmentConfig.getManagementService().getAmiId()) - .addOption(StackDelegate.INSTANCE_SIZE_LONG_ARG, environmentConfig.getManagementService().getInstanceSize()) - .addOption(StackDelegate.KEY_PAIR_NAME_LONG_ARG, environmentConfig.getManagementService().getKeyPairName()) - .addAll(getGlobalTags(environmentConfig)) + .addOption(StackDelegate.AMI_ID_LONG_ARG, cmsConfig.getAmiId()) + .addOption(StackDelegate.INSTANCE_SIZE_LONG_ARG, cmsConfig.getInstanceSize()) + .addOption(StackDelegate.KEY_PAIR_NAME_LONG_ARG, cmsConfig.getKeyPairName()) + .addAll(getGlobalTags(config)) .build(); } @@ -195,17 +209,24 @@ private static List getUploadCertFilesCommandArgs(EnvironmentConfig envi return ArgsBuilder.create() .addOptionUsingPassedArgIfPresent( UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG, - environmentConfig.getManagementService().getCertPath(), + environmentConfig.getCertificateDirectory(), passedArgs ) .build(); } - private static List getCreateBaseCommandArgs(EnvironmentConfig config) { - return ArgsBuilder.create() + private static List getInitializeEnvironmentCommandArgs(EnvironmentConfig config) { + ArgsBuilder args = ArgsBuilder.create() .addAll(getGlobalTags(config)) - .addOption(CreateBaseCommand.ADMIN_ROLE_ARN_LONG_ARG, config.getAdminRoleArn()) - .build(); + .addOption(InitializeEnvironmentCommand.ADMIN_ROLE_ARN_LONG_ARG, config.getAdminRoleArn()) + .addOption(InitializeEnvironmentCommand.PRIMARY_REGION, config.getPrimaryRegion()); + + args.addFlag(InitializeEnvironmentCommand.REGION_LONG_ARG); + config.getRegionSpecificConfiguration().forEach((region, data) -> { + args.addFlag(region); + }); + + return args.build(); } private static List getCreateVpcCommandArgs(EnvironmentConfig config) { @@ -217,7 +238,13 @@ private static List getCreateSecurityGroupsCommandArgs(EnvironmentConfig } private static List getCreateDatabaseCommandArgs(EnvironmentConfig config) { - return getGlobalTags(config); + ArgsBuilder args = ArgsBuilder.create(); + if (config.getPrimaryRegionConfig().getRds().isPresent()) { + args.addOption(CreateDatabaseCommand.INSTANCE_CLASS_LONG_ARG, + config.getPrimaryRegionConfig().getRds().get().getSize()); + } + args.addAll(getGlobalTags(config)); + return args.build(); } private static List getCreateLoadBalancerCommandArgs(EnvironmentConfig config) { @@ -232,12 +259,21 @@ private static List getCreateLoadBalancerCommandArgs(EnvironmentConfig c } private static List getCreateRoute53CommandArgs(EnvironmentConfig config) { - return ArgsBuilder.create() + ArgsBuilder args = ArgsBuilder.create() .addOption(CreateRoute53Command.BASE_DOMAIN_NAME_LONG_ARG, config.getBaseDomainName()) - .addOption(CreateRoute53Command.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) - .addOption(CreateRoute53Command.ORIGIN_DOMAIN_NAME_OVERRIDE, config.getOriginDomainNameOverride()) - .addOption(CreateRoute53Command.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE, config.getLoadBalancerDomainNameOverride()) - .build(); + .addOption(CreateRoute53Command.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()); + + + if (StringUtils.isNotBlank(config.getOriginDomainNameOverride())) { + args.addOption(CreateRoute53Command.ORIGIN_DOMAIN_NAME_OVERRIDE, config.getOriginDomainNameOverride()); + } + + if (config.getPrimaryRegionConfig().getLoadBalancerDomainNameOverride().isPresent()) { + args.addOption(CreateRoute53Command.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE, + config.getPrimaryRegionConfig().getLoadBalancerDomainNameOverride().orElse(null)); + } + + return args.build(); } private static List getCreateWafCommandArgs(EnvironmentConfig config) { @@ -247,27 +283,37 @@ private static List getCreateWafCommandArgs(EnvironmentConfig config) { } private static List getGenerateCertificatesCommandArgs(EnvironmentConfig config) { - ArgsBuilder argsBuilder = ArgsBuilder.create() + ArgsBuilder args = ArgsBuilder.create() .addOption(GenerateCertificateFilesCommandParametersDelegate.BASE_DOMAIN_LONG_ARG, config.getBaseDomainName()) - .addOption(GenerateCertificateFilesCommandParametersDelegate.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()) - .addOption(GenerateCertificateFilesCommandParametersDelegate.ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getOriginDomainNameOverride()) - .addOption(GenerateCertificateFilesCommandParametersDelegate.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getLoadBalancerDomainNameOverride()) .addOption(GenerateCertificateFilesCommandParametersDelegate.HOSTED_ZONE_ID_LONG_ARG, config.getHostedZoneId()) .addOption(GenerateCertificateFilesCommandParametersDelegate.ACME_API_LONG_ARG, config.getAcmeApiUrl()) .addOption(GenerateCertificateFilesCommandParametersDelegate.CONTACT_EMAIL_LONG_ARG, config.getAcmeContactEmail()) - .addOption(GenerateCertificateFilesCommandParametersDelegate.CERT_FOLDER_LONG_ARG, config.getLocalFolderToStoreCerts()); + .addOption(GenerateCertificateFilesCommandParametersDelegate.CERT_FOLDER_LONG_ARG, config.getCertificateDirectory()); + + if (StringUtils.isNotBlank(config.getEdgeDomainNameOverride())) { + args.addOption(GenerateCertificateFilesCommandParametersDelegate.EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, config.getEdgeDomainNameOverride()); + } + + if (StringUtils.isNotBlank(config.getOriginDomainNameOverride())) { + args.addOption(CreateRoute53Command.ORIGIN_DOMAIN_NAME_OVERRIDE, config.getOriginDomainNameOverride()); + } + + if (StringUtils.isNotBlank(config.getPrimaryRegionConfig().getLoadBalancerDomainNameOverride().orElse(null))) { + args.addOption(GenerateCertificateFilesCommandParametersDelegate.LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, + config.getPrimaryRegionConfig().getLoadBalancerDomainNameOverride().orElse(null)); + } if (config.isEnableLeCertFix()) { - argsBuilder.addFlag(GenerateCertificateFilesCommandParametersDelegate.ENABLE_LE_CERTFIX_LONG_ARG); + args.addFlag(GenerateCertificateFilesCommandParametersDelegate.ENABLE_LE_CERTFIX_LONG_ARG); } if (config.getAdditionalSubjectNames() != null) { config.getAdditionalSubjectNames().forEach(sn -> { - argsBuilder.addOption(GenerateCertificateFilesCommandParametersDelegate.SUBJECT_ALT_NAME_LONG_ARG, sn); + args.addOption(GenerateCertificateFilesCommandParametersDelegate.SUBJECT_ALT_NAME_LONG_ARG, sn); }); } - return argsBuilder.build(); + return args.build(); } private static String getStackName(String[] passedArgs) { diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index 503cfc8a..bc5d3e6d 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -16,6 +16,7 @@ package com.nike.cerberus.command; +import com.amazonaws.regions.Regions; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; @@ -25,6 +26,8 @@ import com.nike.cerberus.command.validator.EnvironmentNameValidator; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -41,20 +44,35 @@ public class CerberusCommand { public static final String COMMAND_NAME = "cerberus"; + private final Logger log = LoggerFactory.getLogger(getClass()); + private EnvironmentConfig environmentConfig; @Parameter private List parameters = new ArrayList<>(); - @Parameter(names = {"--file", "-f"}, description = "The environment yaml file, allows users to define their environments in a yaml file rather than passing args for each command. This file is required when using composite commands. If file is supplied all args passed for individual commands are ignored and sourced from the yaml, except on commands that require special identifiers like update-stack and --stack-name, which would be used to point to what stack you want to update in the yaml") + @Parameter( + names = {"--file", "-f"}, + description = "The environment yaml file, allows users to define their environments in a yaml file " + + "rather than passing args for each command. This file is required when using composite commands. " + + "If file is supplied all args passed for individual commands are ignored and sourced from the yaml," + + " except on commands that require special identifiers like update-stack and --stack-name, which " + + "would be used to point to what stack you want to update in the yaml") private String file; - @Parameter(names = {"--environment", "--env", "-e"}, - description = "Cerberus environment name to execute against. This is required to be set via 'CERBERUS_CLI_ENV' env var or supplied via command arg", - validateWith = EnvironmentNameValidator.class) + @Parameter( + names = {"--environment", "--env", "-e"}, + description = "Cerberus environment name to execute against. This is required to be set via " + + "'CERBERUS_CLI_ENV' env var or supplied via command arg", + validateWith = EnvironmentNameValidator.class + ) private String environment; - @Parameter(names = {"--region", "-r"}, description = "The AWS region to execute against. This is required to be set via 'CERBERUS_CLI_REGION' env var or supplied via command arg") + @Parameter( + names = {"--region", "-r"}, + description = "The AWS Region to use for looking up the environment config in S3 to load environment state. " + + "This should be one of the enabled regions, if not supplied the CLI will try to default to us-west-2" + ) private String region; @Parameter(names = {"--debug"}, description = "Enables debug output.") @@ -104,7 +122,7 @@ public EnvironmentConfig getEnvironmentConfig() { * 2. If an env yaml was supplied use the env name defined there * 3. If 1 and 2 fail look for value in CERBERUS_CLI_ENV env var */ - public String getEnvironment() { + public String getEnvironmentName() { String commandLinePassedEnvironment = environment; String environmentConfigFileEnvironment = getEnvironmentConfig() == null ? null : getEnvironmentConfig().getEnvironmentName(); String EnvironmentalVarEnvironment = System.getenv("CERBERUS_CLI_ENV"); @@ -114,7 +132,8 @@ public String getEnvironment() { EnvironmentalVarEnvironment; if (StringUtils.isBlank(calculatedEnv)) { - throw new IllegalArgumentException("Failed to determine environment, checked 'CERBERUS_CLI_ENV' env var and -e, --env, --environment command options, options must go before the command"); + throw new RuntimeException("Failed to determine environment, checked 'CERBERUS_CLI_ENV' env var and -e, --env, --environment command options, Will attempt to use the AWS Default region (us-west-2"); + } return calculatedEnv; @@ -126,7 +145,7 @@ public String getEnvironment() { * 2. If an env yaml was supplied use the region defined there * 3. If 1 and 2 fail look for value in CERBERUS_CLI_REGION env var */ - public String getRegion() { + public String getConfigRegion() { String commandLinePassedRegion = region; String environmentConfigFileRegion = getEnvironmentConfig() == null ? null : getEnvironmentConfig().getPrimaryRegion(); String EnvironmentalVarRegion = System.getenv("CERBERUS_CLI_REGION"); @@ -136,7 +155,8 @@ public String getRegion() { EnvironmentalVarRegion; if (StringUtils.isBlank(calculatedRegion)) { - throw new IllegalArgumentException("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options, options must go before the command"); + log.error("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options, options must go before the command"); + calculatedRegion = Regions.DEFAULT_REGION.toString(); } return calculatedRegion; diff --git a/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java b/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java deleted file mode 100644 index 1d526d7a..00000000 --- a/src/main/java/com/nike/cerberus/command/cms/CreateCmsCmkCommand.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2017 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.cms; - -import com.beust.jcommander.Parameter; -import com.beust.jcommander.Parameters; -import com.nike.cerberus.command.Command; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.cms.CreateCmsCmkOperation; - -import java.util.List; - -import static com.nike.cerberus.command.cms.CreateCmsCmkCommand.COMMAND_NAME; - -/** - * Command to create the CMS cluster. - */ -@Parameters(commandNames = COMMAND_NAME, commandDescription = "Updates the CMS config.") -public class CreateCmsCmkCommand implements Command { - - public static final String COMMAND_NAME = "create-cms-cmk"; - public static final String ADDITIONAL_REGIONS_ARG = "--additional-regions"; - public static final String ROTATE_ARG = "--rotate"; - - @Parameter(names = ADDITIONAL_REGIONS_ARG, - description = "Additional regions to create the CMS CMK in. At least one additional region, other than the primary region where Cerberus will be running, is required.", - variableArity = true, - required = true - ) - private List additionalRegions; - - @Parameter(names = ROTATE_ARG, - description = "Manually rotates the CMKs while leaving the old CMKs in place so they can still be used. " + - "This is generally unnecessary since AWS will automatically rotate keys previously created with this command. " - ) - private boolean rotate = false; - - public List getAdditionalRegions() { - return additionalRegions; - } - - public boolean isRotate() { - return rotate; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateCmsCmkOperation.class; - } -} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java deleted file mode 100644 index a39c8846..00000000 --- a/src/main/java/com/nike/cerberus/command/core/CreateBaseCommand.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2017 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.TagParametersDelegate; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.CreateBaseOperation; - -import static com.nike.cerberus.command.core.CreateBaseCommand.COMMAND_NAME; - -/** - * Command for creating the base components for Cerberus. - */ -@Parameters(commandNames = COMMAND_NAME, - commandDescription = "Create the IAM roles, KMS keys, and S3 buckets for Cerberus") -public class CreateBaseCommand implements Command { - - public static final String COMMAND_NAME = "create-base"; - - public static final String ADMIN_ROLE_ARN_LONG_ARG = "--admin-role-arn"; - - @Parameter(names = ADMIN_ROLE_ARN_LONG_ARG, - description = "A IAM role ARN that will be given elevated privileges for the KMS CMK created.", - required = true) - private String adminRoleArn; - - @ParametersDelegate - private TagParametersDelegate tagParameters = new TagParametersDelegate(); - - public String getAdminRoleArn() { - return adminRoleArn; - } - - public TagParametersDelegate getTagsDelegate() { - return tagParameters; - } - - @Override - public String getCommandName() { - return COMMAND_NAME; - } - - @Override - public Class> getOperationClass() { - return CreateBaseOperation.class; - } - -} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java index af3534ec..13a19f2b 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java @@ -16,6 +16,7 @@ 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; @@ -34,6 +35,8 @@ public class CreateDatabaseCommand implements Command { public static final String COMMAND_NAME = "create-database"; + public static final String INSTANCE_CLASS_LONG_ARG = "--instance-class"; + @ParametersDelegate private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); @@ -41,6 +44,15 @@ public TagParametersDelegate getTagsDelegate() { return tagsDelegate; } + @Parameter(names = INSTANCE_CLASS_LONG_ARG, + description = "The Instance Class to use, defaults to db.r3.large" + ) + private String instanceClass; + + public String getInstanceClass() { + return instanceClass; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java b/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java index 60507008..c36fc4b0 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/CreateEdgeDomainRecordCommand.java @@ -37,7 +37,7 @@ public class CreateEdgeDomainRecordCommand implements Command { public static final String HOSTED_ZONE_ID_LONG_ARG = "--hosted-zone-id"; - public static final String EDGE_DOMAIN_NAME_OVERRIDE = "edge-domain-name-override"; + public static final String EDGE_DOMAIN_NAME_OVERRIDE = "--edge-domain-name-override"; @Parameter(names = BASE_DOMAIN_NAME_LONG_ARG, description = "The base domain name for Cerberus (e.g. url: https://env.cerberus.example.com => base hostname: cerberus.example.com)", diff --git a/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java b/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java index 4929038c..b9ef64a6 100644 --- a/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/DeleteStackCommand.java @@ -38,6 +38,8 @@ public class DeleteStackCommand implements Command { public static final String STACK_NAME_LONG_ARG = "--stack-name"; + public static final String REGION_LONG_ARG = "--region"; + @Parameter( names = {STACK_NAME_LONG_ARG}, required = true, @@ -45,10 +47,20 @@ public class DeleteStackCommand implements Command { converter = StackConverter.class) private Stack stack; + @Parameter( + names = {REGION_LONG_ARG}, + description = "Region to delete stack in, defaults to primary region" + ) + private String region; + public Stack getStack() { return stack; } + public String getRegion() { + return region; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java index 2a91462e..ff396601 100644 --- a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java @@ -138,7 +138,7 @@ public class GenerateCertificateFilesCommandParametersDelegate { }, description = "Flag to supply when running command in an environment without TTY, such as a build job" ) - private boolean tty = false; + private boolean isNoTty = false; @Parameter( names = { @@ -190,7 +190,7 @@ public String getContactEmail() { } public boolean isTty() { - return tty; + return ! isNoTty; } public boolean isAutoAcceptAcmeTos() { diff --git a/src/main/java/com/nike/cerberus/command/core/InitializeEnvironmentCommand.java b/src/main/java/com/nike/cerberus/command/core/InitializeEnvironmentCommand.java new file mode 100644 index 00000000..2fe117b6 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/core/InitializeEnvironmentCommand.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 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.TagParametersDelegate; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.core.InitializeEnvironmentOperation; + +import java.util.List; + +import static com.nike.cerberus.command.core.InitializeEnvironmentCommand.COMMAND_NAME; + +/** + * Command for creating the base components for Cerberus. + */ +@Parameters(commandNames = COMMAND_NAME, + commandDescription = "Create the IAM roles, KMS keys, and S3 buckets and base config files for needed to " + + "bootstrap a Cerberus environment and store env infrastructure state") +public class InitializeEnvironmentCommand implements Command { + + public static final String COMMAND_NAME = "init-env"; + public static final String ADMIN_ROLE_ARN_LONG_ARG = "--admin-role-arn"; + public static final String REGION_LONG_ARG = "--regions"; + public static final String PRIMARY_REGION = "--primary-region"; + + @Parameter( + names = ADMIN_ROLE_ARN_LONG_ARG, + description = "An IAM role ARN that will be given elevated privileges for the KMS CMKs created.", + required = true + ) + private String adminRoleArn; + + @Parameter( + names = REGION_LONG_ARG, + description = "The regions to use with the Cerberus environment, you must declare at least 2 regions, " + + "The CLI and the Management service use the AWS Encryption SDK that supports encrypting payloads " + + "with data keys from multiple regions so that the data can be decrypted in other regions in case of " + + "region outages. We require at least 2 regions to ensure high availability of config and secure data", + variableArity = true, + required = true + ) + private List regions; + + @Parameter( + names = PRIMARY_REGION, + description = "The primary region is the region that will serve traffic by default, this region will " + + "have the necessary infrastructure stood up to serve traffic and store data. " + + "The primary region must be one of the --region args passed", + required = true + ) + private String primaryRegion; + + @ParametersDelegate + private TagParametersDelegate tagParameters = new TagParametersDelegate(); + + public String getAdminRoleArn() { + return adminRoleArn; + } + + public List getRegions() { + return regions; + } + + public String getPrimaryRegion() { + return primaryRegion; + } + + public TagParametersDelegate getTagsDelegate() { + return tagParameters; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return InitializeEnvironmentOperation.class; + } + +} diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java index c052778d..91f6f26d 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java @@ -33,7 +33,7 @@ public class UploadCertificateFilesCommand implements Command { public static final String COMMAND_NAME = "upload-certificate-files"; @ParametersDelegate - private final UploadCertificateFilesCommandParametersDelegate uploadCertificatesPathParametersDelegate = + private UploadCertificateFilesCommandParametersDelegate uploadCertificatesPathParametersDelegate = new UploadCertificateFilesCommandParametersDelegate(); public UploadCertificateFilesCommandParametersDelegate getUploadCertificatesPathParametersDelegate() { diff --git a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java index c226e106..d6bdd655 100644 --- a/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java +++ b/src/main/java/com/nike/cerberus/command/validator/UploadCertFilesPathValidator.java @@ -37,11 +37,11 @@ public void validate(final String name, final Path value) throws ParameterExcept final File certDirectory = value.toFile(); if (!certDirectory.canRead()) { - throw new ParameterException("Specified path is not readable."); + throw new ParameterException(String.format("Specified path: %s is not readable.", certDirectory.getAbsolutePath())); } if (!certDirectory.isDirectory()) { - throw new ParameterException("Specified path is not a directory."); + throw new ParameterException(String.format("Specified path: %s is not a directory.", certDirectory.getAbsolutePath())); } } } diff --git a/src/main/java/com/nike/cerberus/domain/EnvironmentMetadata.java b/src/main/java/com/nike/cerberus/domain/EnvironmentMetadata.java deleted file mode 100644 index 486a95fc..00000000 --- a/src/main/java/com/nike/cerberus/domain/EnvironmentMetadata.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2016 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; - -import com.amazonaws.regions.Regions; - -/** - * Environment metadata. Everything but the bucket name is immutable. Bucket name isn't known immediately - * if running operation is provisioning it! - */ -public class EnvironmentMetadata { - - private final String name; - - private final String regionName; - - private String bucketName; - - public EnvironmentMetadata(final String name, final String regionName) { - this.name = name; - this.regionName = regionName; - } - - public String getName() { - return name; - } - - public String getRegionName() { - return regionName; - } - - public Regions getRegions() { - return Regions.fromName(regionName); - } - - public String getBucketName() { - return bucketName; - } - - public void setBucketName(String bucketName) { - this.bucketName = bucketName; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java deleted file mode 100644 index c9fb582b..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseOutputs.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2017 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 base stack outputs. - */ -public class BaseOutputs { - - private String configBucketDomainName; - - private String configBucketName; - - private String configFileKeyId; - - private String cmsIamRoleArn; - - private String cmsIamRoleName; - - private String cmsKmsPolicyId; - - public String getConfigBucketDomainName() { - return configBucketDomainName; - } - - public BaseOutputs setConfigBucketDomainName(String configBucketDomainName) { - this.configBucketDomainName = configBucketDomainName; - return this; - } - - public String getConfigBucketName() { - return configBucketName; - } - - public BaseOutputs setConfigBucketName(String configBucketName) { - this.configBucketName = configBucketName; - return this; - } - - public String getConfigFileKeyId() { - return configFileKeyId; - } - - public BaseOutputs setConfigFileKeyId(String configFileKeyId) { - this.configFileKeyId = configFileKeyId; - return this; - } - - public String getCmsIamRoleArn() { - return cmsIamRoleArn; - } - - public BaseOutputs setCmsIamRoleArn(String cmsIamRoleArn) { - this.cmsIamRoleArn = cmsIamRoleArn; - return this; - } - - public String getCmsIamRoleName() { - return cmsIamRoleName; - } - - public BaseOutputs setCmsIamRoleName(String cmsIamRoleName) { - this.cmsIamRoleName = cmsIamRoleName; - return this; - } - - public String getCmsKmsPolicyId() { - return cmsKmsPolicyId; - } - - public BaseOutputs setCmsKmsPolicyId(String cmsKmsPolicyId) { - this.cmsKmsPolicyId = cmsKmsPolicyId; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java new file mode 100644 index 00000000..02d4b3a3 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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; + +public class ConfigOutputs { + + private String configBucketDomainName; + + private String configBucketName; + + private String environmentDataKmsCmkArn; + + private String cmsSecureDataKmsCmkArn; + + public String getConfigBucketDomainName() { + return configBucketDomainName; + } + + public ConfigOutputs setConfigBucketDomainName(String configBucketDomainName) { + this.configBucketDomainName = configBucketDomainName; + return this; + } + + public String getConfigBucketName() { + return configBucketName; + } + + public ConfigOutputs setConfigBucketName(String configBucketName) { + this.configBucketName = configBucketName; + return this; + } + + public String getEnvironmentDataKmsCmkArn() { + return environmentDataKmsCmkArn; + } + + public void setEnvironmentDataKmsCmkArn(String environmentDataKmsCmkArn) { + this.environmentDataKmsCmkArn = environmentDataKmsCmkArn; + } + + public String getCmsSecureDataKmsCmkArn() { + return cmsSecureDataKmsCmkArn; + } + + public void setCmsSecureDataKmsCmkArn(String cmsSecureDataKmsCmkArn) { + this.cmsSecureDataKmsCmkArn = cmsSecureDataKmsCmkArn; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigParameters.java similarity index 52% rename from src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java rename to src/main/java/com/nike/cerberus/domain/cloudformation/ConfigParameters.java index 31fc42ed..97764400 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,54 +18,39 @@ /** * Represents the base stack inputs. - * - * @deprecated TODO: remove this class when old backup and restore is removed - * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments */ -@Deprecated -public class DeprecatedBaseParameters { +public class ConfigParameters { private String accountAdminArn; - private String az1; - - private String az2; + private String cmsIamRoleArn; - private String az3; - - public String getAz1() { - return az1; - } - - public DeprecatedBaseParameters setAz1(String az1) { - this.az1 = az1; - return this; - } + private String environmentName; - public String getAz2() { - return az2; + public String getAccountAdminArn() { + return accountAdminArn; } - public DeprecatedBaseParameters setAz2(String az2) { - this.az2 = az2; + public ConfigParameters setAccountAdminArn(String accountAdminArn) { + this.accountAdminArn = accountAdminArn; return this; } - public String getAz3() { - return az3; + public String getCmsIamRoleArn() { + return cmsIamRoleArn; } - public DeprecatedBaseParameters setAz3(String az3) { - this.az3 = az3; + public ConfigParameters setCmsIamRoleArn(String cmsIamRoleArn) { + this.cmsIamRoleArn = cmsIamRoleArn; return this; } - public String getAccountAdminArn() { - return accountAdminArn; + public String getEnvironmentName() { + return environmentName; } - public DeprecatedBaseParameters setAccountAdminArn(String accountAdminArn) { - this.accountAdminArn = accountAdminArn; + public ConfigParameters setEnvironmentName(String environmentName) { + this.environmentName = environmentName; return this; } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java index 731240a1..aa064b3e 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java @@ -27,7 +27,7 @@ public class DatabaseParameters { private String cmsDbInstanceAz3; - private String cmsDbInstanceSize; + private String cmsDbInstanceClass; private String cmsDbMasterPassword; @@ -70,12 +70,12 @@ public DatabaseParameters setCmsDbInstanceAz3(String cmsDbInstanceAz3) { return this; } - public String getCmsDbInstanceSize() { - return cmsDbInstanceSize; + public String getCmsDbInstanceClass() { + return cmsDbInstanceClass; } - public DatabaseParameters setCmsDbInstanceSize(String cmsDbInstanceSize) { - this.cmsDbInstanceSize = cmsDbInstanceSize; + public DatabaseParameters setCmsDbInstanceClass(String cmsDbInstanceClass) { + this.cmsDbInstanceClass = cmsDbInstanceClass; return this; } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java deleted file mode 100644 index 5d95aeb0..00000000 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DeprecatedBaseOutputs.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (c) 2016 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 base stack outputs. - * - * @deprecated TODO: remove this class when old backup and restore is removed - * use {@link com.nike.cerberus.domain.cloudformation.BaseOutputs} for new environments - */ -@Deprecated -public class DeprecatedBaseOutputs { - - private String vpcId; - - private String gatewayIamRoleArn; - - private String cmsIamRoleArn; - - private String consulIamRoleArn; - - private String vaultIamRoleArn; - - private String cloudFrontLogProcessorLambdaIamRoleArn; - - private String gatewayInstanceProfileName; - - private String cmsInstanceProfileName; - - private String consulInstanceProfileName; - - private String vaultInstanceProfileName; - - private String toolsIngressSgId; - - private String gatewayElbSgId; - - private String gatewayServerSgId; - - private String cmsElbSgId; - - private String cmsSgId; - - private String cmsDbSgId; - - private String vaultServerElbSgId; - - private String vaultClientSgId; - - private String vaultServerSgId; - - private String consulClientSgId; - - private String consulServerSgId; - - private String configFileKeyId; - - private String dashboardBucketName; - - private String dashboardBucketWebsiteUrl; - - private String configBucketName; - - private String configBucketDomainName; - - private String cmsDbId; - - private String cmsDbAddress; - - private String cmsDbPort; - - private String cmsDbJdbcConnectionString; - - private String vpcHostedZoneId; - - private String vpcSubnetIdForAz1; - - private String vpcSubnetIdForAz2; - - private String vpcSubnetIdForAz3; - - private String cmsKmsPolicyId; - - private String subnetCidrBlockForAz1; - - private String subnetCidrBlockForAz2; - - private String subnetCidrBlockForAz3; - - public String getVpcId() { - return vpcId; - } - - public DeprecatedBaseOutputs setVpcId(String vpcId) { - this.vpcId = vpcId; - return this; - } - - public String getGatewayIamRoleArn() { - return gatewayIamRoleArn; - } - - public DeprecatedBaseOutputs setGatewayIamRoleArn(String gatewayIamRoleArn) { - this.gatewayIamRoleArn = gatewayIamRoleArn; - return this; - } - - public String getCmsIamRoleArn() { - return cmsIamRoleArn; - } - - public DeprecatedBaseOutputs setCmsIamRoleArn(String cmsIamRoleArn) { - this.cmsIamRoleArn = cmsIamRoleArn; - return this; - } - - public String getConsulIamRoleArn() { - return consulIamRoleArn; - } - - public DeprecatedBaseOutputs setConsulIamRoleArn(String consulIamRoleArn) { - this.consulIamRoleArn = consulIamRoleArn; - return this; - } - - public String getVaultIamRoleArn() { - return vaultIamRoleArn; - } - - public DeprecatedBaseOutputs setVaultIamRoleArn(String vaultIamRoleArn) { - this.vaultIamRoleArn = vaultIamRoleArn; - return this; - } - - public String getCloudFrontLogProcessorLambdaIamRoleArn() { - return cloudFrontLogProcessorLambdaIamRoleArn; - } - - public void setCloudFrontLogProcessorLambdaIamRoleArn(String cloudFrontLogProcessorLambdaIamRoleArn) { - this.cloudFrontLogProcessorLambdaIamRoleArn = cloudFrontLogProcessorLambdaIamRoleArn; - } - - public String getGatewayInstanceProfileName() { - return gatewayInstanceProfileName; - } - - public DeprecatedBaseOutputs setGatewayInstanceProfileName(String gatewayInstanceProfileName) { - this.gatewayInstanceProfileName = gatewayInstanceProfileName; - return this; - } - - public String getCmsInstanceProfileName() { - return cmsInstanceProfileName; - } - - public DeprecatedBaseOutputs setCmsInstanceProfileName(String cmsInstanceProfileName) { - this.cmsInstanceProfileName = cmsInstanceProfileName; - return this; - } - - public String getConsulInstanceProfileName() { - return consulInstanceProfileName; - } - - public DeprecatedBaseOutputs setConsulInstanceProfileName(String consulInstanceProfileName) { - this.consulInstanceProfileName = consulInstanceProfileName; - return this; - } - - public String getVaultInstanceProfileName() { - return vaultInstanceProfileName; - } - - public DeprecatedBaseOutputs setVaultInstanceProfileName(String vaultInstanceProfileName) { - this.vaultInstanceProfileName = vaultInstanceProfileName; - return this; - } - - public String getToolsIngressSgId() { - return toolsIngressSgId; - } - - public DeprecatedBaseOutputs setToolsIngressSgId(String toolsIngressSgId) { - this.toolsIngressSgId = toolsIngressSgId; - return this; - } - - public String getGatewayElbSgId() { - return gatewayElbSgId; - } - - public DeprecatedBaseOutputs setGatewayElbSgId(String gatewayElbSgId) { - this.gatewayElbSgId = gatewayElbSgId; - return this; - } - - public String getGatewayServerSgId() { - return gatewayServerSgId; - } - - public DeprecatedBaseOutputs setGatewayServerSgId(String gatewayServerSgId) { - this.gatewayServerSgId = gatewayServerSgId; - return this; - } - - public String getCmsElbSgId() { - return cmsElbSgId; - } - - public DeprecatedBaseOutputs setCmsElbSgId(String cmsElbSgId) { - this.cmsElbSgId = cmsElbSgId; - return this; - } - - public String getCmsSgId() { - return cmsSgId; - } - - public DeprecatedBaseOutputs setCmsSgId(String cmsSgId) { - this.cmsSgId = cmsSgId; - return this; - } - - public String getCmsDbSgId() { - return cmsDbSgId; - } - - public DeprecatedBaseOutputs setCmsDbSgId(String cmsDbSgId) { - this.cmsDbSgId = cmsDbSgId; - return this; - } - - public String getVaultServerElbSgId() { - return vaultServerElbSgId; - } - - public DeprecatedBaseOutputs setVaultServerElbSgId(String vaultServerElbSgId) { - this.vaultServerElbSgId = vaultServerElbSgId; - return this; - } - - public String getVaultClientSgId() { - return vaultClientSgId; - } - - public DeprecatedBaseOutputs setVaultClientSgId(String vaultClientSgId) { - this.vaultClientSgId = vaultClientSgId; - return this; - } - - public String getVaultServerSgId() { - return vaultServerSgId; - } - - public DeprecatedBaseOutputs setVaultServerSgId(String vaultServerSgId) { - this.vaultServerSgId = vaultServerSgId; - return this; - } - - public String getConsulClientSgId() { - return consulClientSgId; - } - - public DeprecatedBaseOutputs setConsulClientSgId(String consulClientSgId) { - this.consulClientSgId = consulClientSgId; - return this; - } - - public String getConsulServerSgId() { - return consulServerSgId; - } - - public DeprecatedBaseOutputs setConsulServerSgId(String consulServerSgId) { - this.consulServerSgId = consulServerSgId; - return this; - } - - public String getConfigFileKeyId() { - return configFileKeyId; - } - - public DeprecatedBaseOutputs setConfigFileKeyId(String configFileKeyId) { - this.configFileKeyId = configFileKeyId; - return this; - } - - public String getDashboardBucketName() { - return dashboardBucketName; - } - - public DeprecatedBaseOutputs setDashboardBucketName(String dashboardBucketName) { - this.dashboardBucketName = dashboardBucketName; - return this; - } - - public String getDashboardBucketWebsiteUrl() { - return dashboardBucketWebsiteUrl; - } - - public DeprecatedBaseOutputs setDashboardBucketWebsiteUrl(String dashboardBucketWebsiteUrl) { - this.dashboardBucketWebsiteUrl = dashboardBucketWebsiteUrl; - return this; - } - - public String getConfigBucketName() { - return configBucketName; - } - - public DeprecatedBaseOutputs setConfigBucketName(String configBucketName) { - this.configBucketName = configBucketName; - return this; - } - - public String getConfigBucketDomainName() { - return configBucketDomainName; - } - - public DeprecatedBaseOutputs setConfigBucketDomainName(String configBucketDomainName) { - this.configBucketDomainName = configBucketDomainName; - return this; - } - - public String getCmsDbId() { - return cmsDbId; - } - - public DeprecatedBaseOutputs setCmsDbId(String cmsDbId) { - this.cmsDbId = cmsDbId; - return this; - } - - public String getCmsDbAddress() { - return cmsDbAddress; - } - - public DeprecatedBaseOutputs setCmsDbAddress(String cmsDbAddress) { - this.cmsDbAddress = cmsDbAddress; - return this; - } - - public String getCmsDbPort() { - return cmsDbPort; - } - - public DeprecatedBaseOutputs setCmsDbPort(String cmsDbPort) { - this.cmsDbPort = cmsDbPort; - return this; - } - - public String getCmsDbJdbcConnectionString() { - return cmsDbJdbcConnectionString; - } - - public DeprecatedBaseOutputs setCmsDbJdbcConnectionString(String cmsDbJdbcConnectionString) { - this.cmsDbJdbcConnectionString = cmsDbJdbcConnectionString; - return this; - } - - public String getVpcHostedZoneId() { - return vpcHostedZoneId; - } - - public DeprecatedBaseOutputs setVpcHostedZoneId(String vpcHostedZoneId) { - this.vpcHostedZoneId = vpcHostedZoneId; - return this; - } - - public String getVpcSubnetIdForAz1() { - return vpcSubnetIdForAz1; - } - - public DeprecatedBaseOutputs setVpcSubnetIdForAz1(String vpcSubnetIdForAz1) { - this.vpcSubnetIdForAz1 = vpcSubnetIdForAz1; - return this; - } - - public String getVpcSubnetIdForAz2() { - return vpcSubnetIdForAz2; - } - - public DeprecatedBaseOutputs setVpcSubnetIdForAz2(String vpcSubnetIdForAz2) { - this.vpcSubnetIdForAz2 = vpcSubnetIdForAz2; - return this; - } - - public String getVpcSubnetIdForAz3() { - return vpcSubnetIdForAz3; - } - - public DeprecatedBaseOutputs setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { - this.vpcSubnetIdForAz3 = vpcSubnetIdForAz3; - return this; - } - - public String getCmsKmsPolicyId() { - return cmsKmsPolicyId; - } - - public DeprecatedBaseOutputs setCmsKmsPolicyId(String cmsKmsPolicyId) { - this.cmsKmsPolicyId = cmsKmsPolicyId; - return this; - } - - public String getSubnetCidrBlockForAz1() { - return subnetCidrBlockForAz1; - } - - public DeprecatedBaseOutputs setSubnetCidrBlockForAz1(String subnetCidrBlockForAz1) { - this.subnetCidrBlockForAz1 = subnetCidrBlockForAz1; - return this; - } - - public String getSubnetCidrBlockForAz2() { - return subnetCidrBlockForAz2; - } - - public DeprecatedBaseOutputs setSubnetCidrBlockForAz2(String subnetCidrBlockForAz2) { - this.subnetCidrBlockForAz2 = subnetCidrBlockForAz2; - return this; - } - - public String getSubnetCidrBlockForAz3() { - return subnetCidrBlockForAz3; - } - - public DeprecatedBaseOutputs setSubnetCidrBlockForAz3(String subnetCidrBlockForAz3) { - this.subnetCidrBlockForAz3 = subnetCidrBlockForAz3; - return this; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/IamRolesOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/IamRolesOutputs.java new file mode 100644 index 00000000..7a2eab1b --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/IamRolesOutputs.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 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 base stack outputs. + */ +public class IamRolesOutputs { + + private String cmsIamRoleArn; + + private String cmsIamRoleName; + + public String getCmsIamRoleArn() { + return cmsIamRoleArn; + } + + public IamRolesOutputs setCmsIamRoleArn(String cmsIamRoleArn) { + this.cmsIamRoleArn = cmsIamRoleArn; + return this; + } + + public String getCmsIamRoleName() { + return cmsIamRoleName; + } + + public IamRolesOutputs setCmsIamRoleName(String cmsIamRoleName) { + this.cmsIamRoleName = cmsIamRoleName; + return this; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java b/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java deleted file mode 100644 index 5ddff789..00000000 --- a/src/main/java/com/nike/cerberus/domain/environment/CerberusEnvironmentData.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016 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.environment; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Container for data needed by Cerberus. - */ -public class CerberusEnvironmentData { - - private String databasePassword; - - private LinkedList certificateInfoList; - - private String metricsTopicArn; - - public String getDatabasePassword() { - return databasePassword; - } - - public void setDatabasePassword(String databasePassword) { - this.databasePassword = databasePassword; - } - - public LinkedList getCertificateData() { - if (certificateInfoList == null) { - certificateInfoList = new LinkedList<>(); - } - return certificateInfoList; - } - - public void addNewCertificateData(CertificateInformation certificateData) { - getCertificateData().add(certificateData); - } - - public void removeCertificateInformationByName(String identityManagementCertificateName) { - List certificateInformationList = getCertificateData(); - List certsInfoToBeDeleted = certificateInformationList.stream() - .filter(certificateInformation -> - certificateInformation.getCertificateName().equals(identityManagementCertificateName)) - .collect(Collectors.toList()); - - certificateInformationList.removeAll(certsInfoToBeDeleted); - } - - public String getMetricsTopicArn() { - return metricsTopicArn; - } - - public void setMetricsTopicArn(String metricsTopicArn) { - this.metricsTopicArn = metricsTopicArn; - } -} diff --git a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java index a5459a69..30a8bf7f 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java +++ b/src/main/java/com/nike/cerberus/domain/environment/CertificateInformation.java @@ -25,6 +25,7 @@ public class CertificateInformation { private String certificateName; + private String certificateId; private String identityManagementCertificateArn; private DateTime notBefore; private DateTime notAfter; @@ -40,6 +41,14 @@ public void setCertificateName(String certificateName) { this.certificateName = certificateName; } + public String getCertificateId() { + return certificateId; + } + + public void setCertificateId(String certificateId) { + this.certificateId = certificateId; + } + public DateTime getNotBefore() { return notBefore; } @@ -93,6 +102,7 @@ public String toString() { DateTimeFormatter fmt = DateTimeFormat.fullDateTime(); return "CertificateInformation{" + "certificateName='" + certificateName + '\'' + + ", certificateId='" + certificateId + '\'' + ", identityManagementCertificateArn='" + identityManagementCertificateArn + '\'' + ", notBefore=" + fmt.print(notBefore) + ", notAfter=" + fmt.print(notAfter) + @@ -104,6 +114,7 @@ public String toString() { public static final class Builder { private String certificateName; + private String certificateId; private String identityManagementCertificateArn; private DateTime notBefore; private DateTime notAfter; @@ -123,6 +134,11 @@ public Builder withCertificateName(String certificateName) { return this; } + public Builder withCertificateId(String certificateId) { + this.certificateId = certificateId; + return this; + } + public Builder withIdentityManagementCertificateArn(String identityManagementCertificateArn) { this.identityManagementCertificateArn = identityManagementCertificateArn; return this; @@ -156,6 +172,7 @@ public Builder withSubjectAlternateNames(List subjectAlternateNames) { public CertificateInformation build() { CertificateInformation certificateInformation = new CertificateInformation(); certificateInformation.setCertificateName(certificateName); + certificateInformation.setCertificateId(certificateId); certificateInformation.setIdentityManagementCertificateArn(identityManagementCertificateArn); certificateInformation.setNotBefore(notBefore); certificateInformation.setNotAfter(notAfter); diff --git a/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java b/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java new file mode 100644 index 00000000..f2e8e7f4 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016 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.environment; + +import com.amazonaws.regions.Regions; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Container for data needed by Cerberus. + */ +@Singleton +public class EnvironmentData { + + private String environmentName; + + private String adminIamRoleArn; + + private String cmsIamRoleArn; + + private String rootIamRoleArn; + + private String databasePassword; + + private LinkedList certificateInfoList = new LinkedList<>(); + + private Map regionData = new HashMap<>();; + + public String getEnvironmentName() { + return environmentName; + } + + public void setEnvironmentName(String environmentName) { + this.environmentName = environmentName; + } + + public String getAdminIamRoleArn() { + return adminIamRoleArn; + } + + public void setAdminIamRoleArn(String adminIamRoleArn) { + this.adminIamRoleArn = adminIamRoleArn; + } + + public String getRootIamRoleArn() { + return rootIamRoleArn; + } + + public void setRootIamRoleArn(String rootIamRoleArn) { + this.rootIamRoleArn = rootIamRoleArn; + } + + public String getCmsIamRoleArn() { + return cmsIamRoleArn; + } + + public void setCmsIamRoleArn(String cmsIamRoleArn) { + this.cmsIamRoleArn = cmsIamRoleArn; + } + + public String getDatabasePassword() { + return databasePassword; + } + + public void setDatabasePassword(String databasePassword) { + this.databasePassword = databasePassword; + } + + public LinkedList getCertificateData() { + return certificateInfoList; + } + + public void addNewCertificateData(CertificateInformation certificateData) { + getCertificateData().add(certificateData); + } + + public void removeCertificateInformationByName(String identityManagementCertificateName) { + List certsInfoToBeDeleted = certificateInfoList.stream() + .filter(certificateInformation -> + certificateInformation.getCertificateName().equals(identityManagementCertificateName)) + .collect(Collectors.toList()); + + certificateInfoList.removeAll(certsInfoToBeDeleted); + } + + public Map getRegionData() { + return regionData; + } + + public void addRegionData(Regions region, RegionData rData) { + regionData.put(region, rData); + } + + /** + * @return List of all the regions that are configured for storing config. + */ + @JsonIgnore + public List getConfigRegions() { + List configRegions = new LinkedList<>(); + regionData.forEach((region, rData) -> { + if (rData.getEnvironmentDataKmsCmkArn().isPresent() + && rData.getConfigBucket().isPresent()) { + configRegions.add(region); + } + }); + return configRegions; + } + + @JsonIgnore + public Regions getPrimaryRegion() { + return regionData.entrySet().stream() + .filter(entry -> entry.getValue().isPrimary()).findFirst() + .orElseThrow(() -> new RuntimeException("Failed to find primary region in region specific state")) + .getKey(); + } + + public List getCmsCmkArns() { + return regionData.entrySet().stream() + .filter(entry -> entry.getValue().getCmsSecureDataKmsCmkArn().isPresent()) + .map(entry -> entry.getValue().getCmsSecureDataKmsCmkArn().get()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/RegionData.java b/src/main/java/com/nike/cerberus/domain/environment/RegionData.java new file mode 100644 index 00000000..a8343878 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/RegionData.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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.environment; + +import java.util.Optional; + +public class RegionData { + + private boolean primary; + private String configBucket; + private String environmentDataKmsCmkArn; + private String cmsSecureDataKmsCmkArn; + + public boolean isPrimary() { + return primary; + } + + public void setPrimary(boolean primary) { + this.primary = primary; + } + + public Optional getConfigBucket() { + return Optional.ofNullable(configBucket); + } + + public void setConfigBucket(String configBucket) { + this.configBucket = configBucket; + } + + public Optional getEnvironmentDataKmsCmkArn() { + return Optional.ofNullable(environmentDataKmsCmkArn); + } + + public void setEnvironmentDataKmsCmkArn(String environmentDataKmsCmkArn) { + this.environmentDataKmsCmkArn = environmentDataKmsCmkArn; + } + + public Optional getCmsSecureDataKmsCmkArn() { + return Optional.ofNullable(cmsSecureDataKmsCmkArn); + } + + public void setCmsSecureDataKmsCmkArn(String cmsSecureDataKmsCmkArn) { + this.cmsSecureDataKmsCmkArn = cmsSecureDataKmsCmkArn; + } + +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/RegionDeserializer.java b/src/main/java/com/nike/cerberus/domain/environment/RegionDeserializer.java new file mode 100644 index 00000000..2a138013 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/RegionDeserializer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 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.environment; + +import com.amazonaws.regions.Regions; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +public class RegionDeserializer extends StdDeserializer { + + public RegionDeserializer() { + this(null); + } + + public RegionDeserializer(Class r) { + super(r); + } + + @Override + public Regions deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String name = p.getCodec().readValue(p, String.class); + return Regions.fromName(name); + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/RegionKeyDeserializer.java b/src/main/java/com/nike/cerberus/domain/environment/RegionKeyDeserializer.java new file mode 100644 index 00000000..1f280966 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/RegionKeyDeserializer.java @@ -0,0 +1,17 @@ +package com.nike.cerberus.domain.environment; + +import com.amazonaws.regions.Regions; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; + +import java.io.IOException; + +public class RegionKeyDeserializer extends KeyDeserializer { + + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return Regions.fromName(key); + } + +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/RegionKeySerializer.java b/src/main/java/com/nike/cerberus/domain/environment/RegionKeySerializer.java new file mode 100644 index 00000000..3bed18f1 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/RegionKeySerializer.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 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.environment; + +import com.amazonaws.regions.Regions; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class RegionKeySerializer extends JsonSerializer { + + @Override + public void serialize(Regions value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { + gen.writeFieldName(value.getName()); + } +} diff --git a/src/main/java/com/nike/cerberus/domain/environment/RegionSerializer.java b/src/main/java/com/nike/cerberus/domain/environment/RegionSerializer.java new file mode 100644 index 00000000..00a8ca1d --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/environment/RegionSerializer.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 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.environment; + +import com.amazonaws.regions.Regions; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +public class RegionSerializer extends StdSerializer { + + public RegionSerializer() { + this(null); + } + + public RegionSerializer(Class t) { + super(t); + } + + @Override + public void serialize(Regions value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(value.getName()); + } +} 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 69f7725a..042cfd9b 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -32,18 +32,27 @@ */ public class Stack implements Comparable { - public static final Stack BASE = new Stack("base", "base.yaml", false); - public static final Stack VAULT = new Stack("vault", null, false); - public static final Stack CMS = new Stack("cms", "cms-cluster.yaml", true); - public static final Stack GATEWAY = new Stack("gateway", null, false); + public static final Stack IAM_ROLES = new Stack("base-iam-roles", "base-iam-roles.yaml", false); + public static final Stack CONFIG = new Stack("config", "config.yaml", false); public static final Stack VPC = new Stack("vpc", "vpc.yaml", false); - public static final Stack DATABASE = new Stack("database", "database.yaml", false); public static final Stack SECURITY_GROUPS = new Stack("security-groups", "security-groups.yaml", false); + public static final Stack DATABASE = new Stack("database", "database.yaml", false); public static final Stack LOAD_BALANCER = new Stack("load-balancer", "load-balancer.yaml", false); - public static final Stack ROUTE53 = new Stack("route53", "route53.yaml", false); + public static final Stack CMS = new Stack("cms", "cms-cluster.yaml", true); 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 ImmutableList ALL_STACKS = ImmutableList.of(BASE, VAULT, CMS, GATEWAY, VPC, DATABASE, SECURITY_GROUPS, LOAD_BALANCER, ROUTE53, WAF); + public static final ImmutableList ALL_STACKS = ImmutableList.of( + IAM_ROLES, + CONFIG, + VPC, + SECURITY_GROUPS, + DATABASE, + LOAD_BALANCER, + CMS, + WAF, + ROUTE53 + ); 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/domain/input/EnvironmentConfig.java b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java index c35e5f75..a731be6a 100644 --- a/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java +++ b/src/main/java/com/nike/cerberus/domain/input/EnvironmentConfig.java @@ -16,7 +16,7 @@ package com.nike.cerberus.domain.input; - +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -25,33 +25,23 @@ */ public class EnvironmentConfig { - private String version; private String environmentName; - private String primaryRegion; private Map globalTags; private String adminRoleArn; private String baseDomainName; private String edgeDomainNameOverride; private String originDomainNameOverride; - private String loadBalancerDomainNameOverride; - private String loadBalancerSslPolicyOverride; private List additionalSubjectNames; + private String loadBalancerSslPolicyOverride; private String hostedZoneId; - private VpcAccessWhitelist vpcAccessWhitelist; private boolean generateKeysAndCerts; private String acmeApiUrl; private boolean enableLeCertFix; private String acmeContactEmail; - private String localFolderToStoreCerts; - private ManagementService managementService; - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } + private String certificateDirectory; + private VpcAccessWhitelistInput vpcAccessWhitelist; + private ManagementServiceInput managementService; + private Map regionSpecificConfiguration = new HashMap<>(); public String getEnvironmentName() { return environmentName; @@ -61,14 +51,6 @@ public void setEnvironmentName(String environmentName) { this.environmentName = environmentName; } - public String getPrimaryRegion() { - return primaryRegion; - } - - public void setPrimaryRegion(String primaryRegion) { - this.primaryRegion = primaryRegion; - } - public Map getGlobalTags() { return globalTags; } @@ -109,12 +91,12 @@ public void setOriginDomainNameOverride(String originDomainNameOverride) { this.originDomainNameOverride = originDomainNameOverride; } - public String getLoadBalancerDomainNameOverride() { - return loadBalancerDomainNameOverride; + public List getAdditionalSubjectNames() { + return additionalSubjectNames; } - public void setLoadBalancerDomainNameOverride(String loadBalancerDomainNameOverride) { - this.loadBalancerDomainNameOverride = loadBalancerDomainNameOverride; + public void setAdditionalSubjectNames(List additionalSubjectNames) { + this.additionalSubjectNames = additionalSubjectNames; } public String getLoadBalancerSslPolicyOverride() { @@ -125,14 +107,6 @@ public void setLoadBalancerSslPolicyOverride(String loadBalancerSslPolicyOverrid this.loadBalancerSslPolicyOverride = loadBalancerSslPolicyOverride; } - public List getAdditionalSubjectNames() { - return additionalSubjectNames; - } - - public void setAdditionalSubjectNames(List additionalSubjectNames) { - this.additionalSubjectNames = additionalSubjectNames; - } - public String getHostedZoneId() { return hostedZoneId; } @@ -141,14 +115,6 @@ public void setHostedZoneId(String hostedZoneId) { this.hostedZoneId = hostedZoneId; } - public VpcAccessWhitelist getVpcAccessWhitelist() { - return vpcAccessWhitelist; - } - - public void setVpcAccessWhitelist(VpcAccessWhitelist vpcAccessWhitelist) { - this.vpcAccessWhitelist = vpcAccessWhitelist; - } - public boolean isGenerateKeysAndCerts() { return generateKeysAndCerts; } @@ -181,19 +147,49 @@ public void setAcmeContactEmail(String acmeContactEmail) { this.acmeContactEmail = acmeContactEmail; } - public String getLocalFolderToStoreCerts() { - return localFolderToStoreCerts; + public String getCertificateDirectory() { + return certificateDirectory; } - public void setLocalFolderToStoreCerts(String localFolderToStoreCerts) { - this.localFolderToStoreCerts = localFolderToStoreCerts; + public void setCertificateDirectory(String certificateDirectory) { + this.certificateDirectory = certificateDirectory; + } + + public VpcAccessWhitelistInput getVpcAccessWhitelist() { + return vpcAccessWhitelist; + } + + public void setVpcAccessWhitelist(VpcAccessWhitelistInput vpcAccessWhitelist) { + this.vpcAccessWhitelist = vpcAccessWhitelist; } - public ManagementService getManagementService() { + public ManagementServiceInput getManagementService() { return managementService; } - public void setManagementService(ManagementService managementService) { + public void setManagementService(ManagementServiceInput managementService) { this.managementService = managementService; } + + public Map getRegionSpecificConfiguration() { + return regionSpecificConfiguration; + } + + public void setRegionSpecificConfiguration(Map regionSpecificConfiguration) { + this.regionSpecificConfiguration = regionSpecificConfiguration; + } + + public RegionSpecificConfigurationInput getPrimaryRegionConfig() { + return getPrimaryEntry().getValue(); + } + + public String getPrimaryRegion() { + return getPrimaryEntry().getKey(); + } + + private Map.Entry getPrimaryEntry() { + return regionSpecificConfiguration.entrySet().stream() + .filter(entry -> entry.getValue() != null && entry.getValue().isPrimary()).findFirst() + .orElseThrow(() -> new RuntimeException("Failed to find primary region in region specific config")); + } } diff --git a/src/main/java/com/nike/cerberus/domain/input/ManagementServiceInput.java b/src/main/java/com/nike/cerberus/domain/input/ManagementServiceInput.java new file mode 100644 index 00000000..e6eda0b0 --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/input/ManagementServiceInput.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 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.input; + +import java.util.List; + + +/** + * Stores Management Service-specific parameters parsed from YAML + */ +public class ManagementServiceInput { + + private String adminGroup; + private List properties; + + public String getAdminGroup() { + return adminGroup; + } + + public void setAdminGroup(String adminGroup) { + this.adminGroup = adminGroup; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + +} diff --git a/src/main/java/com/nike/cerberus/domain/input/ManagementService.java b/src/main/java/com/nike/cerberus/domain/input/ManagementServiceRegionSpecificInput.java similarity index 63% rename from src/main/java/com/nike/cerberus/domain/input/ManagementService.java rename to src/main/java/com/nike/cerberus/domain/input/ManagementServiceRegionSpecificInput.java index a1a0d187..17f09bd8 100644 --- a/src/main/java/com/nike/cerberus/domain/input/ManagementService.java +++ b/src/main/java/com/nike/cerberus/domain/input/ManagementServiceRegionSpecificInput.java @@ -16,18 +16,11 @@ package com.nike.cerberus.domain.input; -import java.util.List; - - /** - * Stores Management Service-specific parameters parsed from YAML + * Stores region specific Management Service parameters parsed from YAML */ -public class ManagementService { +public class ManagementServiceRegionSpecificInput { - private String adminGroup; - private List properties; - private List additionalEncryptionCmkRegions; - private String certPath; private String amiId; private String instanceSize; private String keyPairName; @@ -35,38 +28,6 @@ public class ManagementService { private String maxInstances; private String minInstances; - public String getAdminGroup() { - return adminGroup; - } - - public void setAdminGroup(String adminGroup) { - this.adminGroup = adminGroup; - } - - public List getProperties() { - return properties; - } - - public void setProperties(List properties) { - this.properties = properties; - } - - public List getAdditionalEncryptionCmkRegions() { - return additionalEncryptionCmkRegions; - } - - public void setAdditionalEncryptionCmkRegions(List additionalEncryptionCmkRegions) { - this.additionalEncryptionCmkRegions = additionalEncryptionCmkRegions; - } - - public String getCertPath() { - return certPath; - } - - public void setCertPath(String certPath) { - this.certPath = certPath; - } - public String getAmiId() { return amiId; } @@ -114,4 +75,5 @@ public String getMinInstances() { public void setMinInstances(String minInstances) { this.minInstances = minInstances; } + } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java b/src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java similarity index 62% rename from src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java rename to src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java index e85b3ec1..343b5e4c 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/BaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java @@ -14,22 +14,19 @@ * limitations under the License. */ -package com.nike.cerberus.domain.cloudformation; +package com.nike.cerberus.domain.input; /** - * Represents the base stack inputs. + * Stores the region specific RDS configuration */ -public class BaseParameters { +public class RdsRegionSpecificInput { + private String size; - private String accountAdminArn; - - public String getAccountAdminArn() { - return accountAdminArn; + public String getSize() { + return size; } - public BaseParameters setAccountAdminArn(String accountAdminArn) { - this.accountAdminArn = accountAdminArn; - return this; + public void setSize(String size) { + this.size = size; } - } diff --git a/src/main/java/com/nike/cerberus/domain/input/RegionSpecificConfigurationInput.java b/src/main/java/com/nike/cerberus/domain/input/RegionSpecificConfigurationInput.java new file mode 100644 index 00000000..567bd17a --- /dev/null +++ b/src/main/java/com/nike/cerberus/domain/input/RegionSpecificConfigurationInput.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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.input; + +import java.util.Optional; + +/** + * Stores all the region specific configuration for a given region + */ +public class RegionSpecificConfigurationInput { + + private boolean primary = false; + private RdsRegionSpecificInput rds; + private ManagementServiceRegionSpecificInput managementService; + private String loadBalancerDomainNameOverride; + + public boolean isPrimary() { + return primary; + } + + public void setPrimary(boolean primary) { + this.primary = primary; + } + + public Optional getRds() { + return Optional.ofNullable(rds); + } + + public void setRds(RdsRegionSpecificInput rds) { + this.rds = rds; + } + + public Optional getManagementService() { + return Optional.ofNullable(managementService); + } + + public void setManagementService(ManagementServiceRegionSpecificInput managementService) { + this.managementService = managementService; + } + + public Optional getLoadBalancerDomainNameOverride() { + return Optional.ofNullable(loadBalancerDomainNameOverride); + } + + public void setLoadBalancerDomainNameOverride(String loadBalancerDomainNameOverride) { + this.loadBalancerDomainNameOverride = loadBalancerDomainNameOverride; + } +} diff --git a/src/main/java/com/nike/cerberus/domain/input/VpcAccessWhitelist.java b/src/main/java/com/nike/cerberus/domain/input/VpcAccessWhitelistInput.java similarity index 96% rename from src/main/java/com/nike/cerberus/domain/input/VpcAccessWhitelist.java rename to src/main/java/com/nike/cerberus/domain/input/VpcAccessWhitelistInput.java index b4333d67..20497373 100644 --- a/src/main/java/com/nike/cerberus/domain/input/VpcAccessWhitelist.java +++ b/src/main/java/com/nike/cerberus/domain/input/VpcAccessWhitelistInput.java @@ -21,7 +21,7 @@ /** * Stores CIDRs to whitelist for VPC access */ -public class VpcAccessWhitelist { +public class VpcAccessWhitelistInput { private List cidrs; private List ports; diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index c59e9e97..a86719b8 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -16,54 +16,41 @@ package com.nike.cerberus.module; -import com.amazonaws.AmazonWebServiceClient; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.auth.AWSCredentialsProviderChain; -import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; -import com.amazonaws.auth.InstanceProfileCredentialsProvider; -import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider; -import com.amazonaws.auth.SystemPropertiesCredentialsProvider; -import com.amazonaws.auth.profile.ProfileCredentialsProvider; -import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; -import com.amazonaws.services.autoscaling.AmazonAutoScaling; import com.amazonaws.services.autoscaling.AmazonAutoScalingClient; -import com.amazonaws.services.cloudformation.AmazonCloudFormation; import com.amazonaws.services.cloudformation.AmazonCloudFormationClient; -import com.amazonaws.services.ec2.AmazonEC2; import com.amazonaws.services.ec2.AmazonEC2Client; -import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancing; import com.amazonaws.services.elasticloadbalancingv2.AmazonElasticLoadBalancingClient; -import com.amazonaws.services.identitymanagement.AmazonIdentityManagement; import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; -import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.services.lambda.AWSLambda; import com.amazonaws.services.lambda.AWSLambdaClient; -import com.amazonaws.services.route53.AmazonRoute53; import com.amazonaws.services.route53.AmazonRoute53Client; -import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; -import com.amazonaws.services.sns.AmazonSNS; import com.amazonaws.services.sns.AmazonSNSClient; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.joda.JodaModule; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.MustacheFactory; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.OptionalBinder; +import com.google.inject.name.Names; import com.google.inject.util.Providers; -import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.CerberusCommand; import com.nike.cerberus.command.ProxyDelegate; -import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.environment.RegionDeserializer; +import com.nike.cerberus.domain.environment.RegionKeyDeserializer; +import com.nike.cerberus.domain.environment.RegionKeySerializer; +import com.nike.cerberus.domain.environment.RegionSerializer; import com.nike.cerberus.domain.input.EnvironmentConfig; +import com.nike.cerberus.service.AwsClientFactory; import com.nike.cerberus.util.UuidSupplier; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -73,9 +60,6 @@ import javax.inject.Singleton; import java.net.InetSocketAddress; import java.net.Proxy; -import java.util.List; -import java.util.Optional; -import java.util.UUID; /** * Guice module for enabling DI. @@ -83,10 +67,8 @@ public class CerberusModule extends AbstractModule { public static final String CONFIG_OBJECT_MAPPER = "configObjectMapper"; - - public static final String CERBERUS_ASSUME_ROLE_ARN = "CERBERUS_ASSUME_ROLE_ARN"; - - public static final String CERBERUS_ASSUME_ROLE_EXTERNAL_ID = "CERBERUS_ASSUME_ROLE_EXTERNAL_ID"; + public static final String ENV_NAME = "environmentName"; + public static final String CONFIG_REGION = "configRegionName"; private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -94,14 +76,14 @@ public class CerberusModule extends AbstractModule { private final String environmentName; - private final String regionName; + private final String configRegionName; private final EnvironmentConfig environmentConfig; public CerberusModule(CerberusCommand cerberusCommand) { proxyDelegate = cerberusCommand.getProxyDelegate(); - environmentName = cerberusCommand.getEnvironment(); - regionName = cerberusCommand.getRegion(); + environmentName = cerberusCommand.getEnvironmentName(); + configRegionName = cerberusCommand.getConfigRegion(); environmentConfig = cerberusCommand.getEnvironmentConfig(); } @@ -110,19 +92,29 @@ public CerberusModule(CerberusCommand cerberusCommand) { */ @Override protected void configure() { + // If a environment yaml was provided make it injectable as an Optional + OptionalBinder.newOptionalBinder(binder(), EnvironmentConfig.class); bind(EnvironmentConfig.class).toProvider(Providers.of(environmentConfig)); - final Region region = Region.getRegion(Regions.fromName(regionName)); - bind(AmazonEC2.class).toInstance(createAmazonClientInstance(AmazonEC2Client.class, region)); - bind(AmazonCloudFormation.class).toInstance(createAmazonClientInstance(AmazonCloudFormationClient.class, region)); - bind(AmazonIdentityManagement.class).toInstance(createAmazonClientInstance(AmazonIdentityManagementClient.class, region)); - bind(AWSKMS.class).toInstance(createAmazonClientInstance(AWSKMSClient.class, region)); - bind(AmazonS3.class).toInstance(createAmazonClientInstance(AmazonS3Client.class, region)); - bind(AmazonAutoScaling.class).toInstance(createAmazonClientInstance(AmazonAutoScalingClient.class, region)); - bind(AWSSecurityTokenService.class).toInstance(createAmazonClientInstance(AWSSecurityTokenServiceClient.class, region)); - bind(AWSLambda.class).toInstance(createAmazonClientInstance(AWSLambdaClient.class, region)); - bind(AmazonSNS.class).toInstance(createAmazonClientInstance(AmazonSNSClient.class, region)); - bind(AmazonRoute53.class).toInstance(createAmazonClientInstance(AmazonRoute53Client.class, region)); - bind(AmazonElasticLoadBalancing.class).toInstance(createAmazonClientInstance(AmazonElasticLoadBalancingClient.class, region)); + + bindConstant().annotatedWith(Names.named(ENV_NAME)).to(environmentName); + bindConstant().annotatedWith(Names.named(CONFIG_REGION)).to(configRegionName); + + // bind the aws client factories + bindAwsClientFactories(); + } + + private void bindAwsClientFactories() { + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); } /** @@ -137,6 +129,15 @@ public static ObjectMapper configObjectMapper() { final ObjectMapper om = new ObjectMapper(); om.findAndRegisterModules(); om.registerModule(new JodaModule()); + om.registerModule(new Jdk8Module()); + + SimpleModule cerberusCustom = new SimpleModule(); + cerberusCustom.addSerializer(Regions.class, new RegionSerializer()); + cerberusCustom.addDeserializer(Regions.class, new RegionDeserializer()); + cerberusCustom.addKeyDeserializer(Regions.class, new RegionKeyDeserializer()); + cerberusCustom.addKeySerializer(Regions.class, new RegionKeySerializer()); + + om.registerModule(cerberusCustom); om.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); om.enable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS); @@ -146,26 +147,6 @@ public static ObjectMapper configObjectMapper() { return om; } - /** - * Environment metadata object for describing the environment being executed against. - * - * @return Environment metadata - */ - @Provides - @Singleton - public EnvironmentMetadata environmentMetadata() { - final Optional bucketName = findBucket(environmentName); - final EnvironmentMetadata environmentMetadata = new EnvironmentMetadata(environmentName, regionName); - - if (bucketName.isPresent()) { - environmentMetadata.setBucketName(bucketName.get()); - } else { - logger.warn("Unable to determine the environment bucket for {}.", environmentName); - } - - return environmentMetadata; - } - @Provides @Singleton public MustacheFactory mustacheFactory() { @@ -198,48 +179,4 @@ public Proxy proxy() { return new Proxy(type, new InetSocketAddress(host, port)); } - private Optional findBucket(final String environmentName) { - AmazonS3Client s3Client = new AmazonS3Client(); - List buckets = s3Client.listBuckets(); - - String envBucket = null; - for (final Bucket bucket : buckets) { - String bucketName = bucket.getName(); - if (StringUtils.contains(bucket.getName(), ConfigConstants.CONFIG_BUCKET_KEY)) { - String tokenizedEnvName = StringUtils.replaceAll(environmentName, "_", "-"); - if (StringUtils.startsWith(bucketName, tokenizedEnvName)) { - envBucket = bucketName; - break; - } - } - } - - return Optional.ofNullable(envBucket); - } - - private static M createAmazonClientInstance(Class clientClass, Region region) { - return region.createClient(clientClass, getAWSCredentialsProviderChain(), new ClientConfiguration()); - } - - public static AWSCredentialsProviderChain getAWSCredentialsProviderChain() { - String cerberusRoleToAssume = System.getenv(CERBERUS_ASSUME_ROLE_ARN) != null ? - System.getenv(CERBERUS_ASSUME_ROLE_ARN) : ""; - String cerberusRoleToAssumeExternalId = System.getenv(CERBERUS_ASSUME_ROLE_EXTERNAL_ID) != null ? - System.getenv(CERBERUS_ASSUME_ROLE_EXTERNAL_ID) : ""; - - STSAssumeRoleSessionCredentialsProvider sTSAssumeRoleSessionCredentialsProvider = - new STSAssumeRoleSessionCredentialsProvider - .Builder(cerberusRoleToAssume, UUID.randomUUID().toString()) - .withExternalId(cerberusRoleToAssumeExternalId) - .build(); - - AWSCredentialsProviderChain chain = new AWSCredentialsProviderChain( - new EnvironmentVariableCredentialsProvider(), - new SystemPropertiesCredentialsProvider(), - new ProfileCredentialsProvider(), - sTSAssumeRoleSessionCredentialsProvider, - new InstanceProfileCredentialsProvider()); - - return chain; - } } diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java index 5e229027..71b5c0d1 100644 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java +++ b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsClusterOperation.java @@ -17,7 +17,6 @@ package com.nike.cerberus.operation.cms; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.CmsParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.CertificateInformation; @@ -32,8 +31,12 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import java.util.List; import java.util.Map; +import java.util.Optional; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** * Operation for creating the CMS cluster. @@ -42,8 +45,6 @@ public class CreateCmsClusterOperation implements Operation certInfoListForStack = configStore.getCertificationInformationList(); + public void run(CreateCmsClusterCommand command) { + VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); + List certInfoListForStack = configStore.getCertificationInformationList(); + + Map tags = command.getStackDelegate().getTagParameters().getTags(); if (certInfoListForStack.isEmpty()) { throw new IllegalStateException("Certificate for cerberus environment has not been uploaded!"); @@ -84,37 +89,39 @@ public void run(final CreateCmsClusterCommand command) { amiTagCheckService.validateAmiTagForStack(command.getStackDelegate().getAmiId(), Stack.CMS); } - final CmsParameters cmsParameters = new CmsParameters() + CmsParameters cmsParameters = new CmsParameters() .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()) - .setBaseStackName(Stack.BASE.getFullName(environmentName)) + .setBaseStackName(Stack.IAM_ROLES.getFullName(environmentName)) .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setSgStackName(Stack.SECURITY_GROUPS.getFullName(environmentName)); cmsParameters.getLaunchConfigParameters().setAmiId(command.getStackDelegate().getAmiId()); cmsParameters.getLaunchConfigParameters().setInstanceSize(command.getStackDelegate().getInstanceSize()); cmsParameters.getLaunchConfigParameters().setKeyPairName(command.getStackDelegate().getKeyPairName()); - cmsParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData(Stack.CMS)); + cmsParameters.getLaunchConfigParameters().setUserData(ec2UserDataService.getUserData( + configStore.getPrimaryRegion(), Stack.CMS, + Optional.ofNullable(tags.getOrDefault("ownerGroup", null)))); - final Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters); + Map parameters = cloudFormationObjectMapper.convertValue(cmsParameters); // allow user to overwrite CloudFormation parameters with -P option parameters.putAll(command.getStackDelegate().getDynamicParameters()); - cloudFormationService.createStackAndWait(Stack.CMS, + cloudFormationService.createStackAndWait( + configStore.getPrimaryRegion(), + Stack.CMS, parameters, true, - command.getStackDelegate().getTagParameters().getTags()); + tags); } @Override - public boolean isRunnable(final CreateCmsClusterCommand command) { - String environmentName = environmentMetadata.getName(); - + public boolean isRunnable(CreateCmsClusterCommand command) { try { - cloudFormationService.getStackId(Stack.LOAD_BALANCER.getFullName(environmentName)); - cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); - cloudFormationService.getStackId(Stack.BASE.getFullName(environmentName)); + cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.LOAD_BALANCER.getFullName(environmentName)); + cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.SECURITY_GROUPS.getFullName(environmentName)); + cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.IAM_ROLES.getFullName(environmentName)); } catch (IllegalArgumentException iae) { logger.error("Could not create the CMS cluster." + "Make sure the load balancer, security group, and base stacks have all been created.", iae); @@ -122,6 +129,6 @@ public boolean isRunnable(final CreateCmsClusterCommand command) { } return configStore.getCmsEnvConfig().isPresent() && - !cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName)); + !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.CMS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java b/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java deleted file mode 100644 index 15e1c11b..00000000 --- a/src/main/java/com/nike/cerberus/operation/cms/CreateCmsCmkOperation.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2017 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.cms; - -import com.amazonaws.regions.Regions; -import com.beust.jcommander.ParameterException; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.command.cms.CreateCmsCmkCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.KmsPolicyGenerator; -import com.nike.cerberus.service.KmsService; -import com.nike.cerberus.store.ConfigStore; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.commons.lang3.time.DateFormatUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import static com.nike.cerberus.ConfigConstants.CMK_ARNS_KEY; - -/** - * Create CMKs in multiple regions for CMS to use. - *

- * We don't want this as part of the base CloudFormation YAML because we are operating on multiple regions. - */ -public class CreateCmsCmkOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ConfigStore configStore; - private final EnvironmentMetadata environmentMetadata; - private final KmsService kmsService; - - @Inject - public CreateCmsCmkOperation(final ConfigStore configStore, - final EnvironmentMetadata environmentMetadata, - final KmsService kmsService) { - this.configStore = configStore; - this.environmentMetadata = environmentMetadata; - this.kmsService = kmsService; - } - - @Override - public void run(final CreateCmsCmkCommand command) { - // load the existing configuration - logger.info("Retrieving configuration data from the configuration bucket."); - final Properties cmsConfigProperties = configStore.getAllExistingCmsEnvProperties(); - - // create the CMKs - List cmkArns = createCmks(command, cmsConfigProperties); - - // store the new configuration - cmsConfigProperties.put(CMK_ARNS_KEY, StringUtils.join(cmkArns, ",")); - logger.info("Uploading the CMS configuration to the configuration bucket."); - configStore.storeCmsEnvConfig(cmsConfigProperties); - logger.info("Uploading complete."); - } - - @Override - public boolean isRunnable(final CreateCmsCmkCommand command) { - boolean isRunnable = configStore.getCmsEnvConfig().isPresent(); - - if (!isRunnable) { - logger.warn("CMS config does not exist, please use 'create-cms-config' command first."); - } - - try { - final Properties cmsConfigProperties = configStore.getAllExistingCmsEnvProperties(); - // additional argument validation - validateRegionsArg(command); - validateRotateArg(command, cmsConfigProperties); - } catch (ParameterException e) { - return false; - } - - - return isRunnable; - } - - private void validateRotateArg(CreateCmsCmkCommand command, Properties cmsConfigProperties) { - if (command.isRotate()) { - if (cmsConfigProperties.containsKey(CMK_ARNS_KEY)) { - logger.info("Overwriting existing CMK ARNs property: " + cmsConfigProperties.getProperty(CMK_ARNS_KEY)); - logger.info("Existing CMKs will not be deleted and may be needed to decrypt existing secrets"); - } else { - throw new ParameterException(CreateCmsCmkCommand.ROTATE_ARG + " was specified but there is no existing CMK to rotate"); - } - } else { - if (cmsConfigProperties.containsKey(CMK_ARNS_KEY)) { - logger.info("Property already exists: " + CMK_ARNS_KEY + "=" + cmsConfigProperties.get(CMK_ARNS_KEY)); - throw new ParameterException("CMK already exists but " + CreateCmsCmkCommand.ROTATE_ARG + " was not specified. Generally, manual rotation is not necessary."); - } else { - logger.info("Will add CMK ARNs property: " + CMK_ARNS_KEY); - } - } - } - - private void validateRegionsArg(CreateCmsCmkCommand command) { - String primaryRegion = environmentMetadata.getRegionName(); - List regions = command.getAdditionalRegions(); - if (regions.contains(primaryRegion)) { - throw new ParameterException("Additional regions should not contain the primary region for the environment"); - } - } - - private List createCmks(CreateCmsCmkCommand command, Properties cmsConfigProperties) { - String envName = environmentMetadata.getName(); - String primaryRegion = environmentMetadata.getRegionName(); - - String alias = generateAliasName(envName, primaryRegion); - - String description = "Generated for the Cerberus " + envName + " environment running in " + primaryRegion; - String policyAsJson = generateKeyPolicy(cmsConfigProperties, description); - logger.info("Generated the following policy:\n" + policyAsJson); - - Map tags = Maps.newHashMap(); - tags.put("created_by", "cerberus_cli"); - tags.put("created_for", "cerberus_cms"); - tags.put("cerberus_env", envName); - - List cmkRegions = getTargetRegions(command); - return kmsService.createKeysAndAliases(cmkRegions, alias, policyAsJson, description, tags); - } - - private String generateAliasName(String envName, String primaryRegion) { - return "alias/cerberus/cms-" + envName + "-" + primaryRegion + "-" + DateFormatUtils.format(new Date(), "yyyy-MM-dd-HH-mm"); - } - - private String generateKeyPolicy(Properties cmsConfigProperties, String description) { - String rootUserArn = getRequiredProperty(cmsConfigProperties, ConfigConstants.ROOT_USER_ARN_KEY); - String adminUserArn = getRequiredProperty(cmsConfigProperties, ConfigConstants.ADMIN_ROLE_ARN_KEY); - String cmsRoleArn = getRequiredProperty(cmsConfigProperties, ConfigConstants.CMS_ROLE_ARN_KEY); - - KmsPolicyGenerator generator = new KmsPolicyGenerator() - .withDescription(description) - .withAdminArns(Lists.newArrayList(rootUserArn, adminUserArn)) - .withCmsArn(cmsRoleArn); - - return generator.generatePolicyJson(); - } - - private String getRequiredProperty(Properties cmsConfigProperties, String propertyName) { - String value = cmsConfigProperties.getProperty(propertyName); - Validate.notBlank(value, "CMS config value " + propertyName + " was not found!"); - return value; - } - - private List getTargetRegions(CreateCmsCmkCommand command) { - // make sure primary region is first in the list - List cmkRegions = Lists.newArrayList(environmentMetadata.getRegions()); - for (String region : command.getAdditionalRegions()) { - cmkRegions.add(Regions.fromName(region)); - } - return cmkRegions; - } -} diff --git a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java index b456e64c..33110bd9 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CompositeOperation.java @@ -88,7 +88,7 @@ public void run(T compositeCommand) { } // Use jcommander to bind the resolved args to the command object - JCommander.newBuilder().addObject(chainedCommand).build().parse(args); + new JCommander(chainedCommand).parse(args); // Get the instance of the operation from guice Operation operation = getOperationInstance(chainedCommand); @@ -124,7 +124,7 @@ public void run(T compositeCommand) { protected abstract List getCompositeCommandChain(T compositeCommand); /** - * If you command doesn't require that the environment yaml be supplied, you can override this to false. + * If your command doesn't require that the environment yaml be supplied, you can override this to false. * * @return boolean of whether or not the environment yaml is required. */ diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java index 352e54e2..d0ef3744 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java @@ -18,10 +18,9 @@ import com.google.common.collect.Lists; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; -import com.nike.cerberus.command.cms.CreateCmsCmkCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; -import com.nike.cerberus.command.core.CreateBaseCommand; +import com.nike.cerberus.command.core.InitializeEnvironmentCommand; import com.nike.cerberus.command.core.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; @@ -48,7 +47,7 @@ public class CreateEnvironmentOperation extends CompositeOperation getCompositeCommandChain(CreateEnvironmentCommand compositeCommand) { List list = Lists.newArrayList( // Step 1 Create the Base Cloud Formation Stack that creates S3 Buckets, Iam Roles and KMS keys needed for config - new ChainableCommand(new CreateBaseCommand()), + new ChainableCommand(new InitializeEnvironmentCommand()), // Step 2 Create the VPC Cloud Formation Stack that Cerberus will use new ChainableCommand(new CreateVpcCommand()), @@ -79,20 +78,16 @@ protected List getCompositeCommandChain(CreateEnvironmentComma // Upload to S3 for CMS to download at service start new ChainableCommand(new CreateCmsConfigCommand()), - // Step 9 Create CMS CMK, create KMS master keys in the regions specified for CMS to use with the AWS Encryption - // client to encrypt secure data in a manor that is decryptable in multiple regions and store in cms props - new ChainableCommand(new CreateCmsCmkCommand()), - - // Step 10 Create the CMS Cluster Stack + // Step 9 Create the CMS Cluster Stack new ChainableCommand(new CreateCmsClusterCommand()), - // Step 11 Create the Web Application Fire wall stack + // Step 10 Create the Web Application Fire wall stack new ChainableCommand(new CreateWafCommand()), - // Step 12 Create the Route 53 DNS Record Stack for origin and the load balancer + // Step 11 Create the Route 53 DNS Record Stack for origin and the load balancer new ChainableCommand(new CreateRoute53Command()), - // Step 13 Create the outer most domain name record that will point to the origin record + // Step 12 Create the outer most domain name record that will point to the origin record new ChainableCommand(new CreateEdgeDomainRecordCommand()) )); diff --git a/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java index ac1ba76b..7bbe93d0 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java @@ -3,33 +3,40 @@ import com.google.common.collect.ImmutableList; import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; import com.nike.cerberus.command.core.DeleteStackCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.service.ConsoleService; +import com.nike.cerberus.store.ConfigStore; import javax.inject.Inject; +import javax.inject.Named; import java.util.LinkedList; import java.util.List; import static com.nike.cerberus.domain.environment.Stack.*; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; public class DeleteEnvironmentOperation extends CompositeOperation { private final ConsoleService consoleService; - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; + + private final ConfigStore configStore; @Inject public DeleteEnvironmentOperation(ConsoleService consoleService, - EnvironmentMetadata environmentMetadata) { + @Named(ENV_NAME) String environmentName, + ConfigStore configStore) { this.consoleService = consoleService; - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; + this.configStore = configStore; } @Override protected List getCompositeCommandChain(DeleteEnvironmentCommand compositeCommand) { List chainableCommandList = new LinkedList<>(); + // todo delete the kms keys generated by cms ImmutableList.of( ROUTE53, WAF, @@ -37,16 +44,28 @@ protected List getCompositeCommandChain(DeleteEnvironmentComma LOAD_BALANCER, DATABASE, SECURITY_GROUPS, - VPC, - BASE + VPC ).forEach(stack -> chainableCommandList.add(ChainableCommand.Builder.create() .withCommand(new DeleteStackCommand()) - .withAdditionalArg(DeleteStackCommand.STACK_NAME_LONG_ARG) - .withAdditionalArg(stack.getName()) + .withOption(DeleteStackCommand.STACK_NAME_LONG_ARG, stack.getName()) .build()) ); + configStore.getConfigEnabledRegions().forEach(region -> { + chainableCommandList.add(ChainableCommand.Builder.create() + .withCommand(new DeleteStackCommand()) + .withOption(DeleteStackCommand.STACK_NAME_LONG_ARG, CONFIG.getName()) + .withOption(DeleteStackCommand.REGION_LONG_ARG, region.getName()) + .build()); + }); + + chainableCommandList.add(ChainableCommand.Builder.create() + .withCommand(new DeleteStackCommand()) + .withAdditionalArg(DeleteStackCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(IAM_ROLES.getName()) + .build()); + return chainableCommandList; } @@ -55,7 +74,7 @@ public boolean isRunnable(DeleteEnvironmentCommand command) { try { String warning = String.format( "This will delete the environment '%s' including all the secure data.", - environmentMetadata.getName() + environmentName ); consoleService.askUserToProceed(warning, ConsoleService.DefaultAction.NO); } catch (RuntimeException e) { diff --git a/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java index d96d417b..509e7ee6 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java @@ -21,27 +21,32 @@ import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; import javax.inject.Inject; +import javax.inject.Named; import java.util.List; import static com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG; import static com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate.*; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; public class GenerateAndRotateCertificatesOperation extends CompositeOperation { private final CloudFormationService cloudFormationService; - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; + private final ConfigStore configStore; @Inject public GenerateAndRotateCertificatesOperation(CloudFormationService cloudFormationService, - EnvironmentMetadata environmentMetadata) { + @Named(ENV_NAME) String environmentName, + ConfigStore configStore) { this.cloudFormationService = cloudFormationService; - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; + this.configStore = configStore; } @@ -89,6 +94,14 @@ protected List getCompositeCommandChain(GenerateAndRotateCerti parameters.getContactEmail() ); + if (!parameters.isTty()) { + generateCertificateFilesCommandBuilder.withAdditionalArg(NO_TTY_LONG_ARG); + } + + if (parameters.isAutoAcceptAcmeTos()) { + generateCertificateFilesCommandBuilder.withAdditionalArg(ACCEPT_ACME_TOS); + } + parameters.getSubjectAlternativeNames().forEach(name -> { generateCertificateFilesCommandBuilder.withOption(SUBJECT_ALT_NAME_LONG_ARG, name); }); @@ -109,14 +122,13 @@ protected List getCompositeCommandChain(GenerateAndRotateCerti @Override public boolean isRunnable(GenerateAndRotateCertificatesCommand command) { boolean isRunnable = true; - String environmentName = environmentMetadata.getName(); - if (! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { + if (! cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.LOAD_BALANCER.getFullName(environmentName))) { log.error("The load-balancer stack must be present in order to rotate certificates"); isRunnable = false; } - if (! cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName))) { + if (! cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.CMS.getFullName(environmentName))) { log.error("The cms stack must be present to rotate certificates"); isRunnable = false; } diff --git a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java index f83bbf9e..70b7418c 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java @@ -25,26 +25,32 @@ import com.nike.cerberus.command.core.UpdateStackCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; +import javax.inject.Named; import java.util.List; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + /** * Operation for the certificate rotation command */ public class RotateCertificatesOperation extends CompositeOperation { private final CloudFormationService cloudFormationService; - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; + private final ConfigStore configStore; @Inject public RotateCertificatesOperation(CloudFormationService cloudFormationService, - EnvironmentMetadata environmentMetadata) { + @Named(ENV_NAME) String environmentName, + ConfigStore configStore) { this.cloudFormationService = cloudFormationService; - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; + this.configStore = configStore; } @Override @@ -77,14 +83,13 @@ protected List getCompositeCommandChain(RotateCertificatesComm @Override public boolean isRunnable(RotateCertificatesCommand command) { boolean isRunnable = true; - String environmentName = environmentMetadata.getName(); - if (! cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { + if (! cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.LOAD_BALANCER.getFullName(environmentName))) { log.error("The load-balancer stack must be present in order to rotate certificates"); isRunnable = false; } - if (! cloudFormationService.isStackPresent(Stack.CMS.getFullName(environmentName))) { + if (! cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.CMS.getFullName(environmentName))) { log.error("The cms stack must be present to rotate certificates"); isRunnable = false; } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java deleted file mode 100644 index 33acfa81..00000000 --- a/src/main/java/com/nike/cerberus/operation/core/CreateBaseOperation.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2017 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.nike.cerberus.command.core.CreateBaseCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseParameters; -import com.nike.cerberus.domain.environment.Stack; -import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.CloudFormationService; -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 java.util.Map; - -/** - * Creates the base components via CloudFormation used by all of Cerberus. - */ -public class CreateBaseOperation implements Operation { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final EnvironmentMetadata environmentMetadata; - - private final CloudFormationService cloudFormationService; - - private final ConfigStore configStore; - - private final CloudFormationObjectMapper cloudFormationObjectMapper; - - @Inject - public CreateBaseOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final ConfigStore configStore, - final CloudFormationObjectMapper cloudFormationObjectMapper) { - this.environmentMetadata = environmentMetadata; - this.cloudFormationService = cloudFormationService; - this.configStore = configStore; - this.cloudFormationObjectMapper = cloudFormationObjectMapper; - } - - @Override - public void run(final CreateBaseCommand command) { - final BaseParameters baseParameters = new BaseParameters() - .setAccountAdminArn(command.getAdminRoleArn()); - - final Map parameters = cloudFormationObjectMapper.convertValue(baseParameters); - - cloudFormationService.createStackAndWait(Stack.BASE, parameters, true, - command.getTagsDelegate().getTags()); - - - logger.info("Stack creation complete, initializing the configuration bucket."); - environmentMetadata.setBucketName(configStore.getBaseStackOutputs().getConfigBucketName()); - configStore.initEnvironmentData(); - logger.info("Initialization complete."); - } - - @Override - public boolean isRunnable(final CreateBaseCommand command) { - String environmentName = environmentMetadata.getName(); - return !cloudFormationService.isStackPresent(Stack.BASE.getFullName(environmentName)); - } - -} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java index f37e4975..61514849 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java @@ -16,10 +16,9 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; +import com.amazonaws.regions.Regions; import com.nike.cerberus.command.core.CreateDatabaseCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; +import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.cloudformation.VpcParameters; @@ -33,8 +32,11 @@ 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 the base components via CloudFormation used by all of Cerberus. */ @@ -42,7 +44,7 @@ public class CreateDatabaseOperation implements Operation private final Logger logger = LoggerFactory.getLogger(getClass()); - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final CloudFormationService cloudFormationService; @@ -53,24 +55,26 @@ public class CreateDatabaseOperation implements Operation private RandomStringGenerator passwordGenerator = new RandomStringGenerator(); @Inject - public CreateDatabaseOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final ConfigStore configStore, - final CloudFormationObjectMapper cloudFormationObjectMapper) { - this.environmentMetadata = environmentMetadata; + public CreateDatabaseOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + ConfigStore configStore, + CloudFormationObjectMapper cloudFormationObjectMapper) { + + this.environmentName = environmentName; this.cloudFormationService = cloudFormationService; this.configStore = configStore; this.cloudFormationObjectMapper = cloudFormationObjectMapper; } @Override - public void run(final CreateDatabaseCommand command) { - final String environmentName = environmentMetadata.getName(); - final VpcParameters vpcParameters = configStore.getVpcStackParameters(); - final VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); - final String databasePassword = passwordGenerator.get(); + public void run(CreateDatabaseCommand command) { + Regions region = configStore.getPrimaryRegion(); + + VpcParameters vpcParameters = configStore.getVpcStackParameters(); + VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); + String databasePassword = passwordGenerator.get(); - final DatabaseParameters databaseParameters = new DatabaseParameters() + DatabaseParameters databaseParameters = new DatabaseParameters() .setCmsDbMasterPassword(databasePassword) .setSgStackName(Stack.SECURITY_GROUPS.getFullName(environmentName)) .setCmsDbInstanceAz1(vpcParameters.getAz1()) @@ -78,26 +82,31 @@ public void run(final CreateDatabaseCommand command) { .setCmsDbInstanceAz3(vpcParameters.getAz3()) .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) - .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); + .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()) + .setCmsDbInstanceClass(command.getInstanceClass()); - final Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters); - - cloudFormationService.createStackAndWait(Stack.DATABASE, parameters, true, - command.getTagsDelegate().getTags()); + Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters); configStore.storeCmsDatabasePassword(databasePassword); + + cloudFormationService.createStackAndWait( + region, + Stack.DATABASE, + parameters, + true, + command.getTagsDelegate().getTags() + ); } @Override - public boolean isRunnable(final CreateDatabaseCommand command) { - String environmentName = environmentMetadata.getName(); + public boolean isRunnable(CreateDatabaseCommand command) { try { - cloudFormationService.getStackId(Stack.SECURITY_GROUPS.getFullName(environmentName)); + cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.SECURITY_GROUPS.getFullName(environmentName)); } catch (IllegalArgumentException iae) { logger.error("The security group stack must exist to create the the data base stack!"); return false; } - return !cloudFormationService.isStackPresent(Stack.DATABASE.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.DATABASE.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java index 7d1a4c80..5eeef06c 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateEdgeDomainRecordOperation.java @@ -20,7 +20,6 @@ import com.amazonaws.services.route53.model.ResourceRecord; import com.amazonaws.services.route53.model.ResourceRecordSet; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; @@ -32,9 +31,12 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import java.util.Optional; import java.util.stream.Collectors; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + /** * Creates the edge domain Route53 record for Cerberus */ @@ -48,30 +50,30 @@ public class CreateEdgeDomainRecordOperation implements Operation parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters); + Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters); - cloudFormationService.createStackAndWait(Stack.LOAD_BALANCER, parameters, true, + cloudFormationService.createStackAndWait( + configStore.getPrimaryRegion(), + Stack.LOAD_BALANCER, + parameters, + true, command.getTagsDelegate().getTags()); } @Override - public boolean isRunnable(final CreateLoadBalancerCommand command) { + public boolean isRunnable(CreateLoadBalancerCommand command) { boolean isRunnable = true; - String environmentName = environmentMetadata.getName(); - if (!cloudFormationService.isStackPresent(Stack.SECURITY_GROUPS.getFullName(environmentName))) { + if (!cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.SECURITY_GROUPS.getFullName(environmentName))) { logger.error("The security group stack must exist to create the load balancer!"); isRunnable = false; } @@ -100,7 +106,8 @@ public boolean isRunnable(final CreateLoadBalancerCommand command) { isRunnable = false; } - if (cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { + if (cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.LOAD_BALANCER.getFullName(environmentName))) { logger.error("The load balancer stack already exists"); isRunnable = false; } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java index dc96f030..23a2a8a5 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateRoute53Operation.java @@ -16,23 +16,24 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateRoute53Command; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.Route53Parameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Route53Service; +import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; 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 the origin and load balancer Route53 records for Cerberus */ @@ -40,7 +41,7 @@ public class CreateRoute53Operation implements Operation { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final CloudFormationService cloudFormationService; @@ -48,45 +49,53 @@ public class CreateRoute53Operation implements Operation { private final CloudFormationObjectMapper cloudFormationObjectMapper; + private final ConfigStore configStore; + @Inject - public CreateRoute53Operation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final Route53Service route53Service, - final CloudFormationObjectMapper cloudFormationObjectMapper) { - this.environmentMetadata = environmentMetadata; + public CreateRoute53Operation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + Route53Service route53Service, + CloudFormationObjectMapper cloudFormationObjectMapper, + ConfigStore configStore) { + + this.environmentName = environmentName; this.cloudFormationService = cloudFormationService; this.route53Service = route53Service; this.cloudFormationObjectMapper = cloudFormationObjectMapper; + this.configStore = configStore; } @Override - public void run(final CreateRoute53Command command) { - final String environmentName = environmentMetadata.getName(); - - final Route53Parameters route53Parameters = new Route53Parameters() + public void run(CreateRoute53Command command) { + Route53Parameters route53Parameters = new Route53Parameters() .setHostedZoneId(command.getHostedZoneId()) .setLoadBalancerDomainName(getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride())) .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setOriginDomainName(getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride())); - final Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters); + Map parameters = cloudFormationObjectMapper.convertValue(route53Parameters); - cloudFormationService.createStackAndWait(Stack.ROUTE53, parameters, true, + cloudFormationService.createStackAndWait( + configStore.getPrimaryRegion(), + Stack.ROUTE53, + parameters, + true, command.getTagsDelegate().getTags()); } @Override - public boolean isRunnable(final CreateRoute53Command command) { - final String environmentName = environmentMetadata.getName(); - final String loadBalancerDomainName = getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride()); - final String originDomainName = getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride()); + public boolean isRunnable(CreateRoute53Command command) { + String loadBalancerDomainName = getLoadBalancerDomainName(command.getBaseDomainName(), command.getLoadBalancerDomainNameOverride()); + String originDomainName = getOriginDomainName(command.getBaseDomainName(), command.getOriginDomainNameOverride()); boolean isRunnable = true; - if (!cloudFormationService.isStackPresent(Stack.LOAD_BALANCER.getFullName(environmentName))) { + if (!cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.LOAD_BALANCER.getFullName(environmentName))) { logger.error("The load balancer stack must exist to create the Route53 record!"); isRunnable = false; } - if (cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName))) { + if (cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.ROUTE53.getFullName(environmentName))) { logger.error("Route53 stack already exists."); isRunnable = false; } @@ -102,19 +111,19 @@ public boolean isRunnable(final CreateRoute53Command command) { return isRunnable; } - private String getLoadBalancerDomainName(final String baseDomainName, final String loadBalancerDomainNameOverride) { - final String defaultLoadBalancerDomainName = String.format("%s.%s.%s", - environmentMetadata.getName(), - environmentMetadata.getRegionName(), + private String getLoadBalancerDomainName(String baseDomainName, String loadBalancerDomainNameOverride) { + String defaultLoadBalancerDomainName = String.format("%s.%s.%s", + environmentName, + configStore.getPrimaryRegion().getName(), baseDomainName); return StringUtils.isBlank(loadBalancerDomainNameOverride) ? defaultLoadBalancerDomainName : loadBalancerDomainNameOverride; } - private String getOriginDomainName(final String baseDomainName, final String originDomainNameOverride) { - final String defaultOriginDomainName = String.format("origin.%s.%s", - environmentMetadata.getName(), + private String getOriginDomainName(String baseDomainName, String originDomainNameOverride) { + String defaultOriginDomainName = String.format("origin.%s.%s", + environmentName, baseDomainName); return StringUtils.isBlank(originDomainNameOverride) ? diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java index 94fd45ae..bbf2f568 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateSecurityGroupsOperation.java @@ -16,10 +16,7 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.environment.Stack; @@ -31,8 +28,11 @@ 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 the base components via CloudFormation used by all of Cerberus. */ @@ -40,7 +40,7 @@ public class CreateSecurityGroupsOperation implements Operation parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters); + Map parameters = cloudFormationObjectMapper.convertValue(securityGroupParameters); - cloudFormationService.createStackAndWait(Stack.SECURITY_GROUPS, parameters, true, + cloudFormationService.createStackAndWait( + configStore.getPrimaryRegion(), + Stack.SECURITY_GROUPS, + parameters, + true, command.getTagParameters().getTags()); } @Override - public boolean isRunnable(final CreateSecurityGroupsCommand command) { - String environmentName = environmentMetadata.getName(); - return !cloudFormationService.isStackPresent(Stack.SECURITY_GROUPS.getFullName(environmentName)); + public boolean isRunnable(CreateSecurityGroupsCommand command) { + return !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.SECURITY_GROUPS.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index d6131187..be8e4cb2 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -17,23 +17,24 @@ package com.nike.cerberus.operation.core; import com.beust.jcommander.internal.Maps; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateVpcCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.VpcParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2Service; +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.List; import java.util.Map; import static com.nike.cerberus.ConfigConstants.MINIMUM_AZS; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** * Creates the base components via CloudFormation used by all of Cerberus. @@ -42,7 +43,7 @@ public class CreateVpcOperation implements Operation { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final CloudFormationService cloudFormationService; @@ -50,36 +51,45 @@ public class CreateVpcOperation implements Operation { private final CloudFormationObjectMapper cloudFormationObjectMapper; + private final ConfigStore configStore; + @Inject - public CreateVpcOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final Ec2Service ec2Service, - final CloudFormationObjectMapper cloudFormationObjectMapper) { - this.environmentMetadata = environmentMetadata; + public CreateVpcOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + Ec2Service ec2Service, + CloudFormationObjectMapper cloudFormationObjectMapper, + ConfigStore configStore) { + + this.environmentName = environmentName; this.cloudFormationService = cloudFormationService; this.ec2Service = ec2Service; this.cloudFormationObjectMapper = cloudFormationObjectMapper; + this.configStore = configStore; } @Override - public void run(final CreateVpcCommand command) { - final Map azByIdentifier = mapAvailabilityZones(); + public void run(CreateVpcCommand command) { + Map azByIdentifier = mapAvailabilityZones(); - final VpcParameters vpcParameters = new VpcParameters() + VpcParameters vpcParameters = new VpcParameters() .setAz1(azByIdentifier.get(1)) .setAz2(azByIdentifier.get(2)) .setAz3(azByIdentifier.get(3)); - final Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters); + Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters); - cloudFormationService.createStackAndWait(Stack.VPC, parameters, true, + cloudFormationService.createStackAndWait( + configStore.getPrimaryRegion(), + Stack.VPC, + parameters, + true, command.getTagsDelegate().getTags()); } @Override - public boolean isRunnable(final CreateVpcCommand command) { - String environmentName = environmentMetadata.getName(); - return !cloudFormationService.isStackPresent(Stack.VPC.getFullName(environmentName)); + public boolean isRunnable(CreateVpcCommand command) { + return !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.VPC.getFullName(environmentName)); } private Map mapAvailabilityZones() { diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java index 511d6680..e89331a5 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateWafOperation.java @@ -16,10 +16,7 @@ package com.nike.cerberus.operation.core; -import com.amazonaws.services.cloudformation.model.StackStatus; -import com.google.common.collect.Sets; import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.WafParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; @@ -30,8 +27,11 @@ 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 the base components via CloudFormation used by all of Cerberus. */ @@ -39,49 +39,53 @@ public class CreateWafOperation implements Operation { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final CloudFormationService cloudFormationService; - private final ConfigStore configStore; - private final CloudFormationObjectMapper cloudFormationObjectMapper; + private final ConfigStore configStore; + @Inject - public CreateWafOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final ConfigStore configStore, - final CloudFormationObjectMapper cloudFormationObjectMapper) { - this.environmentMetadata = environmentMetadata; + public CreateWafOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + CloudFormationObjectMapper cloudFormationObjectMapper, + ConfigStore configStore) { + + this.environmentName = environmentName; this.cloudFormationService = cloudFormationService; - this.configStore = configStore; this.cloudFormationObjectMapper = cloudFormationObjectMapper; + this.configStore = configStore; } @Override - public void run(final CreateWafCommand command) { - final String environmentName = environmentMetadata.getName(); - - final WafParameters wafParameters = new WafParameters() + public void run(CreateWafCommand command) { + WafParameters wafParameters = new WafParameters() .setLoadBalancerStackName(Stack.LOAD_BALANCER.getFullName(environmentName)) .setWafName("cerberus-" + environmentName + "-waf"); - final Map parameters = cloudFormationObjectMapper.convertValue(wafParameters); + Map parameters = cloudFormationObjectMapper.convertValue(wafParameters); - cloudFormationService.createStackAndWait(Stack.WAF, parameters, true, - command.getTagsDelegate().getTags()); + cloudFormationService.createStackAndWait( + configStore.getPrimaryRegion(), + Stack.WAF, + parameters, + true, + command.getTagsDelegate().getTags() + ); } @Override - public boolean isRunnable(final CreateWafCommand command) { - String environmentName = environmentMetadata.getName(); + public boolean isRunnable(CreateWafCommand command) { try { - cloudFormationService.getStackId(Stack.LOAD_BALANCER.getFullName(environmentName)); + cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.LOAD_BALANCER.getFullName(environmentName)); } catch (IllegalArgumentException iae) { logger.error("The load balancer stack must exist to create the WAF!"); return false; } - return !cloudFormationService.isStackPresent(Stack.ROUTE53.getFullName(environmentName)); + return !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.ROUTE53.getFullName(environmentName)); } } diff --git a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java index 85d5e40b..a6395763 100644 --- a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java @@ -16,44 +16,59 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.iterable.S3Versions; import com.amazonaws.services.s3.model.S3VersionSummary; import com.google.inject.Inject; import com.nike.cerberus.command.core.DeleteStackCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.AwsClientFactory; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Named; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + public class DeleteStackOperation implements Operation { private final Logger log = LoggerFactory.getLogger(getClass()); - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final CloudFormationService cloudFormationService; - private final AmazonS3 amazonS3; + private final AwsClientFactory amazonS3Factory; + + private final ConfigStore configStore; @Inject - public DeleteStackOperation(EnvironmentMetadata environmentMetadata, - CloudFormationService cloudFormationService, AmazonS3 amazonS3) { + public DeleteStackOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + AwsClientFactory amazonS3Factory, + ConfigStore configStore) { - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; this.cloudFormationService = cloudFormationService; - this.amazonS3 = amazonS3; + this.amazonS3Factory = amazonS3Factory; + this.configStore = configStore; } @Override public void run(DeleteStackCommand command) { - String stackName = command.getStack().getFullName(environmentMetadata.getName()); - cloudFormationService.getStackResources(stackName) + Regions region = getRegion(command); + + String stackName = command.getStack().getFullName(environmentName); + cloudFormationService.getStackResources(region, stackName) .forEach(stackResourceSummary -> { if (stackResourceSummary.getResourceType().equals("AWS::S3::Bucket")) { String bucketName = stackResourceSummary.getPhysicalResourceId(); log.info("Detected S3 Bucket: {}, emptying contents before deleting stack", bucketName); + AmazonS3 amazonS3 = amazonS3Factory.getClient(region); for (S3VersionSummary version : S3Versions.inBucket(amazonS3, bucketName)) { String key = version.getKey(); String versionId = version.getVersionId(); @@ -62,15 +77,20 @@ public void run(DeleteStackCommand command) { } }); - log.info("Deleting stack: {}", stackName); - cloudFormationService.deleteStackAndWait(stackName); - log.info("Finished deleting stack: {}", stackName); + log.info("Deleting stack: {} in region: {}", stackName, region); + cloudFormationService.deleteStackAndWait(region, stackName); + log.info("Finished deleting stack: {} in regionL {}", stackName, region); + } + + private Regions getRegion(DeleteStackCommand command) { + return command.getRegion() == null ? configStore.getPrimaryRegion() : Regions.fromName(command.getRegion()); } @Override public boolean isRunnable(DeleteStackCommand command) { - if (! cloudFormationService.isStackPresent(command.getStack().getFullName(environmentMetadata.getName()))) { - log.error("The stack: {} does not exists", command.getStack()); + Regions region = getRegion(command); + if (! cloudFormationService.isStackPresent(region, command.getStack().getFullName(environmentName))) { + log.error("The stack: {} does not exist in region: {}", command.getStack(), region.getName()); return false; } diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java index f1f154fd..6b02bfa4 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java @@ -16,12 +16,13 @@ package com.nike.cerberus.operation.core; +import com.github.tomaslanger.chalk.Chalk; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CertificateService; import com.nike.cerberus.service.ConsoleService; +import com.nike.cerberus.store.ConfigStore; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -29,11 +30,15 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import java.io.File; import java.util.HashSet; +import java.util.Optional; import java.util.Set; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; import static com.nike.cerberus.service.ConsoleService.DefaultAction.NO; +import static com.nike.cerberus.service.ConsoleService.DefaultAction.YES; /** * Operation that uses the cert service to generate the certificates needed to enable https in a Cerberus Env. @@ -42,17 +47,20 @@ public class GenerateCertificateFilesOperation implements Operation subjectAlternativeNames = new HashSet<>(); subjectAlternativeNames.addAll(parameters.getSubjectAlternativeNames()); @@ -131,6 +140,25 @@ public void run(GenerateCertificateFilesCommand command) { @Override public boolean isRunnable(GenerateCertificateFilesCommand command) { - return true; + boolean isRunnable = true; + if (command.getGenerateCertificateFilesCommandParametersDelegate().isTty()) { + boolean filesAlreadyPreset = true; + File certDir = new File(command.getGenerateCertificateFilesCommandParametersDelegate().getCertDir()); + try { + certService.checkForRequiredFiles(certDir); + } catch (RuntimeException e) { + filesAlreadyPreset = false; + } + if (filesAlreadyPreset) { + try { + consoleService.askUserToProceed( + Chalk.on(String.format("The required files already exists in %s would you like generate " + + "new files over these?", certDir.getAbsolutePath())).green().toString(), YES); + } catch (RuntimeException e) { + isRunnable = false; + } + } + } + return isRunnable; } -} +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java new file mode 100644 index 00000000..8f4415b3 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 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.InitializeEnvironmentCommand; +import com.nike.cerberus.domain.cloudformation.ConfigParameters; +import com.nike.cerberus.domain.cloudformation.ConfigOutputs; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +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.HashMap; +import java.util.Map; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + +/** + * Creates the base components via CloudFormation used by all of Cerberus. + */ +public class InitializeEnvironmentOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final String environmentName; + + private final CloudFormationService cloudFormationService; + + private final ConfigStore configStore; + + private final CloudFormationObjectMapper cloudFormationObjectMapper; + + @Inject + public InitializeEnvironmentOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + ConfigStore configStore, + CloudFormationObjectMapper cloudFormationObjectMapper) { + + this.environmentName = environmentName; + this.cloudFormationService = cloudFormationService; + this.configStore = configStore; + this.cloudFormationObjectMapper = cloudFormationObjectMapper; + } + + @Override + public void run(InitializeEnvironmentCommand command) { + Regions primaryRegion = Regions.fromName(command.getPrimaryRegion()); + + // create the global cms iam role + cloudFormationService.createStackAndWait(primaryRegion, Stack.IAM_ROLES, new HashMap<>(), true, + command.getTagsDelegate().getTags()); + + String cmsIamRoleArn = configStore.getCmsIamRoleOutputs(primaryRegion).getCmsIamRoleArn(); + + ConfigParameters configParameters = new ConfigParameters() + .setAccountAdminArn(command.getAdminRoleArn()) + .setCmsIamRoleArn(cmsIamRoleArn) + .setEnvironmentName(environmentName); + Map parameters = cloudFormationObjectMapper.convertValue(configParameters); + + Map regionConfigOutputsMap = new HashMap<>(); + // for each region, create a config bucket and kms cmk for encrypting environment data + command.getRegions().forEach(regionName -> { + Regions region = Regions.fromName(regionName); + cloudFormationService.createStackAndWait( + region, + Stack.CONFIG, parameters, true, + command.getTagsDelegate().getTags() + ); + regionConfigOutputsMap.put(region, configStore.getConfigBucketStackOutputs(region)); + }); + + configStore.initializeEnvironment(command.getAdminRoleArn(), cmsIamRoleArn, primaryRegion, regionConfigOutputsMap); + } + + @Override + public boolean isRunnable(InitializeEnvironmentCommand command) { + boolean isRunnable = true; + + if (command.getRegions().size() < 2) { + log.error("You must supply at least 2 regions so that config and secure data can be encrypted " + + "in a highly available manner"); + isRunnable = false; + } + + if (! command.getRegions().contains(command.getPrimaryRegion())) { + log.error("The primary region: {} must be in the region collection passed to the command. regions passed: {}", + command.getPrimaryRegion(), String.join(", ", command.getRegions())); + isRunnable = false; + } + + if (cloudFormationService.isStackPresent(Regions.fromName(command.getPrimaryRegion()), + Stack.IAM_ROLES.getFullName(environmentName))) { + + log.error("The IAM Role stack has already been created"); + isRunnable = false; + } + + + + return isRunnable; + } + +} diff --git a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java index aa7d7cc9..e127f39b 100644 --- a/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/PrintStackInfoOperation.java @@ -19,11 +19,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.nike.cerberus.command.core.PrintStackInfoCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.cloudformation.StackInfo; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AutoScalingService; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +34,7 @@ import java.util.Map; import static com.nike.cerberus.module.CerberusModule.CONFIG_OBJECT_MAPPER; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** * Prints the parameters and outputs for the specified component. @@ -44,7 +45,7 @@ public class PrintStackInfoOperation implements Operation private final Logger logger = LoggerFactory.getLogger(getClass()); - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final CloudFormationService cloudFormationService; @@ -52,35 +53,41 @@ public class PrintStackInfoOperation implements Operation private final ObjectMapper objectMapper; + private final ConfigStore configStore; + @Inject - public PrintStackInfoOperation(final EnvironmentMetadata environmentMetadata, - final CloudFormationService cloudFormationService, - final AutoScalingService autoScalingService, - @Named(CONFIG_OBJECT_MAPPER) final ObjectMapper objectMapper) { + public PrintStackInfoOperation(@Named(ENV_NAME) String environmentName, + CloudFormationService cloudFormationService, + AutoScalingService autoScalingService, + @Named(CONFIG_OBJECT_MAPPER) ObjectMapper objectMapper, + ConfigStore configStore) { - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; this.cloudFormationService = cloudFormationService; this.autoScalingService = autoScalingService; this.objectMapper = objectMapper; + this.configStore = configStore; } @Override public void run(final PrintStackInfoCommand command) { - final String stackId = command.getStack().getFullName(environmentMetadata.getName()); + String stackId = command.getStack().getFullName(environmentName); + + if (StringUtils.isBlank(stackId) || + !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), stackId)) { - if (StringUtils.isBlank(stackId) || !cloudFormationService.isStackPresent(stackId)) { logger.error("The specified environment doesn't contain a stack for " + command.getStack().getName()); return; } - final Map stackParameters = cloudFormationService.getStackParameters(stackId); - final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); - final StackInfo stackInfo = new StackInfo(); + Map stackParameters = cloudFormationService.getStackParameters(configStore.getPrimaryRegion(), stackId); + Map stackOutputs = cloudFormationService.getStackOutputs(configStore.getPrimaryRegion(), stackId); + StackInfo stackInfo = new StackInfo(); stackInfo.setStackId(stackId).setStackParameters(stackParameters).setStackOutputs(stackOutputs); - final String autoscalingGroupLogicalId = stackOutputs.get(ASG_LOGICAL_ID_OUTPUT_NAME); + String autoscalingGroupLogicalId = stackOutputs.get(ASG_LOGICAL_ID_OUTPUT_NAME); if (StringUtils.isNotBlank(autoscalingGroupLogicalId)) { - final List publicDnsForAutoScalingGroup = + List publicDnsForAutoScalingGroup = autoScalingService.getPublicDnsForAutoScalingGroup(autoscalingGroupLogicalId); stackInfo.setPublicDnsForInstances(publicDnsForAutoScalingGroup); } diff --git a/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java b/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java index 9eba1fd4..6bb6302d 100644 --- a/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/RebootCmsOperation.java @@ -17,15 +17,16 @@ package com.nike.cerberus.operation.core; import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.*; import com.github.tomaslanger.chalk.Chalk; import com.google.inject.Inject; import com.nike.cerberus.client.HttpClientFactory; import com.nike.cerberus.command.core.RebootCmsCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AutoScalingService; +import com.nike.cerberus.service.AwsClientFactory; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2Service; import com.nike.cerberus.store.ConfigStore; @@ -39,10 +40,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Named; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; import static com.nike.cerberus.service.CloudFormationService.MIN_INSTANCES_STACK_PARAMETER_KEY; import static com.nike.cerberus.service.Ec2Service.EC2_ASG_GROUP_NAME_TAG_KEY; import static com.nike.cerberus.service.Ec2Service.INSTANCE_STATE_FILTER_NAME; @@ -77,7 +80,7 @@ public class RebootCmsOperation implements Operation { private final AutoScalingService autoScalingService; - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final AmazonEC2 ec2Client; @@ -88,16 +91,16 @@ public RebootCmsOperation(ConfigStore configStore, CloudFormationService cloudFormationService, Ec2Service ec2Service, AutoScalingService autoScalingService, - EnvironmentMetadata environmentMetadata, - AmazonEC2 ec2Client, + @Named(ENV_NAME) String environmentName, + AwsClientFactory amazonS3ClientFactory, HttpClientFactory httpClientFactory) { this.configStore = configStore; this.cloudFormationService = cloudFormationService; this.ec2Service = ec2Service; this.autoScalingService = autoScalingService; - this.environmentMetadata = environmentMetadata; - this.ec2Client = ec2Client; + this.environmentName = environmentName; + this.ec2Client = amazonS3ClientFactory.getClient(configStore.getPrimaryRegion()); this.httpClientFactory = httpClientFactory; } @@ -117,10 +120,11 @@ public void run(final RebootCmsCommand command) { try { final Stack stack = Stack.CMS; - final String stackId = stack.getFullName(environmentMetadata.getName()); - final Map stackOutputs = cloudFormationService.getStackOutputs(stackId); + final String stackId = stack.getFullName(environmentName); + final Map stackOutputs = cloudFormationService.getStackOutputs(configStore.getPrimaryRegion(), stackId); - final Map stackParameters = cloudFormationService.getStackParameters(stackId); + final Map stackParameters = + cloudFormationService.getStackParameters(configStore.getPrimaryRegion(), stackId); final int minInstances = Integer.parseInt(stackParameters.get(MIN_INSTANCES_STACK_PARAMETER_KEY)); final String autoScalingGroupId = stackOutputs.get(CloudFormationService.AUTO_SCALING_GROUP_LOGICAL_ID_OUTPUT_KEY); @@ -254,8 +258,9 @@ private String getCurrentPublicIpAddress() { @Override public boolean isRunnable(final RebootCmsCommand command) { final Stack stack = Stack.CMS; - final String stackId = stack.getFullName(environmentMetadata.getName()); - final Map stackParameters = cloudFormationService.getStackParameters(stackId); + final String stackId = stack.getFullName(environmentName); + final Map stackParameters = + cloudFormationService.getStackParameters(configStore.getPrimaryRegion(), stackId); if (!stackParameters.containsKey(MIN_INSTANCES_STACK_PARAMETER_KEY)) { log.error("Could not find parameter 'minInstances' on stack: {}", stackId); diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 37089426..5e446fea 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -18,7 +18,6 @@ import com.amazonaws.AmazonServiceException; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CloudFormationService; @@ -29,7 +28,11 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import java.util.Map; +import java.util.Optional; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** * Operation for updating stacks. @@ -41,32 +44,34 @@ public class UpdateStackOperation implements Operation { private final CloudFormationService cloudFormationService; private final Ec2UserDataService ec2UserDataService; - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final ConfigStore configStore; @Inject - public UpdateStackOperation(final CloudFormationService cloudFormationService, - final Ec2UserDataService ec2UserDataService, - final EnvironmentMetadata environmentMetadata, - final ConfigStore configStore) { + public UpdateStackOperation(CloudFormationService cloudFormationService, + Ec2UserDataService ec2UserDataService, + @Named(ENV_NAME) String environmentName, + ConfigStore configStore) { this.cloudFormationService = cloudFormationService; this.ec2UserDataService = ec2UserDataService; - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; this.configStore = configStore; } @Override - public void run(final UpdateStackCommand command) { - final String stackId = command.getStack().getFullName(environmentMetadata.getName()); + public void run(UpdateStackCommand command) { + String stackId = command.getStack().getFullName(environmentName); - final Map parameters = cloudFormationService.getStackParameters(stackId); + Map parameters = cloudFormationService.getStackParameters(configStore.getPrimaryRegion(), stackId); + Map tags = command.getTagsDelegate().getTags(); // only some stacks need user data if (command.getStack().needsUserData()) { - parameters.put("userData", ec2UserDataService.getUserData(command.getStack())); + parameters.put("userData", ec2UserDataService.getUserData(configStore.getPrimaryRegion(), command.getStack(), + Optional.ofNullable(tags.getOrDefault("ownerGroup", null)))); } if (Stack.CMS.equals(command.getStack())) { @@ -83,8 +88,14 @@ public void run(final UpdateStackCommand command) { try { logger.info("Starting the update for '{}' overwrite:{}.", stackId, command.isOverwriteTemplate()); - cloudFormationService.updateStackAndWait(command.getStack(), parameters, true, command.isOverwriteTemplate(), - command.getTagsDelegate().getTags()); + cloudFormationService.updateStackAndWait( + configStore.getPrimaryRegion(), + command.getStack(), + parameters, + true, + command.isOverwriteTemplate(), + command.getTagsDelegate().getTags() + ); logger.info("Update complete."); } catch (AmazonServiceException ase) { @@ -98,11 +109,11 @@ public void run(final UpdateStackCommand command) { } @Override - public boolean isRunnable(final UpdateStackCommand command) { + public boolean isRunnable(UpdateStackCommand command) { boolean isRunnable = true; - String fullName = command.getStack().getFullName(environmentMetadata.getName()); - if (!cloudFormationService.isStackPresent(fullName)) { + String fullName = command.getStack().getFullName(environmentName); + if (!cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), fullName)) { logger.error("CloudFormation doesn't have the specified stack: {}", fullName); isRunnable = false; } diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java index 0fa0ad85..9894472b 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java @@ -17,10 +17,9 @@ package com.nike.cerberus.operation.core; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CertificateService; -import org.apache.commons.lang3.StringUtils; +import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,14 +32,14 @@ public class UploadCertificatesFilesOperation implements Operation amazonS3ClientFactory, + @Named(ENV_NAME) String environmentName) { + this.cloudFormationService = cloudFormationService; this.configStore = configStore; - this.ec2Client = ec2Client; - this.environmentMetadata = environmentMetadata; + this.ec2Client = amazonS3ClientFactory.getClient(configStore.getPrimaryRegion()); + this.environmentName = environmentName; } @Override - public void run(final WhitelistCidrForVpcAccessCommand command) { - final SecurityGroupOutputs securityGroupOutputs = configStore.getSecurityGroupStackOutputs(); + public void run(WhitelistCidrForVpcAccessCommand command) { + SecurityGroupOutputs securityGroupOutputs = configStore.getSecurityGroupStackOutputs(); logger.info("Revoking the previous ingress rules..."); - final DescribeSecurityGroupsResult securityGroupsResult = ec2Client.describeSecurityGroups( + DescribeSecurityGroupsResult securityGroupsResult = ec2Client.describeSecurityGroups( new DescribeSecurityGroupsRequest().withGroupIds(securityGroupOutputs.getWhitelistIngressSgId())); securityGroupsResult.getSecurityGroups().forEach(securityGroup -> { if (!securityGroup.getIpPermissions().isEmpty()) { @@ -80,7 +85,7 @@ public void run(final WhitelistCidrForVpcAccessCommand command) { logger.info("Done."); logger.info("Authorizing the new ingress rules..."); - final List ipPermissionList = Lists.newArrayListWithCapacity(command.getPorts().size()); + List ipPermissionList = Lists.newArrayListWithCapacity(command.getPorts().size()); command.getPorts().forEach(port -> { IpPermission ipPermission = new IpPermission() .withIpRanges(command.getCidrs()) @@ -99,11 +104,9 @@ public void run(final WhitelistCidrForVpcAccessCommand command) { } @Override - public boolean isRunnable(final WhitelistCidrForVpcAccessCommand command) { - String environmentName = environmentMetadata.getName(); - + public boolean isRunnable(WhitelistCidrForVpcAccessCommand command) { try { - cloudFormationService.getStackId(Stack.BASE.getFullName(environmentName)); + cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.IAM_ROLES.getFullName(environmentName)); } catch (IllegalArgumentException iae) { logger.error("Could not create the CMS cluster." + "Make sure the load balancer, security group, and base stacks have all been created.", iae); diff --git a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java index 7f66ab65..6239fb7a 100644 --- a/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java +++ b/src/main/java/com/nike/cerberus/service/AmiTagCheckService.java @@ -17,12 +17,15 @@ package com.nike.cerberus.service; import com.amazonaws.AmazonServiceException; +import com.amazonaws.regions.Regions; import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.DescribeImagesRequest; import com.amazonaws.services.ec2.model.DescribeImagesResult; import com.amazonaws.services.ec2.model.Filter; import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.store.ConfigStore; import javax.inject.Inject; import java.util.HashMap; @@ -33,31 +36,45 @@ */ public class AmiTagCheckService { - private final AmazonEC2 ec2Client; + private final AwsClientFactory amazonEC2ClientFactory; + private final ConfigStore configStore; private final Map stackAmiTagValueMap; @Inject - public AmiTagCheckService(final AmazonEC2 ec2Client) { - this.ec2Client = ec2Client; + public AmiTagCheckService(AwsClientFactory amazonEC2ClientFactory, + ConfigStore configStore) { + + this.amazonEC2ClientFactory = amazonEC2ClientFactory; + this.configStore = configStore; stackAmiTagValueMap = new HashMap<>(); stackAmiTagValueMap.put(Stack.CMS, ConfigConstants.CMS_AMI_TAG_VALUE); } /** - * Validates if the given AMI has given tag and value. + * Validates if the given AMI has given tag and value in the primary region. + * + * @return true if matches otherwise false + */ + public boolean isAmiWithTagExist(String amiId, String tagName, String tagValue) { + return isAmiWithTagExist(configStore.getPrimaryRegion(), amiId, tagName, tagValue); + } + + /** + * Validates if the given AMI has given tag and value in the provided region. * * @return true if matches otherwise false */ - public boolean isAmiWithTagExist(final String amiId, final String tagName, final String tagValue) { + public boolean isAmiWithTagExist(Regions region, String amiId, String tagName, String tagValue) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); - final DescribeImagesRequest request = new DescribeImagesRequest() + DescribeImagesRequest request = new DescribeImagesRequest() .withFilters(new Filter().withName(tagName).withValues(tagValue)) .withFilters(new Filter().withName("image-id").withValues(amiId)); try { - final DescribeImagesResult result = ec2Client.describeImages(request); + DescribeImagesResult result = ec2Client.describeImages(request); return result.getImages().size() > 0; } catch (final AmazonServiceException ase) { if (ase.getErrorCode() == "InvalidAMIID.NotFound") { diff --git a/src/main/java/com/nike/cerberus/service/AutoScalingService.java b/src/main/java/com/nike/cerberus/service/AutoScalingService.java index 7f2ec56f..e9878141 100644 --- a/src/main/java/com/nike/cerberus/service/AutoScalingService.java +++ b/src/main/java/com/nike/cerberus/service/AutoScalingService.java @@ -16,7 +16,8 @@ package com.nike.cerberus.service; -import com.amazonaws.services.autoscaling.AmazonAutoScaling; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.autoscaling.AmazonAutoScalingClient; import com.amazonaws.services.autoscaling.model.AutoScalingGroup; import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsRequest; import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsResult; @@ -24,9 +25,11 @@ import com.amazonaws.services.autoscaling.model.ExitStandbyRequest; import com.amazonaws.services.autoscaling.model.UpdateAutoScalingGroupRequest; import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.DescribeInstancesRequest; import com.amazonaws.services.ec2.model.DescribeInstancesResult; import com.beust.jcommander.internal.Lists; +import com.nike.cerberus.store.ConfigStore; import javax.inject.Inject; import java.util.List; @@ -37,34 +40,53 @@ */ public class AutoScalingService { - private final AmazonAutoScaling autoScalingClient; - - private final AmazonEC2 ec2Client; + private final AwsClientFactory autoScalingClientFactory; + private final AwsClientFactory amazonEC2ClientFactory; + private final ConfigStore configStore; @Inject - public AutoScalingService(final AmazonAutoScaling autoScalingClient, final AmazonEC2 ec2Client) { - this.autoScalingClient = autoScalingClient; - this.ec2Client = ec2Client; + public AutoScalingService(AwsClientFactory autoScalingClientFactory, + AwsClientFactory amazonEC2ClientFactory, + ConfigStore configStore) { + + this.autoScalingClientFactory = autoScalingClientFactory; + this.amazonEC2ClientFactory = amazonEC2ClientFactory; + this.configStore = configStore; } /** - * For a given AutoScaling group logical id, get the public dns names associated with each instance. + * For a given AutoScaling group logical id, get the public dns names associated with each instance in the + * primary region * * @param logicalId AutoScaling group logical id * @return List of public dns names */ - public List getPublicDnsForAutoScalingGroup(final String logicalId) { - final List instanceIds = Lists.newLinkedList(); - final Optional autoScalingGroup = describeAutoScalingGroup(logicalId); - final List publicDnsNames = Lists.newLinkedList(); + public List getPublicDnsForAutoScalingGroup(String logicalId) { + return getPublicDnsForAutoScalingGroup(configStore.getPrimaryRegion(), logicalId); + } + + /** + * For a given AutoScaling group logical id, get the public dns names associated with each instance in the + * provided region + * + * @param region The region to use + * @param logicalId AutoScaling group logical id + * @return List of public dns names + */ + public List getPublicDnsForAutoScalingGroup(Regions region, String logicalId) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); + + List instanceIds = Lists.newLinkedList(); + Optional autoScalingGroup = describeAutoScalingGroup(region, logicalId); + List publicDnsNames = Lists.newLinkedList(); if (autoScalingGroup.isPresent()) { autoScalingGroup.get() .getInstances().stream().forEach(instance -> instanceIds.add(instance.getInstanceId())); - final DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest() + DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest() .withInstanceIds(instanceIds); - final DescribeInstancesResult describeInstancesResult = + DescribeInstancesResult describeInstancesResult = ec2Client.describeInstances(describeInstancesRequest); describeInstancesResult.getReservations().forEach(reservation -> @@ -76,14 +98,25 @@ public List getPublicDnsForAutoScalingGroup(final String logicalId) { return publicDnsNames; } + /** + * Updates the minimum number of instances allowed in the auto scaling group in the primary region + * + * @param logicalId - Name of the auto scaling group + */ + public void updateMinInstancesForAutoScalingGroup(String logicalId, int minInstances) { + updateMinInstancesForAutoScalingGroup(configStore.getPrimaryRegion(), logicalId, minInstances); + } + /** * Updates the minimum number of instances allowed in the auto scaling group * + * @param region The region to use * @param logicalId - Name of the auto scaling group */ - public void updateMinInstancesForAutoScalingGroup(final String logicalId, final int minInstances) { + public void updateMinInstancesForAutoScalingGroup(Regions region, String logicalId, int minInstances) { + AmazonAutoScalingClient autoScalingClient = autoScalingClientFactory.getClient(region); - final UpdateAutoScalingGroupRequest request = new UpdateAutoScalingGroupRequest() + UpdateAutoScalingGroupRequest request = new UpdateAutoScalingGroupRequest() .withAutoScalingGroupName(logicalId) .withMinSize(minInstances); @@ -95,11 +128,28 @@ public void updateMinInstancesForAutoScalingGroup(final String logicalId, final * and a new instance is not spun up on instance reboot. This also removes the instance from the ELB, so that the * instance is not terminated when the health check fails. * + * uses the primary region + * + * @param logicalId - Name of the auto scaling group + * @param instanceId - ID of the EC2 instance + */ + public void setInstanceStateToStandby(String logicalId, String instanceId) { + setInstanceStateToStandby(configStore.getPrimaryRegion(), logicalId, instanceId); + } + + /** + * Set an EC2 instance to standby state, so that the desired instance count on the AutoScaling group is decreased + * and a new instance is not spun up on instance reboot. This also removes the instance from the ELB, so that the + * instance is not terminated when the health check fails. + * + * @param region The region to use * @param logicalId - Name of the auto scaling group * @param instanceId - ID of the EC2 instance */ - public void setInstanceStateToStandby(final String logicalId, final String instanceId) { - final EnterStandbyRequest request = new EnterStandbyRequest() + public void setInstanceStateToStandby(Regions region, String logicalId, String instanceId) { + AmazonAutoScalingClient autoScalingClient = autoScalingClientFactory.getClient(region); + + EnterStandbyRequest request = new EnterStandbyRequest() .withAutoScalingGroupName(logicalId) .withInstanceIds(instanceId) .withShouldDecrementDesiredCapacity(true); @@ -109,23 +159,46 @@ public void setInstanceStateToStandby(final String logicalId, final String insta /** * Signify that the EC2 instance is now in service and ready to be re-added to the ELB and AutoScaling group. This - * will also increase the desired instance count for the ASG. + * will also increase the desired instance count for the ASG in the primary region. * * @param logicalId - Name of the auto scaling group * @param instanceId - ID of the EC2 instance */ - public void setInstanceStateToInService(final String logicalId, final String instanceId) { - final ExitStandbyRequest request = new ExitStandbyRequest() + public void setInstanceStateToInService(String logicalId, String instanceId) { + setInstanceStateToInService(configStore.getPrimaryRegion(), logicalId, instanceId); + } + + /** + * Signify that the EC2 instance is now in service and ready to be re-added to the ELB and AutoScaling group. This + * will also increase the desired instance count for the ASG in the provided region. + * + * @param region The region to use + * @param logicalId - Name of the auto scaling group + * @param instanceId - ID of the EC2 instance + */ + public void setInstanceStateToInService(Regions region, String logicalId, String instanceId) { + + AmazonAutoScalingClient autoScalingClient = autoScalingClientFactory.getClient(region); + + ExitStandbyRequest request = new ExitStandbyRequest() .withAutoScalingGroupName(logicalId) .withInstanceIds(instanceId); autoScalingClient.exitStandby(request); } - private Optional describeAutoScalingGroup(final String autoscalingGroupName) { - final DescribeAutoScalingGroupsRequest describeAsg = new DescribeAutoScalingGroupsRequest() - .withAutoScalingGroupNames(autoscalingGroupName); - final DescribeAutoScalingGroupsResult result = autoScalingClient.describeAutoScalingGroups(describeAsg); + /** + * + * @param region The region to look for the ASG + * @param autoScalingGroupName The ASG name to look for. + * @return AutoScalingGroup details + */ + private Optional describeAutoScalingGroup(Regions region, String autoScalingGroupName) { + AmazonAutoScalingClient autoScalingClient = autoScalingClientFactory.getClient(region); + + DescribeAutoScalingGroupsRequest describeAsg = new DescribeAutoScalingGroupsRequest() + .withAutoScalingGroupNames(autoScalingGroupName); + DescribeAutoScalingGroupsResult result = autoScalingClient.describeAutoScalingGroups(describeAsg); return result.getAutoScalingGroups().stream().findFirst(); } diff --git a/src/main/java/com/nike/cerberus/service/AwsClientFactory.java b/src/main/java/com/nike/cerberus/service/AwsClientFactory.java new file mode 100644 index 00000000..ec545b0d --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/AwsClientFactory.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017 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.AmazonWebServiceClient; +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentialsProviderChain; +import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; +import com.amazonaws.auth.InstanceProfileCredentialsProvider; +import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider; +import com.amazonaws.auth.SystemPropertiesCredentialsProvider; +import com.amazonaws.auth.profile.ProfileCredentialsProvider; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.google.common.collect.Maps; +import com.google.common.reflect.TypeToken; + +import java.util.Map; +import java.util.UUID; + +/** + * Generic AWS Client factory that will create instances of AmazonWebServiceClients configured with the + * proper credentials provider chain and configuration + * + * @param The type of client that will be created. + */ +abstract public class AwsClientFactory { + + public static final String CERBERUS_ASSUME_ROLE_ARN = "CERBERUS_ASSUME_ROLE_ARN"; + public static final String CERBERUS_ASSUME_ROLE_EXTERNAL_ID = "CERBERUS_ASSUME_ROLE_EXTERNAL_ID"; + + /** + * Cache of clients by region + */ + private Map clients = Maps.newHashMap(); + + /** + * Factory that creates and caches Aws clients by region for re-use; + */ + public T getClient(Regions region) { + if (!clients.containsKey(region)) { + clients.put(region, createAmazonClientInstance(getGenericTypeClass(), region)); + } + return clients.get(region); + } + + @SuppressWarnings("unchecked") + private Class getGenericTypeClass() { + TypeToken typeToken = new TypeToken(getClass()) {}; + return (Class) typeToken.getRawType(); + } + + private M createAmazonClientInstance(Class clientClass, Regions region) { + return Region.getRegion(region) + .createClient(clientClass, getAWSCredentialsProviderChain(), getClientConfiguration()); + } + + private ClientConfiguration getClientConfiguration() { + return new ClientConfiguration(); + } + + private AWSCredentialsProviderChain getAWSCredentialsProviderChain() { + String cerberusRoleToAssume = System.getenv(CERBERUS_ASSUME_ROLE_ARN) != null ? + System.getenv(CERBERUS_ASSUME_ROLE_ARN) : ""; + String cerberusRoleToAssumeExternalId = System.getenv(CERBERUS_ASSUME_ROLE_EXTERNAL_ID) != null ? + System.getenv(CERBERUS_ASSUME_ROLE_EXTERNAL_ID) : ""; + + STSAssumeRoleSessionCredentialsProvider sTSAssumeRoleSessionCredentialsProvider = + new STSAssumeRoleSessionCredentialsProvider + .Builder(cerberusRoleToAssume, UUID.randomUUID().toString()) + .withExternalId(cerberusRoleToAssumeExternalId) + .build(); + + AWSCredentialsProviderChain chain = new AWSCredentialsProviderChain( + new EnvironmentVariableCredentialsProvider(), + new SystemPropertiesCredentialsProvider(), + new ProfileCredentialsProvider(), + sTSAssumeRoleSessionCredentialsProvider, + InstanceProfileCredentialsProvider.getInstance()); + + return chain; + } + +} diff --git a/src/main/java/com/nike/cerberus/service/CertificateService.java b/src/main/java/com/nike/cerberus/service/CertificateService.java index a4611560..7d3fc517 100644 --- a/src/main/java/com/nike/cerberus/service/CertificateService.java +++ b/src/main/java/com/nike/cerberus/service/CertificateService.java @@ -17,7 +17,9 @@ package com.nike.cerberus.service; import com.amazonaws.AmazonServiceException; +import com.amazonaws.regions.Regions; import com.amazonaws.services.route53.AmazonRoute53; +import com.amazonaws.services.route53.AmazonRoute53Client; import com.amazonaws.services.route53.model.Change; import com.amazonaws.services.route53.model.ChangeAction; import com.amazonaws.services.route53.model.ChangeBatch; @@ -31,7 +33,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.gson.Gson; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.CertificateInformation; import com.nike.cerberus.store.ConfigStore; import com.nike.cerberus.util.UuidSupplier; @@ -64,6 +65,7 @@ import org.xbill.DNS.Type; import javax.inject.Inject; +import javax.inject.Named; import java.io.File; import java.io.FileReader; import java.io.FileWriter; @@ -88,6 +90,9 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import static com.nike.cerberus.module.CerberusModule.CONFIG_REGION; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + /** * Service for managing Cerificates and calls to the ACME cert provider *

@@ -146,22 +151,24 @@ public class CertificateService { private final UuidSupplier uuidSupplier; private final ConfigStore configStore; private final IdentityManagementService identityManagementService; - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; @Inject public CertificateService(ConsoleService console, - AmazonRoute53 route53, + AwsClientFactory route53ClientFactory, UuidSupplier uuidSupplier, ConfigStore configStore, IdentityManagementService identityManagementService, - EnvironmentMetadata environmentMetadata) { + @Named(ENV_NAME) String environmentName, + @Named(CONFIG_REGION) String configRegion) { this.console = console; - this.route53 = route53; + // not a region specific AWS service so the config region is fine + this.route53 = route53ClientFactory.getClient(Regions.fromName(configRegion)); this.uuidSupplier = uuidSupplier; this.configStore = configStore; this.identityManagementService = identityManagementService; - this.environmentMetadata = environmentMetadata; + this.environmentName = environmentName; } /** @@ -515,12 +522,12 @@ public void uploadCertFiles(File certDir) { final String keyContents = getFileContents(certDir, DOMAIN_PKCS1_KEY_FILE); final String pkcs8KeyContents = getFileContents(certDir, DOMAIN_PKCS8_KEY_FILE); final String pubKeyContents = getFileContents(certDir, DOMAIN_PUBLIC_KEY_FILE); - final String certificateName = String.format("cerberus_%s_%s", environmentMetadata.getName(), uuidSupplier.get()); + final String certificateName = String.format("cerberus_%s_%s", environmentName, uuidSupplier.get()); log.info("Uploading certificate files to IAM with name of {}.", certificateName); - String identityManagementCertificateName = identityManagementService.uploadServerCertificate(certificateName, getPath(), + String certificateId = identityManagementService.uploadServerCertificate(certificateName, getPath(), certContents, caContents, keyContents); - log.info("Identity Management Cert Name: {}", identityManagementCertificateName); + log.info("Identity Management Cert Name: {}", certificateName); log.info("Sleeping to let iam cert become eventually consistent"); try { @@ -545,7 +552,8 @@ public void uploadCertFiles(File certDir) { } CertificateInformation certificateInformation = CertificateInformation.Builder.create() - .withCertificateName(identityManagementCertificateName) + .withCertificateName(certificateName) + .withCertificateId(certificateId) .withIdentityManagementCertificateArn(identityManagementService.getServerCertificateArn(certificateName).get()) .withCommonName(StringUtils.removeStart(certificate.getSubjectX500Principal().getName(), "CN=")) .withSubjectAlternateNames(sans) @@ -563,7 +571,7 @@ public void uploadCertFiles(File certDir) { * * @param certDir The directory that contains the required files */ - private void checkForRequiredFiles(File certDir) { + public void checkForRequiredFiles(File certDir) { final Set filenames = Sets.newHashSet(); @@ -598,7 +606,7 @@ private String getFileContents(final File certDir, final String filename) { } private String getPath() { - return "/cerberus/" + environmentMetadata.getName() + "/"; + return "/cerberus/" + environmentName + "/"; } /** @@ -607,6 +615,7 @@ private String getPath() { * @param certificateName the certificate to delete */ public void deleteCertificate(String certificateName) { + log.info("Attempting to delete certificate with name: {}", certificateName); try { identityManagementService.deleteServerCertificate(certificateName); } catch (AmazonServiceException e) { diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index 376ada6c..20fed817 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -18,7 +18,9 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.regions.Regions; import com.amazonaws.services.cloudformation.AmazonCloudFormation; +import com.amazonaws.services.cloudformation.AmazonCloudFormationClient; import com.amazonaws.services.cloudformation.model.CreateStackRequest; import com.amazonaws.services.cloudformation.model.CreateStackResult; import com.amazonaws.services.cloudformation.model.DeleteStackRequest; @@ -45,9 +47,9 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.UnexpectedCloudFormationStatusException; +import com.nike.cerberus.store.ConfigStore; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -56,6 +58,7 @@ import javax.annotation.Nullable; import javax.inject.Inject; +import javax.inject.Named; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -69,49 +72,53 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + /** * Service wrapper for AWS CloudFormation. */ public class CloudFormationService { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger log = LoggerFactory.getLogger(getClass()); public final static String AUTO_SCALING_GROUP_LOGICAL_ID_OUTPUT_KEY = "autoscalingGroupLogicalId"; public final static String MIN_INSTANCES_STACK_PARAMETER_KEY = "minimumInstances"; - private final AmazonCloudFormation cloudFormationClient; - - private final EnvironmentMetadata environmentMetadata; + private final AwsClientFactory cloudFormationClientFactory; - private final AmazonCloudFormationWaiters waiters; + private final String environmentName; @Inject - public CloudFormationService(final AmazonCloudFormation cloudFormationClient, - final EnvironmentMetadata environmentMetadata) { - this.cloudFormationClient = cloudFormationClient; - this.environmentMetadata = environmentMetadata; + public CloudFormationService(AwsClientFactory cloudFormationClientFactory, + @Named(ENV_NAME) String environmentName) { - waiters = new AmazonCloudFormationWaiters(cloudFormationClient); + this.cloudFormationClientFactory = cloudFormationClientFactory; + this.environmentName = environmentName; } /** - * Creates a new stack. + * Creates a new stack in the provided region. * - * @param parameters Input parameters. + * @param region The region for the stack + * @param stack the stack that is being updated + * @param parameters the parameters to send to the cloud formation + * @param iamCapabilities flag for iam capabilities + * @param globalTags map of tags to apply to all resources created/updated * @return Stack ID */ - public String createStackAndWait(final Stack stack, - final Map parameters, - final boolean iamCapabilities, - final Map globalTags) { + public String createStackAndWait(Regions region, + Stack stack, + Map parameters, + boolean iamCapabilities, + Map globalTags) { - String stackName = stack.getFullName(environmentMetadata.getName()); + String stackName = stack.getFullName(environmentName); - logger.info(String.format("Executing the Cloud Formation: %s, Stack Name: %s", stack.getTemplatePath(), stackName)); + log.info("Creating: Cloud Formation Template: {}, Stack Name: {}, Region: {}", stack.getTemplatePath(), stackName, region.getName()); - final CreateStackRequest request = new CreateStackRequest() - .withStackName(stack.getFullName(environmentMetadata.getName())) + CreateStackRequest request = new CreateStackRequest() + .withStackName(stack.getFullName(environmentName)) .withParameters(convertParameters(parameters)) .withTemplateBody(stack.getTemplateText()) .withTags(getTags(globalTags)); @@ -120,42 +127,43 @@ public String createStackAndWait(final Stack stack, request.getCapabilities().add("CAPABILITY_IAM"); } - final CreateStackResult result = cloudFormationClient.createStack(request); - - waitAndPrintCFEvents(stackName, waiters.stackCreateComplete()); + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); + CreateStackResult result = cloudFormationClient.createStack(request); + waitAndPrintCFEvents(region, stackName, new AmazonCloudFormationWaiters(cloudFormationClient).stackCreateComplete()); return result.getStackId(); } /** * Uses AWS CF Aync Waiters to wait for Cloud Formation actions to complete, while logging events and verifying success + * + * @param region The Region to use * @param stackName The stack that is having an action performed * @param waiter The Amazon waiter */ - private void waitAndPrintCFEvents(String stackName, Waiter waiter) { + private void waitAndPrintCFEvents(Regions region, String stackName, Waiter waiter) { SuccessTrackingWaiterHandler handler = new SuccessTrackingWaiterHandler(); @SuppressWarnings("unchecked") Future future = waiter .runAsync(new WaiterParameters<>(new DescribeStacksRequest().withStackName(stackName)), handler); + Set recordedStackEvents = Sets.newHashSet(); do { try { DateTime now = DateTime.now(DateTimeZone.UTC).minusSeconds(10); - Set recordedStackEvents = Sets.newHashSet(); TimeUnit.SECONDS.sleep(5); - StackStatus stackStatus = getStackStatus(stackName); - + StackStatus stackStatus = getStackStatus(region, stackName); if (stackStatus != null) { - List stackEvents = getStackEvents(stackName); + List stackEvents = getStackEvents(region, stackName); stackEvents.sort(Comparator.comparing(StackEvent::getTimestamp)); for (StackEvent stackEvent : stackEvents) { DateTime eventTime = new DateTime(stackEvent.getTimestamp()); if (!recordedStackEvents.contains(stackEvent.getEventId()) && now.isBefore(eventTime)) { - logger.info( + log.info( String.format("TS: %s, Status: %s, Type: %s, Reason: %s", Chalk.on(stackEvent.getTimestamp().toString()).yellow(), getStatusColor(stackEvent.getResourceStatus()), @@ -167,10 +175,10 @@ private void waitAndPrintCFEvents(String stackName, Waiter waiter) { } } } catch (InterruptedException e) { - logger.error("Polling interrupted", e); + log.error("Polling interrupted", e); Thread.currentThread().interrupt(); } catch (Exception e) { - logger.error("Failed to poll and print stack", e); + log.error("Failed to poll and print stack", e); } } while (!future.isDone()); @@ -181,16 +189,26 @@ private void waitAndPrintCFEvents(String stackName, Waiter waiter) { } /** - * Updates an existing stack + * Updates an existing stack in the provided region + * @param region The region for the stack + * @param stack the stack that is being updated + * @param parameters the parameters to send to the cloud formation + * @param iamCapabilities flag for iam capabilities + * @param overwrite overwrite the deployed template with the current template in the cli + * @param globalTags map of tags to apply to all resources created/updated */ - public void updateStackAndWait(final Stack stack, - final Map parameters, - final boolean iamCapabilities, - final boolean overwrite, - final Map globalTags) { - - String stackName = stack.getFullName(environmentMetadata.getName()); - final UpdateStackRequest request = new UpdateStackRequest() + public void updateStackAndWait(Regions region, + Stack stack, + Map parameters, + boolean iamCapabilities, + boolean overwrite, + Map globalTags) { + + String stackName = stack.getFullName(environmentName); + + log.info("Updating: Cloud Formation Template: {}, Stack Name: {}, Region: {}", stack.getTemplatePath(), stackName, region.getName()); + + UpdateStackRequest request = new UpdateStackRequest() .withStackName(stackName) .withParameters(convertParameters(parameters)); @@ -206,44 +224,50 @@ public void updateStackAndWait(final Stack stack, request.setTags(getTags(globalTags)); + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); cloudFormationClient.updateStack(request); - waitAndPrintCFEvents(stackName, waiters.stackUpdateComplete()); + waitAndPrintCFEvents(region, stackName, new AmazonCloudFormationWaiters(cloudFormationClient).stackUpdateComplete()); } /** - * Deletes an existing stack by name. + * Deletes an existing stack by name in the region provided. * + * @param region The Region to use * @param stackName Stack ID. */ - public void deleteStackAndWait(final String stackName) { - final DeleteStackRequest request = new DeleteStackRequest().withStackName(stackName); + public void deleteStackAndWait(Regions region, String stackName) { + log.info("Deleting the Stack Name: {}, Region: {}", stackName, region.getName()); + DeleteStackRequest request = new DeleteStackRequest().withStackName(stackName); + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); cloudFormationClient.deleteStack(request); - waitAndPrintCFEvents(stackName, waiters.stackDeleteComplete()); + waitAndPrintCFEvents(region, stackName, new AmazonCloudFormationWaiters(cloudFormationClient).stackDeleteComplete()); } /** * Returns the current status of the named stack. * + * @param region The Region to use * @param stackName Stack ID. * @return Stack status data. */ @Nullable - public StackStatus getStackStatus(final String stackName) { - final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); + public StackStatus getStackStatus(Regions region, String stackName) { + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); + DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); try { - final DescribeStacksResult result = cloudFormationClient.describeStacks(request); + DescribeStacksResult result = cloudFormationClient.describeStacks(request); if (result.getStacks().size() > 0) { - final String status = result.getStacks().get(0).getStackStatus(); + String status = result.getStacks().get(0).getStackStatus(); if (StringUtils.isNotBlank(status)) { return StackStatus.fromValue(status); } } - } catch (final AmazonServiceException ase) { + } catch (AmazonServiceException ase) { // Stack doesn't exist, just return with no status if (ase.getStatusCode() != 400) { throw ase; @@ -254,15 +278,17 @@ public StackStatus getStackStatus(final String stackName) { } /** - * Returns the current status of the named stack. + * Returns the current status of the named stack in the provided region. * + * @param region The Region to use * @param stackName Stack name. * @return Stack outputs data. */ - public Map getStackParameters(final String stackName) { - final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); - final DescribeStacksResult result = cloudFormationClient.describeStacks(request); - final Map parameters = Maps.newHashMap(); + public Map getStackParameters(Regions region, String stackName) { + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); + DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); + DescribeStacksResult result = cloudFormationClient.describeStacks(request); + Map parameters = Maps.newHashMap(); if (result.getStacks().size() > 0) { parameters.putAll(result.getStacks().get(0).getParameters().stream().collect( @@ -276,13 +302,15 @@ public Map getStackParameters(final String stackName) { /** * Returns the current status of the named stack. * + * @param region The Region to use * @param stackName Stack name. * @return Stack outputs data. */ - public Map getStackOutputs(final String stackName) { - final DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); - final DescribeStacksResult result = cloudFormationClient.describeStacks(request); - final Map outputs = Maps.newHashMap(); + public Map getStackOutputs(Regions region, String stackName) { + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); + DescribeStacksRequest request = new DescribeStacksRequest().withStackName(stackName); + DescribeStacksResult result = cloudFormationClient.describeStacks(request); + Map outputs = Maps.newHashMap(); if (result.getStacks().size() > 0) { outputs.putAll(result.getStacks().get(0).getOutputs().stream().collect( @@ -293,7 +321,15 @@ public Map getStackOutputs(final String stackName) { return outputs; } - public List getStackResources(String stackName) { + /** + * Returns list of StackResourceSummary for stack provided in region provided + * + * @param region The region to use + * @param stackName The stack name to use + * @return list of StackResourceSummary + */ + public List getStackResources(Regions region, String stackName) { + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); List stackResourceSummaries = new LinkedList<>(); ListStackResourcesResult result; do { @@ -310,15 +346,17 @@ public List getStackResources(String stackName) { * Returns the events for a named stack. * * @param stackName Stack ID. + * @param region The Region to use * @return Collection of events */ - public List getStackEvents(final String stackName) { - final DescribeStackEventsRequest request = new DescribeStackEventsRequest().withStackName(stackName); + public List getStackEvents(Regions region, String stackName) { + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); + DescribeStackEventsRequest request = new DescribeStackEventsRequest().withStackName(stackName); try { - final DescribeStackEventsResult result = cloudFormationClient.describeStackEvents(request); + DescribeStackEventsResult result = cloudFormationClient.describeStackEvents(request); return result.getStackEvents(); - } catch (final AmazonServiceException ase) { + } catch (AmazonServiceException ase) { // Stack doesn't exist, just return with no status if (ase.getStatusCode() != 400) { throw ase; @@ -329,12 +367,14 @@ public List getStackEvents(final String stackName) { } /** - * Get the full ID of the stack + * Get stack id for stack in region provided. * - * @param stackName - The stack logical id / full stack name - * @return + * @param stackName The stack logical id / full stack name + * @param region The Region to use + * @return The full ID of the stack */ - public String getStackId(String stackName) { + public String getStackId(Regions region, String stackName) { + AmazonCloudFormation cloudFormationClient = cloudFormationClientFactory.getClient(region); Preconditions.checkArgument(StringUtils.isNotBlank(stackName), "Stack name cannot be blank"); DescribeStacksResult result = cloudFormationClient.describeStacks( @@ -345,7 +385,7 @@ public String getStackId(String stackName) { if (stacks.isEmpty()) { throw new IllegalArgumentException("No stack found with name: " + stackName); } else if (stacks.size() > 1) { - logger.warn("Found more than stack with name: {}. Selecting the first one: stack id: {}", + log.warn("Found more than stack with name: {}. Selecting the first one: stack id: {}", stackName, stacks.get(0).getStackId()); } @@ -356,13 +396,14 @@ public String getStackId(String stackName) { /** * Checks if a stack exists with the specified ID. * + * @param region The Region to use * @param stackName Stack ID. * @return boolean */ - public boolean isStackPresent(final String stackName) { + public boolean isStackPresent(Regions region, String stackName) { Preconditions.checkArgument(StringUtils.isNotBlank(stackName), "Stack ID cannot be blank"); - return getStackStatus(stackName) != null; + return getStackStatus(region, stackName) != null; } /** @@ -371,11 +412,11 @@ public boolean isStackPresent(final String stackName) { * @param parameterMap Map to be converted. * @return Collection of parameters. */ - public Collection convertParameters(final Map parameterMap) { - final List parameterList = Lists.newArrayListWithCapacity(parameterMap.size()); + public Collection convertParameters(Map parameterMap) { + List parameterList = Lists.newArrayListWithCapacity(parameterMap.size()); for (Map.Entry entry : parameterMap.entrySet()) { - final Parameter param = new Parameter() + Parameter param = new Parameter() .withParameterKey(entry.getKey()) .withParameterValue(entry.getValue()); parameterList.add(param); @@ -384,35 +425,6 @@ public Collection convertParameters(final Map paramet return parameterList; } - - /** - * Since there doesn't appear to be a first class way through the SDK at this time to get a CF export. We can - * iterate through the stacks for a given output key and return the value. - * - * @param outputKey The exported CF variable to search and retrieve the value of. - * @return The value for the export if found - */ - public Optional searchStacksForOutput(String outputKey) { - DescribeStacksResult describeStacksResult = null; - do { - DescribeStacksRequest request = new DescribeStacksRequest(); - if (describeStacksResult != null && describeStacksResult.getNextToken() != null) { - request.withNextToken(describeStacksResult.getNextToken()); - } - describeStacksResult = cloudFormationClient.describeStacks(); - for (com.amazonaws.services.cloudformation.model.Stack stack : describeStacksResult.getStacks()) { - for (Output output : stack.getOutputs()) { - if (StringUtils.equals(output.getOutputKey(), outputKey)) { - return Optional.of(output.getOutputValue()); - } - } - } - - } while (describeStacksResult.getNextToken() != null); - - return Optional.empty(); - } - /** * Takes a map of key values and converts them to a collection of tags adding more global tags * @@ -427,7 +439,7 @@ private Collection getTags(Map globalTags) { tags.add(new Tag() .withKey("Name") - .withValue(ConfigConstants.ENV_PREFIX + environmentMetadata.getName()) + .withValue(ConfigConstants.ENV_PREFIX + environmentName) ); return tags; diff --git a/src/main/java/com/nike/cerberus/service/Ec2Service.java b/src/main/java/com/nike/cerberus/service/Ec2Service.java index 5ac9ab95..2a6adaab 100644 --- a/src/main/java/com/nike/cerberus/service/Ec2Service.java +++ b/src/main/java/com/nike/cerberus/service/Ec2Service.java @@ -17,7 +17,9 @@ package com.nike.cerberus.service; import com.amazonaws.AmazonServiceException; +import com.amazonaws.regions.Regions; import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.AvailabilityZone; import com.amazonaws.services.ec2.model.AvailabilityZoneState; import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult; @@ -32,6 +34,8 @@ import com.amazonaws.services.ec2.model.RebootInstancesRequest; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; import java.util.List; @@ -51,40 +55,70 @@ public class Ec2Service { protected final static String FILTER_NAME_TEMPL_FOR_EC2_TAGS = "tag:%s"; - private final AmazonEC2 ec2Client; + private final AwsClientFactory amazonEC2ClientFactory; + + private final ConfigStore configStore; @Inject - public Ec2Service(final AmazonEC2 ec2Client) { - this.ec2Client = ec2Client; + public Ec2Service(AwsClientFactory amazonEC2ClientFactory, + ConfigStore configStore) { + + this.amazonEC2ClientFactory = amazonEC2ClientFactory; + this.configStore = configStore; + } + + /** + * Import a key pair to AWS EC2 in the primary region. + * + * @param keyName Friendly name for the key + * @param publicKeyMaterial Public key + * @return Key name + */ + public String importKey(String keyName, String publicKeyMaterial) { + return importKey(configStore.getPrimaryRegion(), keyName, publicKeyMaterial); } /** * Import a key pair to AWS EC2. * + * @param region The region to use. * @param keyName Friendly name for the key * @param publicKeyMaterial Public key * @return Key name */ - public String importKey(final String keyName, final String publicKeyMaterial) { - final ImportKeyPairRequest request = new ImportKeyPairRequest(keyName, publicKeyMaterial); - final ImportKeyPairResult result = ec2Client.importKeyPair(request); + public String importKey(Regions region, String keyName, String publicKeyMaterial) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); + ImportKeyPairRequest request = new ImportKeyPairRequest(keyName, publicKeyMaterial); + ImportKeyPairResult result = ec2Client.importKeyPair(request); return result.getKeyName(); } /** - * Checks if a key pair is present in AWS EC2. + * Checks if a key pair is present in AWS EC2 in the primary region * * @param keyName Friendly name for the key * @return If present */ - public boolean isKeyPairPresent(final String keyName) { - final DescribeKeyPairsRequest request = new DescribeKeyPairsRequest().withKeyNames(keyName); + public boolean isKeyPairPresent(String keyName) { + return isKeyPairPresent(configStore.getPrimaryRegion(), keyName); + } + + /** + * Checks if a key pair is present in AWS EC2 in the provided region + * + * @param region The region to use. + * @param keyName Friendly name for the key + * @return If present + */ + public boolean isKeyPairPresent(Regions region, String keyName) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); + DescribeKeyPairsRequest request = new DescribeKeyPairsRequest().withKeyNames(keyName); try { - final DescribeKeyPairsResult result = ec2Client.describeKeyPairs(request); + DescribeKeyPairsResult result = ec2Client.describeKeyPairs(request); return result.getKeyPairs().size() > 0; - } catch (final AmazonServiceException ase) { - if (ase.getErrorCode() == "InvalidKeyPair.NotFound") { + } catch (AmazonServiceException ase) { + if (StringUtils.equals(ase.getErrorCode(),"InvalidKeyPair.NotFound")) { return false; } @@ -93,12 +127,23 @@ public boolean isKeyPairPresent(final String keyName) { } /** - * Determines all availability zones for a region that are marked as available. + * Determines all availability zones for a region that are marked as available in the primary region. * * @return List of availability zones */ public List getAvailabilityZones() { - final DescribeAvailabilityZonesResult result = ec2Client.describeAvailabilityZones(); + return getAvailabilityZones(configStore.getPrimaryRegion()); + } + + /** + * Determines all availability zones for a region that are marked as available, in the provided region. + * + * @param region The region to use. + * @return List of availability zones + */ + public List getAvailabilityZones(Regions region) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); + DescribeAvailabilityZonesResult result = ec2Client.describeAvailabilityZones(); return result.getAvailabilityZones() .stream() @@ -106,21 +151,35 @@ public List getAvailabilityZones() { .map(AvailabilityZone::getZoneName).collect(Collectors.toList()); } + /** + * Gets all EC2 instances with the given tag key/value pair in the primary region + * + * @param tagKey - Key of the tag + * @param tagValue - Value of the tag + * @param filters - Array of EC2 filters + * @return - List of instances with the given tag + */ + public List getInstancesByTag(String tagKey, String tagValue, Filter... filters) { + return getInstancesByTag(configStore.getPrimaryRegion(), tagKey, tagValue, filters); + } + /** * Gets all EC2 instances with the given tag key/value pair * + * @param region The region to use. * @param tagKey - Key of the tag * @param tagValue - Value of the tag * @param filters - Array of EC2 filters * @return - List of instances with the given tag */ - public List getInstancesByTag(final String tagKey, final String tagValue, final Filter... filters) { - final String filterName = String.format(FILTER_NAME_TEMPL_FOR_EC2_TAGS, tagKey); - final Filter tagFilter = new Filter().withName(filterName).withValues(tagValue); + public List getInstancesByTag(Regions region, String tagKey, String tagValue, Filter... filters) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); + String filterName = String.format(FILTER_NAME_TEMPL_FOR_EC2_TAGS, tagKey); + Filter tagFilter = new Filter().withName(filterName).withValues(tagValue); - final Set filterSet = Sets.newHashSet(filters); + Set filterSet = Sets.newHashSet(filters); filterSet.add(tagFilter); - final DescribeInstancesRequest request = new DescribeInstancesRequest().withFilters(filterSet); + DescribeInstancesRequest request = new DescribeInstancesRequest().withFilters(filterSet); DescribeInstancesResult result = ec2Client.describeInstances(request); List instances = Lists.newArrayList(); @@ -137,10 +196,19 @@ public List getInstancesByTag(final String tagKey, final String tagVal * * @param instanceId - EC2 instance ID */ - public void rebootEc2Instance(final String instanceId) { - - final RebootInstancesRequest request = new RebootInstancesRequest().withInstanceIds(instanceId); + public void rebootEc2Instance(String instanceId) { + rebootEc2Instance(configStore.getPrimaryRegion(), instanceId); + } + /** + * Reboots the EC2 instance with the given ID + * + * @param region The region to use. + * @param instanceId - EC2 instance ID + */ + public void rebootEc2Instance(Regions region, String instanceId) { + AmazonEC2 ec2Client = amazonEC2ClientFactory.getClient(region); + RebootInstancesRequest request = new RebootInstancesRequest().withInstanceIds(instanceId); ec2Client.rebootInstances(request); } diff --git a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java index 3796314a..ecacc08c 100644 --- a/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java +++ b/src/main/java/com/nike/cerberus/service/Ec2UserDataService.java @@ -16,59 +16,68 @@ package com.nike.cerberus.service; +import com.amazonaws.regions.Regions; import com.google.common.collect.Maps; import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.domain.EnvironmentMetadata; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.store.ConfigStore; import javax.inject.Inject; +import javax.inject.Named; import java.nio.charset.Charset; import java.util.Base64; import java.util.Map; +import java.util.Optional; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** * Service for generating EC2 user data for Cerberus instances. */ public class Ec2UserDataService { - private final EnvironmentMetadata environmentMetadata; + private final String environmentName; private final ConfigStore configStore; @Inject - public Ec2UserDataService(final EnvironmentMetadata environmentMetadata, - final ConfigStore configStore) { - this.environmentMetadata = environmentMetadata; + public Ec2UserDataService(@Named(ENV_NAME) String environmentName, + ConfigStore configStore) { + + this.environmentName = environmentName; this.configStore = configStore; } - public String getUserData(final Stack stack) { + public String getUserData(Regions region, Stack stack, Optional group) { if (stack.equals(Stack.CMS)) { - return getCmsUserData(); + return getCmsUserData(region, group); } else { throw new IllegalArgumentException("The stack specified does not support user data. stack: " + stack.getName()); } } - private String getCmsUserData() { - final Map userDataMap = Maps.newHashMap(); - addStandardEnvironmentVariables(userDataMap, Stack.CMS.getName()); + private String getCmsUserData(Regions region, Optional group) { + Map userDataMap = Maps.newHashMap(); + addStandardEnvironmentVariables(region, userDataMap, Stack.CMS.getName(), group); return encodeUserData(writeExportEnvVars(userDataMap)); } - private void addStandardEnvironmentVariables(final Map userDataMap, - final String appName) { - userDataMap.put("CLOUD_ENVIRONMENT", ConfigConstants.ENV_PREFIX + environmentMetadata.getName()); + private void addStandardEnvironmentVariables(Regions region, + Map userDataMap, + String appName, + Optional group) { + + userDataMap.put("CLOUD_ENVIRONMENT", ConfigConstants.ENV_PREFIX + environmentName); userDataMap.put("CLOUD_MONITOR_BUCKET", appName); userDataMap.put("CLOUD_APP", appName); + userDataMap.put("CLOUD_APP_GROUP", group.orElse("cerberus")); userDataMap.put("CLOUD_CLUSTER", appName); userDataMap.put("CLASSIFICATION", "Gold"); - userDataMap.put("EC2_REGION", environmentMetadata.getRegionName()); - userDataMap.put("AWS_REGION", environmentMetadata.getRegionName()); - userDataMap.put("CONFIG_S3_BUCKET", environmentMetadata.getBucketName()); - userDataMap.put("CONFIG_KEY_ID", configStore.getBaseStackOutputs().getConfigFileKeyId()); + userDataMap.put("EC2_REGION", region.getName()); + userDataMap.put("AWS_REGION", region.getName()); + userDataMap.put("CONFIG_S3_BUCKET", configStore.getConfigBucketForRegion(region)); + userDataMap.put("CONFIG_KEY_ID", configStore.getEnvironmentDataSecureDataKmsCmkRegion(region)); } private String writeExportEnvVars(Map userDataMap) { diff --git a/src/main/java/com/nike/cerberus/service/EncryptionService.java b/src/main/java/com/nike/cerberus/service/EncryptionService.java new file mode 100644 index 00000000..abe374ae --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/EncryptionService.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017 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.encryptionsdk.AwsCrypto; +import com.amazonaws.encryptionsdk.MasterKeyProvider; +import com.amazonaws.encryptionsdk.ParsedCiphertext; +import com.amazonaws.encryptionsdk.kms.KmsMasterKey; +import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; +import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory; +import com.nike.cerberus.util.CiphertextUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +public class EncryptionService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final AwsCrypto awsCrypto; + + @Inject + public EncryptionService(AwsCrypto awsCrypto) { + this.awsCrypto = awsCrypto; + } + + /** + * Encrypt the plainTextPayload. + *

+ * Generates a Base64 encoded String the the 'AWS Encryption SDK Message Format' + *

+ * http://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html + * + * @param plainTextPayload the secrets to encrypt + */ + public String encrypt(MasterKeyProvider encryptProvider, String plainTextPayload) { + return awsCrypto.encryptString(encryptProvider, plainTextPayload).getResult(); + } + + /** + * Decrypt the encryptedPayload. + *

+ * Expects a Base64 encoded String the the 'AWS Encryption SDK Message Format'. + *

+ * http://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html + * + * @param encryptedPayload encryptedPayload + */ + public String decrypt(String encryptedPayload) { + ParsedCiphertext parsedCiphertext = CiphertextUtils.parse(encryptedPayload); + // Parses the ARNs out of the encryptedPayload so that you can manually rotate the CMKs, if desired + // Whatever CMKs were used in the encrypt operation will be used to decrypt + try { + List cmkArns = CiphertextUtils.getCustomerMasterKeyArns(parsedCiphertext); + MasterKeyProvider decryptProvider = initializeKeyProvider(cmkArns); + return new String(awsCrypto.decryptData(decryptProvider, parsedCiphertext).getResult(), StandardCharsets.UTF_8); + } catch (RuntimeException e) { + log.error("Decrypt operation failed " + CiphertextUtils.toJson(parsedCiphertext), e); + throw e; + } + } + + /** + * Initialize a Multi-KMS-MasterKeyProvider. + *

+ * For encrypt, KMS in all regions must be available. + * For decrypt, KMS in at least one region must be available. + */ + protected MasterKeyProvider initializeKeyProvider(List cmkArns) { + List> providers = cmkArns.stream() + .map(KmsMasterKeyProvider::new) + .collect(Collectors.toList()); + return (MasterKeyProvider) MultipleProviderFactory.buildMultiProvider(providers); + } +} diff --git a/src/main/java/com/nike/cerberus/service/IdentityManagementService.java b/src/main/java/com/nike/cerberus/service/IdentityManagementService.java index 048b0691..4a3f8b8d 100644 --- a/src/main/java/com/nike/cerberus/service/IdentityManagementService.java +++ b/src/main/java/com/nike/cerberus/service/IdentityManagementService.java @@ -16,7 +16,9 @@ package com.nike.cerberus.service; +import com.amazonaws.regions.Regions; import com.amazonaws.services.identitymanagement.AmazonIdentityManagement; +import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; import com.amazonaws.services.identitymanagement.model.DeleteServerCertificateRequest; import com.amazonaws.services.identitymanagement.model.GetServerCertificateRequest; import com.amazonaws.services.identitymanagement.model.GetServerCertificateResult; @@ -25,8 +27,11 @@ import com.amazonaws.services.identitymanagement.model.UploadServerCertificateResult; import javax.inject.Inject; +import javax.inject.Named; import java.util.Optional; +import static com.nike.cerberus.module.CerberusModule.CONFIG_REGION; + /** * Wrapper for AWS IAM. */ @@ -35,8 +40,11 @@ public class IdentityManagementService { private final AmazonIdentityManagement client; @Inject - public IdentityManagementService(final AmazonIdentityManagement client) { - this.client = client; + public IdentityManagementService(AwsClientFactory identityManagementClientFactory, + @Named(CONFIG_REGION) String configRegion) { + + // IAM is not region specific, the config region will suffice. + client = identityManagementClientFactory.getClient(Regions.fromName(configRegion)); } /** @@ -49,20 +57,20 @@ public IdentityManagementService(final AmazonIdentityManagement client) { * @param key PEM-encoded certificate key. * @return The server certificate ID of the uploaded certificate. */ - public String uploadServerCertificate( - final String name, - final String path, - final String body, - final String chain, - final String key) { - final UploadServerCertificateRequest request = new UploadServerCertificateRequest() + public String uploadServerCertificate(String name, + String path, + String body, + String chain, + String key) { + + UploadServerCertificateRequest request = new UploadServerCertificateRequest() .withServerCertificateName(name) .withPath(sanitizePath(path)) .withCertificateBody(body) .withCertificateChain(chain) .withPrivateKey(key); - final UploadServerCertificateResult result = client.uploadServerCertificate(request); + UploadServerCertificateResult result = client.uploadServerCertificate(request); return result.getServerCertificateMetadata().getServerCertificateId(); } @@ -72,54 +80,22 @@ public String uploadServerCertificate( * * @param name The server certificate name */ - public void deleteServerCertificate(final String name) { + public void deleteServerCertificate(String name) { client.deleteServerCertificate(new DeleteServerCertificateRequest().withServerCertificateName(name)); } - /** - * Checks if the server certificate is present. - * - * @param name The server certificate name - * @return If present - */ - public boolean isServerCertificatePresent(final String name) { - try { - client.getServerCertificate(new GetServerCertificateRequest().withServerCertificateName(name)); - return true; - } catch (final NoSuchEntityException nsee) { - return false; - } - } - /** * Gets the ARN for the specified server certificate name. * * @param name The server certificate name * @return ARN */ - public Optional getServerCertificateArn(final String name) { + public Optional getServerCertificateArn(String name) { try { - final GetServerCertificateResult serverCertificateResult = + GetServerCertificateResult serverCertificateResult = client.getServerCertificate(new GetServerCertificateRequest().withServerCertificateName(name)); return Optional.of(serverCertificateResult.getServerCertificate().getServerCertificateMetadata().getArn()); - } catch (final NoSuchEntityException nsee) { - return Optional.empty(); - } - } - - /** - * Gets the ID for the specified server certificate name. - * - * @param name The server certificate name - * @return ID - */ - public Optional getServerCertificateId(final String name) { - try { - final GetServerCertificateResult serverCertificateResult = - client.getServerCertificate(new GetServerCertificateRequest().withServerCertificateName(name)); - return Optional.of(serverCertificateResult.getServerCertificate() - .getServerCertificateMetadata().getServerCertificateId()); - } catch (final NoSuchEntityException nsee) { + } catch (NoSuchEntityException nsee) { return Optional.empty(); } } diff --git a/src/main/java/com/nike/cerberus/service/KmsClientFactory.java b/src/main/java/com/nike/cerberus/service/KmsClientFactory.java deleted file mode 100644 index 0f23ec1c..00000000 --- a/src/main/java/com/nike/cerberus/service/KmsClientFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2017 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.kms.AWSKMS; -import com.amazonaws.services.kms.AWSKMSClientBuilder; -import com.beust.jcommander.internal.Maps; - -import javax.inject.Singleton; -import java.util.Map; - -/** - * Factory that caches kmsClients for re-use. - */ -@Singleton -public class KmsClientFactory { - - /** - * Cache of kmsClients by region - */ - private Map kmsClients = Maps.newHashMap(); - - public AWSKMS getClient(String regionName) { - return getClient(Regions.fromName(regionName)); - } - - /** - * Factory that caches kmsClients for re-use. - */ - public AWSKMS getClient(Regions region) { - if (!kmsClients.containsKey(region)) { - kmsClients.put(region, AWSKMSClientBuilder.standard().withRegion(region).build()); - } - return kmsClients.get(region); - } -} diff --git a/src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java b/src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java deleted file mode 100644 index 96256c21..00000000 --- a/src/main/java/com/nike/cerberus/service/KmsPolicyGenerator.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2017 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.auth.policy.Policy; -import com.amazonaws.auth.policy.Principal; -import com.amazonaws.auth.policy.Resource; -import com.amazonaws.auth.policy.Statement; -import com.amazonaws.auth.policy.actions.KMSActions; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * Generate a Key Policy for Customer Master Key (CMK) in KMS. - */ -public class KmsPolicyGenerator { - - private static final String AWS_PROVIDER = "AWS"; - private static final String KEY_ADMINS = "Key admins can manage this CMK"; - private static final String CMS_USER = "CMS can use this CMK"; - - private static final Principal[] NONE = new Principal[0]; - - private String description; - private List adminArns; - private String cmsArn; - - /** - * Sid description - */ - public KmsPolicyGenerator withDescription(String description) { - this.description = description; - return this; - } - - public KmsPolicyGenerator withAdminArns(List adminArns) { - this.adminArns = adminArns; - return this; - } - - public KmsPolicyGenerator withCmsArn(String cmsArn) { - this.cmsArn = cmsArn; - return this; - } - - /** - * Generate policy as a JSON String - */ - public String generatePolicyJson() { - Policy policy = generatePolicy(); - - // Let AWS libraries generate the JSON - String awsJson = policy.toJson(); - - // Pretty print the AWS JSON for our users - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - return gson.toJson(gson.fromJson(awsJson, JsonObject.class)); - } - - /** - * Generate a Key Policy - */ - protected Policy generatePolicy() { - Policy policy = new Policy(); - policy.withId(description); - - Statement keyAdminsStatement = new Statement(Statement.Effect.Allow); - keyAdminsStatement.withId(KEY_ADMINS); - keyAdminsStatement.withPrincipals(arnsToPrincipals(adminArns)); - keyAdminsStatement.withActions(KMSActions.AllKMSActions); - keyAdminsStatement.withResources(new Resource("*")); - - Statement cmsStatement = new Statement(Statement.Effect.Allow); - cmsStatement.withId(CMS_USER); - cmsStatement.withPrincipals(arnToPrincipal(cmsArn)); - cmsStatement.withActions( - // To Use the Key - KMSActions.Encrypt, - KMSActions.Decrypt, - KMSActions.ReEncryptTo, - KMSActions.ReEncryptFrom, - KMSActions.GenerateDataKey, - KMSActions.GenerateDataKeyWithoutPlaintext - ); - cmsStatement.withResources(new Resource("*")); - - policy.withStatements( - keyAdminsStatement, - cmsStatement - ); - - return policy; - } - - /** - * Convert ARNs to principal objects - */ - protected Principal[] arnsToPrincipals(List arns) { - if (arns.isEmpty()) { - return NONE; - } else { - return arns.stream() - .map(this::arnToPrincipal) - .collect(Collectors.toList()) - .toArray(NONE); - } - } - - protected Principal arnToPrincipal(String arn) { - return new Principal(AWS_PROVIDER, arn, false); - } -} diff --git a/src/main/java/com/nike/cerberus/service/KmsService.java b/src/main/java/com/nike/cerberus/service/KmsService.java deleted file mode 100644 index 5f4b7398..00000000 --- a/src/main/java/com/nike/cerberus/service/KmsService.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2017 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.kms.AWSKMS; -import com.amazonaws.services.kms.model.CreateAliasRequest; -import com.amazonaws.services.kms.model.CreateKeyRequest; -import com.amazonaws.services.kms.model.CreateKeyResult; -import com.amazonaws.services.kms.model.EnableKeyRotationRequest; -import com.amazonaws.services.kms.model.KeyMetadata; -import com.amazonaws.services.kms.model.Tag; -import com.beust.jcommander.internal.Lists; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Convenience methods for calls to KMS - */ -@Singleton -public class KmsService { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private KmsClientFactory kmsClientFactory; - - @Inject - public KmsService(KmsClientFactory kmsClientFactory) { - this.kmsClientFactory = kmsClientFactory; - } - - /** - * Create keys with a shared alias in multiple regions - * - * @param regions the target regions to create CMKs in - * @param alias the alias for the keys - * @param policy the policy - * @param description the key description - * @param tags tags to apply to the keys - * @return the ARNs of the created CMKs - */ - public List createKeysAndAliases(List regions, String alias, String policy, String description, Map tags) { - List keyArns = Lists.newArrayList(); - for (Regions region : regions) { - String keyArn = createKey(region, policy, description, alias, tags); - keyArns.add(keyArn); - } - return keyArns; - } - - /** - * Create a CMK in the supplied region with the supplied policy and description - * - * @param region the target region - * @param policy the default key poicy - * @param description the Key description - * @param alias the CMK alias to generate for this key - * @return cmkArn - */ - private String createKey(Regions region, String policy, String description, String alias, Map tags) { - - Collection kmsTags = tags.entrySet().stream() - .map(entry -> new Tag().withTagKey(entry.getKey()).withTagValue(entry.getValue())) - .collect(Collectors.toList()); - - AWSKMS client = kmsClientFactory.getClient(region); - - CreateKeyRequest request = new CreateKeyRequest(); - request.setPolicy(policy); - request.setDescription(description); - request.setTags(kmsTags); - CreateKeyResult result = client.createKey(request); - KeyMetadata keyMetadata = result.getKeyMetadata(); - - String keyArn = keyMetadata.getArn(); - String keyId = keyMetadata.getKeyId(); - - logger.info(String.format("Created CMK '%s' in region '%s'", keyArn, region)); - - enableKeyRotation(region, keyId); - createAlias(region, keyId, alias); - - return keyArn; - } - - - /** - * Ensure CMK key rotation is enabled for the supplied key - */ - private void enableKeyRotation(Regions region, String keyId) { - AWSKMS client = kmsClientFactory.getClient(region); - - client.enableKeyRotation(new EnableKeyRotationRequest().withKeyId(keyId)); - logger.info(String.format("Enabled key rotation for '%s' in '%s'", keyId, region)); - - } - - /** - * Create a CMK Alias for the supplied region and keyId - */ - private void createAlias(Regions region, String keyId, String alias) { - AWSKMS client = kmsClientFactory.getClient(region); - - CreateAliasRequest request = new CreateAliasRequest(); - request.setAliasName(alias); - request.setTargetKeyId(keyId); - client.createAlias(request); - - logger.info(String.format("Created Alias '%s' for key '%s' in region '%s'", alias, keyId, region)); - } - -} diff --git a/src/main/java/com/nike/cerberus/service/MetricsService.java b/src/main/java/com/nike/cerberus/service/MetricsService.java deleted file mode 100644 index 49814fa8..00000000 --- a/src/main/java/com/nike/cerberus/service/MetricsService.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2017 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.services.sns.AmazonSNS; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.Inject; -import com.nike.cerberus.store.ConfigStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; -import java.util.Optional; - -/** - * A Service for sending messages to the Cerberus Metrics SNS Topic, if the topic has been created. - */ -public class MetricsService { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private final AmazonSNS sns; - private final Optional metricsTopicArn; - private final ObjectMapper objectMapper; - - @Inject - public MetricsService(AmazonSNS sns, ObjectMapper objectMapper, ConfigStore configStore) { - this.sns = sns; - this.objectMapper = objectMapper; - - metricsTopicArn = configStore.getMetricsTopicArn(); - } - - public void trackMetric(MetricType metricType, String key, Integer value, Map dimensions) { - - log.info("Attempting to track metric, TYPE: {}, KEY: {}, VALUE: {}, DIMENSIONS: {}", - metricType, key, value, dimensions); - - if (!metricsTopicArn.isPresent()) { - log.warn("No metrics topic arn set, CLI will not track metric"); - return; - } - - CerberusMetricMessage message = new CerberusMetricMessage() - .setMetricType(metricType.toString()) - .setMetricKey(key) - .setMetricValue(value) - .setDimensions(dimensions); - - try { - sns.publish(metricsTopicArn.get(), objectMapper.writeValueAsString(message)); - } catch (Throwable t) { - log.error("Failed to track metric", t); - } - } - - public void trackGauge(String key, Integer value, Map dimensions) { - trackMetric(MetricType.GAUGE, key, value, dimensions); - } - - public void trackCounter(String key, Integer value, Map dimensions) { - trackMetric(MetricType.COUNTER, key, value, dimensions); - } - - enum MetricType { - GAUGE("gauge"), - COUNTER("counter"); - - private final String stringValue; - - MetricType(String stringValue) { - this.stringValue = stringValue; - } - - @Override - public String toString() { - return stringValue; - } - } - - private static class CerberusMetricMessage { - private String metricKey; - private Integer metricValue; - private String metricType; - private Map dimensions; - - public String getMetricKey() { - return metricKey; - } - - public CerberusMetricMessage setMetricKey(String metricKey) { - this.metricKey = metricKey; - return this; - } - - public Integer getMetricValue() { - return metricValue; - } - - public CerberusMetricMessage setMetricValue(Integer metricValue) { - this.metricValue = metricValue; - return this; - } - - public String getMetricType() { - return metricType; - } - - public CerberusMetricMessage setMetricType(String metricType) { - this.metricType = metricType; - return this; - } - - public Map getDimensions() { - return dimensions; - } - - public CerberusMetricMessage setDimensions(Map dimensions) { - this.dimensions = dimensions; - return this; - } - } - -} diff --git a/src/main/java/com/nike/cerberus/service/Route53Service.java b/src/main/java/com/nike/cerberus/service/Route53Service.java index 65a76296..315ba04b 100644 --- a/src/main/java/com/nike/cerberus/service/Route53Service.java +++ b/src/main/java/com/nike/cerberus/service/Route53Service.java @@ -16,7 +16,9 @@ package com.nike.cerberus.service; +import com.amazonaws.regions.Regions; import com.amazonaws.services.route53.AmazonRoute53; +import com.amazonaws.services.route53.AmazonRoute53Client; import com.amazonaws.services.route53.model.Change; import com.amazonaws.services.route53.model.ChangeAction; import com.amazonaws.services.route53.model.ChangeBatch; @@ -30,8 +32,11 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javax.inject.Named; import java.util.Optional; +import static com.nike.cerberus.module.CerberusModule.CONFIG_REGION; + /** * Service wrapper for AWS CloudFormation. */ @@ -42,8 +47,11 @@ public class Route53Service { private final AmazonRoute53 route53Client; @Inject - public Route53Service(final AmazonRoute53 route53Client) { - this.route53Client = route53Client; + public Route53Service(AwsClientFactory route53ClientFactory, + @Named(CONFIG_REGION) String configRegion) { + + // not region specific config region works + this.route53Client = route53ClientFactory.getClient(Regions.fromName(configRegion)); } public void createRoute53RecordSet(String hostedZoneId, @@ -53,14 +61,14 @@ public void createRoute53RecordSet(String hostedZoneId, String resourceRecordTtl) { logger.info("Creating Route53 record name: {}, value: {}", recordSetName, recordValue); - final ResourceRecord record = new ResourceRecord().withValue(recordValue); - final ResourceRecordSet recordSet = new ResourceRecordSet() + ResourceRecord record = new ResourceRecord().withValue(recordValue); + ResourceRecordSet recordSet = new ResourceRecordSet() .withResourceRecords(record) .withName(recordSetName) .withType(recordSetType) .withTTL(Long.parseLong(resourceRecordTtl)); - final ChangeBatch recordSetChangeBatch = new ChangeBatch() + ChangeBatch recordSetChangeBatch = new ChangeBatch() .withChanges(new Change() .withAction(ChangeAction.UPSERT) .withResourceRecordSet(recordSet)); @@ -70,8 +78,8 @@ public void createRoute53RecordSet(String hostedZoneId, .withChangeBatch(recordSetChangeBatch)); } - public Optional getRecordSetByName(final String recordSetName, final String hostedZoneId) { - final ListResourceRecordSetsResult recordSets = route53Client.listResourceRecordSets( + public Optional getRecordSetByName(String recordSetName, String hostedZoneId) { + ListResourceRecordSetsResult recordSets = route53Client.listResourceRecordSets( new ListResourceRecordSetsRequest() .withHostedZoneId(hostedZoneId)); diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 1b8f9f71..c45c45f2 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Nike, Inc. + * Copyright (c) 2017 Nike, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,37 +16,38 @@ package com.nike.cerberus.store; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.regions.Region; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3EncryptionClient; -import com.amazonaws.services.s3.model.CryptoConfiguration; -import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; +import com.amazonaws.encryptionsdk.MasterKeyProvider; +import com.amazonaws.encryptionsdk.kms.KmsMasterKey; +import com.amazonaws.encryptionsdk.kms.KmsMasterKeyProvider; +import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.securitytoken.AWSSecurityTokenService; +import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomaslanger.chalk.Chalk; import com.nike.cerberus.ConfigConstants; -import com.nike.cerberus.domain.EnvironmentMetadata; -import com.nike.cerberus.domain.cloudformation.BaseOutputs; -import com.nike.cerberus.domain.cloudformation.BaseParameters; -import com.nike.cerberus.domain.cloudformation.CmsOutputs; -import com.nike.cerberus.domain.cloudformation.CmsParameters; +import com.nike.cerberus.domain.cloudformation.IamRolesOutputs; import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; -import com.nike.cerberus.domain.cloudformation.DatabaseParameters; -import com.nike.cerberus.domain.cloudformation.GatewayParameters; -import com.nike.cerberus.domain.cloudformation.LoadBalancerOutputs; +import com.nike.cerberus.domain.cloudformation.ConfigOutputs; import com.nike.cerberus.domain.cloudformation.Route53Outputs; -import com.nike.cerberus.domain.cloudformation.Route53Parameters; import com.nike.cerberus.domain.cloudformation.SecurityGroupOutputs; -import com.nike.cerberus.domain.cloudformation.SecurityGroupParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.cloudformation.VpcParameters; -import com.nike.cerberus.domain.environment.CerberusEnvironmentData; +import com.nike.cerberus.domain.environment.EnvironmentData; import com.nike.cerberus.domain.environment.CertificateInformation; +import com.nike.cerberus.domain.environment.RegionData; import com.nike.cerberus.domain.environment.Stack; -import com.nike.cerberus.service.*; +import com.nike.cerberus.service.AwsClientFactory; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.EncryptionService; +import com.nike.cerberus.service.S3StoreService; +import com.nike.cerberus.service.SaltGenerator; +import com.nike.cerberus.service.StoreService; import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -54,39 +55,32 @@ import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Singleton; import java.io.IOException; import java.io.StringReader; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.LinkedList; +import java.util.stream.Collectors; -import static com.nike.cerberus.ConfigConstants.ADMIN_ROLE_ARN_KEY; -import static com.nike.cerberus.ConfigConstants.CERT_PART_CA; -import static com.nike.cerberus.ConfigConstants.CERT_PART_CERT; -import static com.nike.cerberus.ConfigConstants.CERT_PART_KEY; -import static com.nike.cerberus.ConfigConstants.CERT_PART_PKCS8_KEY; -import static com.nike.cerberus.ConfigConstants.CERT_PART_PUBKEY; -import static com.nike.cerberus.ConfigConstants.CMS_ENV_NAME; -import static com.nike.cerberus.ConfigConstants.CMS_ROLE_ARN_KEY; -import static com.nike.cerberus.ConfigConstants.HASH_SALT; -import static com.nike.cerberus.ConfigConstants.JDBC_PASSWORD_KEY; -import static com.nike.cerberus.ConfigConstants.JDBC_URL_KEY; -import static com.nike.cerberus.ConfigConstants.JDBC_USERNAME_KEY; -import static com.nike.cerberus.ConfigConstants.ROOT_USER_ARN_KEY; -import static com.nike.cerberus.ConfigConstants.SYSTEM_CONFIGURED_CMS_PROPERTIES; -import static com.nike.cerberus.ConfigConstants.CMS_CERTIFICATE_TO_USE; +import static com.nike.cerberus.ConfigConstants.*; import static com.nike.cerberus.module.CerberusModule.CONFIG_OBJECT_MAPPER; +import static com.nike.cerberus.module.CerberusModule.CONFIG_REGION; +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** - * Abstraction for accessing the configuration storage bucket. + * Abstraction for accessing the configuration storage bucket and the environment state stored in it. */ +@Singleton public class ConfigStore { private final Logger logger = LoggerFactory.getLogger(getClass()); - private static final String CERBERUS_METRICS_TOPIC_ARN_STACK_OUTPUT_KEY = "CerberusMetricsTopicArn"; + private final AwsClientFactory amazonS3ClientFactory; private final CloudFormationService cloudFormationService; @@ -94,36 +88,38 @@ public class ConfigStore { private final CloudFormationObjectMapper cloudFormationObjectMapper; - private final AmazonS3 s3Client; - - private final EnvironmentMetadata environmentMetadata; - private final SaltGenerator saltGenerator; - private final Object cerberusEnvironmentDataLock = new Object(); + private final AwsClientFactory securityTokenServiceFactory; - private StoreService encryptedConfigStoreService; + private final String environmentName; - private StoreService configStoreService; + private final Regions configRegion; - private AWSSecurityTokenService securityTokenService; + private final EncryptionService encryptionService; + + private Map storeServiceMap = new HashMap<>(); @Inject - public ConfigStore(final AmazonS3 s3Client, - final CloudFormationService cloudFormationService, - final AWSSecurityTokenService securityTokenService, - final EnvironmentMetadata environmentMetadata, - final SaltGenerator saltGenerator, - @Named(CONFIG_OBJECT_MAPPER) final ObjectMapper configObjectMapper, - final CloudFormationObjectMapper cloudFormationObjectMapper) { + public ConfigStore(AwsClientFactory amazonS3ClientFactory, + CloudFormationService cloudFormationService, + AwsClientFactory securityTokenServiceFactory, + SaltGenerator saltGenerator, + @Named(CONFIG_OBJECT_MAPPER) ObjectMapper configObjectMapper, + CloudFormationObjectMapper cloudFormationObjectMapper, + @Named(ENV_NAME) String environmentName, + @Named(CONFIG_REGION) String configRegion, + EncryptionService encryptionService) { this.cloudFormationService = cloudFormationService; this.configObjectMapper = configObjectMapper; this.cloudFormationObjectMapper = cloudFormationObjectMapper; - this.s3Client = s3Client; - this.environmentMetadata = environmentMetadata; + this.amazonS3ClientFactory = amazonS3ClientFactory; this.saltGenerator = saltGenerator; - this.securityTokenService = securityTokenService; + this.securityTokenServiceFactory = securityTokenServiceFactory; + this.environmentName = environmentName; + this.configRegion = Regions.fromName(configRegion); + this.encryptionService = encryptionService; } /** @@ -132,10 +128,8 @@ public ConfigStore(final AmazonS3 s3Client, * @return CMS database password */ public Optional getCmsDatabasePassword() { - synchronized (cerberusEnvironmentDataLock) { - final CerberusEnvironmentData environmentData = getDecryptedEnvironmentData(); - return Optional.ofNullable(environmentData.getDatabasePassword()); - } + EnvironmentData environmentData = getDecryptedEnvironmentData(); + return Optional.ofNullable(environmentData.getDatabasePassword()); } /** @@ -143,12 +137,10 @@ public Optional getCmsDatabasePassword() { * * @param databasePassword Database password */ - public void storeCmsDatabasePassword(final String databasePassword) { - synchronized (cerberusEnvironmentDataLock) { - final CerberusEnvironmentData environmentData = getDecryptedEnvironmentData(); - environmentData.setDatabasePassword(databasePassword); - saveEnvironmentData(environmentData); - } + public void storeCmsDatabasePassword(String databasePassword) { + EnvironmentData environmentData = getDecryptedEnvironmentData(); + environmentData.setDatabasePassword(databasePassword); + saveEnvironmentData(environmentData); } /** @@ -167,39 +159,53 @@ public void storeCert(CertificateInformation certificateInformation, String pkcs8KeyContents, String pubKeyContents) { + EnvironmentData environmentData = getDecryptedEnvironmentData(); + String name = certificateInformation.getCertificateName(); - saveEncryptedObject(buildCertFilePath(name, CERT_PART_CA), caContents); - saveEncryptedObject(buildCertFilePath(name, CERT_PART_CERT), certContents); - saveEncryptedObject(buildCertFilePath(name, CERT_PART_KEY), keyContents); - saveEncryptedObject(buildCertFilePath(name, CERT_PART_PKCS8_KEY), pkcs8KeyContents); - saveEncryptedObject(buildCertFilePath(name, CERT_PART_PUBKEY), pubKeyContents); + encryptAndSaveObject(buildCertFilePath(name, CERT_PART_CA), caContents, environmentData); + encryptAndSaveObject(buildCertFilePath(name, CERT_PART_CERT), certContents, environmentData); + encryptAndSaveObject(buildCertFilePath(name, CERT_PART_KEY), keyContents, environmentData); + encryptAndSaveObject(buildCertFilePath(name, CERT_PART_PKCS8_KEY), pkcs8KeyContents, environmentData); + encryptAndSaveObject(buildCertFilePath(name, CERT_PART_PUBKEY), pubKeyContents, environmentData); - synchronized (cerberusEnvironmentDataLock) { - final CerberusEnvironmentData environment = getDecryptedEnvironmentData(); - environment.addNewCertificateData(certificateInformation); + environmentData.addNewCertificateData(certificateInformation); - saveEnvironmentData(environment); - } + saveEnvironmentData(environmentData); } public LinkedList getCertificationInformationList() { return getDecryptedEnvironmentData().getCertificateData(); } + public Optional getLastCert() { + EnvironmentData environmentData = getDecryptedEnvironmentData(); + if (environmentData.getCertificateData().isEmpty()) { + return Optional.empty(); + } + return Optional.of(environmentData.getCertificateData().getLast()); + } + /** * Deletes a set of cert and key files by certificate name * @param certificateName the name of the cert file bundle to delete */ public void deleteCertificate(String certificateName) { - initEncryptedConfigStoreService(); + String path = "certificates/" + certificateName; + getDecryptedEnvironmentData().getConfigRegions().forEach(region -> { + try { + getStoreServiceForRegion(region, getDecryptedEnvironmentData()).deleteAllKeysOnPartialPath(path); + } catch (Exception e) { + logger.error(Chalk.on( + String.format("Failed to delete object at path: %s for region: %s, cross region data may be " + + "out of sync and require manual fixing", path, region) + ).bold().red().toString()); + } + }); - encryptedConfigStoreService.deleteAllKeysOnPartialPath("certificates/" + certificateName); - synchronized (cerberusEnvironmentDataLock) { - final CerberusEnvironmentData environment = getDecryptedEnvironmentData(); - environment.removeCertificateInformationByName(certificateName); + EnvironmentData environment = getDecryptedEnvironmentData(); + environment.removeCertificateInformationByName(certificateName); - saveEnvironmentData(environment); - } + saveEnvironmentData(environment); } /** @@ -209,21 +215,21 @@ public void deleteCertificate(String certificateName) { * @return */ public Optional getCertPart(String certName, String part) { - return getEncryptedObject(buildCertFilePath(certName, part)); + return getEncryptedObject(buildCertFilePath(certName, part)).map(encryptionService::decrypt); } - public void storeCmsEnvConfig(final Properties cmsConfigMap) { - final StringBuilder cmsConfigContents = new StringBuilder(); + public void storeCmsEnvConfig(Properties cmsConfigMap) { + StringBuilder cmsConfigContents = new StringBuilder(); - cmsConfigMap.keySet().stream().forEach(key -> { + cmsConfigMap.keySet().forEach(key -> { cmsConfigContents.append(key).append('=').append(cmsConfigMap.get(key)).append('\n'); }); - saveEncryptedObject(ConfigConstants.CMS_ENV_CONFIG_PATH, cmsConfigContents.toString()); + encryptAndSaveObject(ConfigConstants.CMS_ENV_CONFIG_PATH, cmsConfigContents.toString(), getDecryptedEnvironmentData()); } public Optional getCmsEnvConfig() { - return getEncryptedObject(ConfigConstants.CMS_ENV_CONFIG_PATH); + return getEncryptedObject(ConfigConstants.CMS_ENV_CONFIG_PATH).map(encryptionService::decrypt); } /** @@ -248,31 +254,28 @@ public Properties getAllExistingCmsEnvProperties() { } /** - * Generate CMS properties that are not set by the user + * Generate the global CMS properties that are not set by the user * * @return - System configured properties */ private Properties generateBaseCmsSystemProperties() { + EnvironmentData data = getDecryptedEnvironmentData(); + String cmsDatabasePassword = data.getDatabasePassword(); + String cmsIamRoleArn = data.getCmsIamRoleArn(); + String adminRoleArn = data.getAdminIamRoleArn(); + String rootUserArn = data.getRootIamRoleArn(); + String jdbcUrl = getDatabaseStackOutputs(getPrimaryRegion()).getCmsDbJdbcConnectionString(); - final BaseOutputs baseOutputs = getBaseStackOutputs(); - final DatabaseOutputs databaseOutputs = getDatabaseStackOutputs(); - final BaseParameters baseParameters = getBaseStackParameters(); - final String cmsDatabasePassword = getDecryptedEnvironmentData().getDatabasePassword(); - - final GetCallerIdentityResult callerIdentity = securityTokenService.getCallerIdentity( - new GetCallerIdentityRequest()); - final String rootUserArn = String.format("arn:aws:iam::%s:root", callerIdentity.getAccount()); - - final Properties properties = new Properties(); + Properties properties = new Properties(); properties.put(ROOT_USER_ARN_KEY, rootUserArn); - properties.put(ADMIN_ROLE_ARN_KEY, baseParameters.getAccountAdminArn()); - properties.put(CMS_ROLE_ARN_KEY, baseOutputs.getCmsIamRoleArn()); - properties.put(JDBC_URL_KEY, databaseOutputs.getCmsDbJdbcConnectionString()); + properties.put(ADMIN_ROLE_ARN_KEY, adminRoleArn); + properties.put(CMS_ROLE_ARN_KEY, cmsIamRoleArn); + properties.put(JDBC_URL_KEY, jdbcUrl); properties.put(JDBC_USERNAME_KEY, ConfigConstants.DEFAULT_CMS_DB_NAME); properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword); - properties.put(CMS_ENV_NAME, environmentMetadata.getName()); - // Ust the latest uploaded certificate + properties.put(CMS_ENV_NAME, environmentName); properties.put(CMS_CERTIFICATE_TO_USE, getCertificationInformationList().getLast().getCertificateName()); + properties.put(CMK_ARNS_KEY, StringUtils.join(data.getCmsCmkArns(), ",")); return properties; } @@ -326,51 +329,50 @@ public Properties getExistingCmsUserProperties() { * @param path - Path to configuration file (e.g. 'config/environment.properties') */ public Optional getConfigProperties(String path) { - - return getEncryptedObject(path); + return getEncryptedObject(path).map(encryptionService::decrypt); } /** * returns the complete stack name */ public String getCloudFormationStackName(Stack stack) { - return stack.getFullName(environmentMetadata.getName()); + return stack.getFullName(environmentName); } /** - * Get the base stack parameters. + * Get the cms iam role stack outputs for primary region * - * @return Base parameters + * @return Base outputs */ - public BaseParameters getBaseStackParameters() { - return getStackParameters(getCloudFormationStackName(Stack.BASE), BaseParameters.class); + public IamRolesOutputs getCmsIamRoleOutputs() { + return getStackOutputs(getPrimaryRegion(), getCloudFormationStackName(Stack.IAM_ROLES), IamRolesOutputs.class); } /** - * Get the base stack outputs. + * Get the cms iam role stack outputs for provided region * * @return Base outputs */ - public BaseOutputs getBaseStackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.BASE), BaseOutputs.class); + public IamRolesOutputs getCmsIamRoleOutputs(Regions region) { + return getStackOutputs(region, getCloudFormationStackName(Stack.IAM_ROLES), IamRolesOutputs.class); } /** - * Get the base stack parameters. + * Get the cms iam role stack outputs. * - * @return Base parameters + * @return Base outputs */ - public VpcParameters getVpcStackParameters() { - return getStackParameters(getCloudFormationStackName(Stack.VPC), VpcParameters.class); + public ConfigOutputs getConfigBucketStackOutputs(Regions region) { + return getStackOutputs(region, getCloudFormationStackName(Stack.CONFIG), ConfigOutputs.class); } /** - * Get the base stack outputs. + * Get the base stack parameters for primary region. * - * @return Base outputs + * @return Base parameters */ - public VpcOutputs getVpcStackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.VPC), VpcOutputs.class); + public VpcParameters getVpcStackParameters() { + return getStackParameters(getPrimaryRegion(), getCloudFormationStackName(Stack.VPC), VpcParameters.class); } /** @@ -378,17 +380,17 @@ public VpcOutputs getVpcStackOutputs() { * * @return Base parameters */ - public SecurityGroupParameters getSecurityGroupStackParameters() { - return getStackParameters(getCloudFormationStackName(Stack.SECURITY_GROUPS), SecurityGroupParameters.class); + public VpcParameters getVpcStackParameters(Regions region) { + return getStackParameters(region, getCloudFormationStackName(Stack.VPC), VpcParameters.class); } /** - * Get the base stack outputs. + * Get the base stack outputs for primary region. * * @return Base outputs */ - public SecurityGroupOutputs getSecurityGroupStackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.SECURITY_GROUPS), SecurityGroupOutputs.class); + public VpcOutputs getVpcStackOutputs() { + return getStackOutputs(getPrimaryRegion(), getCloudFormationStackName(Stack.VPC), VpcOutputs.class); } /** @@ -396,26 +398,26 @@ public SecurityGroupOutputs getSecurityGroupStackOutputs() { * * @return Base outputs */ - public LoadBalancerOutputs getLoadBalancerStackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.LOAD_BALANCER), LoadBalancerOutputs.class); + public VpcOutputs getVpcStackOutputs(Regions region) { + return getStackOutputs(region, getCloudFormationStackName(Stack.VPC), VpcOutputs.class); } /** - * Get the base stack parameters. + * Get the base stack outputs. * - * @return Base parameters + * @return Base outputs */ - public DatabaseParameters getDatabaseStackParameters() { - return getStackParameters(getCloudFormationStackName(Stack.DATABASE), DatabaseParameters.class); + public SecurityGroupOutputs getSecurityGroupStackOutputs() { + return getStackOutputs(getPrimaryRegion(), getCloudFormationStackName(Stack.SECURITY_GROUPS), SecurityGroupOutputs.class); } /** - * Get the base stack parameters. + * Get the base stack outputs. * - * @return Base parameters + * @return Base outputs */ - public Route53Parameters getRoute53Parameters() { - return getStackParameters(getCloudFormationStackName(Stack.ROUTE53), Route53Parameters.class); + public SecurityGroupOutputs getSecurityGroupStackOutputs(Regions region) { + return getStackOutputs(region, getCloudFormationStackName(Stack.SECURITY_GROUPS), SecurityGroupOutputs.class); } /** @@ -424,43 +426,25 @@ public Route53Parameters getRoute53Parameters() { * @return Base parameters */ public Route53Outputs getRoute53StackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.ROUTE53), Route53Outputs.class); + return getStackOutputs(getPrimaryRegion(), getCloudFormationStackName(Stack.ROUTE53), Route53Outputs.class); } /** - * Get the base stack outputs. - * - * @return Base outputs - */ - public DatabaseOutputs getDatabaseStackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.DATABASE), DatabaseOutputs.class); - } - - /** - * Get the CMS stack parameters. - * - * @return CMS parameters - */ - public CmsParameters getCmsStackParamters() { - return getStackParameters(getCloudFormationStackName(Stack.CMS), CmsParameters.class); - } - - /** - * Get the CMS stack outputs. + * Get the base stack parameters. * - * @return CMS outputs + * @return Base parameters */ - public CmsOutputs getCmsStackOutputs() { - return getStackOutputs(getCloudFormationStackName(Stack.CMS), CmsOutputs.class); + public Route53Outputs getRoute53StackOutputs(Regions region) { + return getStackOutputs(region, getCloudFormationStackName(Stack.ROUTE53), Route53Outputs.class); } /** - * Get the Gateway stack parameters. + * Get the base stack outputs. * - * @return Gateway parameters + * @return Base outputs */ - public GatewayParameters getGatewayStackParameters() { - return getStackParameters(getCloudFormationStackName(Stack.GATEWAY), GatewayParameters.class); + public DatabaseOutputs getDatabaseStackOutputs(Regions region) { + return getStackOutputs(region, getCloudFormationStackName(Stack.DATABASE), DatabaseOutputs.class); } /** @@ -471,12 +455,12 @@ public GatewayParameters getGatewayStackParameters() { * @param Outputs type * @return Outputs */ - public M getStackOutputs(final String stackName, final Class outputClass) { - if (!cloudFormationService.isStackPresent(stackName)) { + public M getStackOutputs(Regions region, String stackName, Class outputClass) { + if (!cloudFormationService.isStackPresent(region, stackName)) { throw new IllegalStateException("Failed to get CloudFormation output for stack: '" + stackName + "'. Stack does not exist."); } - final Map stackOutputs = cloudFormationService.getStackOutputs(stackName); + Map stackOutputs = cloudFormationService.getStackOutputs(region, stackName); return cloudFormationObjectMapper.convertValue(stackOutputs, outputClass); } @@ -488,40 +472,26 @@ public M getStackOutputs(final String stackName, final Class outputClass) * @param Parameters type * @return Parameters */ - public M getStackParameters(final String stackName, final Class parameterClass) { - if (!cloudFormationService.isStackPresent(stackName)) { + public M getStackParameters(Regions region, String stackName, Class parameterClass) { + if (!cloudFormationService.isStackPresent(region, stackName)) { throw new IllegalStateException("Failed to get CloudFormation parameters for stack: '" + stackName + "'. Stack does not exist."); } - final Map stackOutputs = cloudFormationService.getStackParameters(stackName); + Map stackOutputs = cloudFormationService.getStackParameters(region, stackName); return cloudFormationObjectMapper.convertValue(stackOutputs, parameterClass); } - /** - * Initializes the environmentData data in the config bucket. - */ - public void initEnvironmentData() { - synchronized (cerberusEnvironmentDataLock) { - try { - getDecryptedEnvironmentData(); - final String errorMessage = "Attempting to initialize environmentData data, but it already exists!"; - logger.error(errorMessage); - throw new RuntimeException(errorMessage); - } catch (IllegalStateException ise) { - final CerberusEnvironmentData environmentData = new CerberusEnvironmentData(); - saveEnvironmentData(environmentData); - } + private EnvironmentData getDecryptedEnvironmentData() { + if (! storeServiceMap.containsKey(configRegion)) { + String bucket = findConfigBucketInSuppliedConfigRegion(); + storeServiceMap.put(configRegion, new S3StoreService(amazonS3ClientFactory.getClient(configRegion), bucket, "")); } - } - - private CerberusEnvironmentData getDecryptedEnvironmentData() { - initEncryptedConfigStoreService(); - final Optional environmentData = encryptedConfigStoreService.get(ConfigConstants.ENVIRONMENT_DATA_FILE); + Optional environmentData = storeServiceMap.get(configRegion).get(ConfigConstants.ENVIRONMENT_DATA_FILE); if (environmentData.isPresent()) { try { - return configObjectMapper.readValue(environmentData.get(), CerberusEnvironmentData.class); + return configObjectMapper.readValue(encryptionService.decrypt(environmentData.get()), EnvironmentData.class); } catch (IOException e) { throw new IllegalStateException("Unable to read the environment data!", e); } @@ -530,82 +500,169 @@ private CerberusEnvironmentData getDecryptedEnvironmentData() { } } - private void saveEnvironmentData(final CerberusEnvironmentData environmentData) { + private void saveEnvironmentData(EnvironmentData environmentData) { try { - final String environmentDataData = configObjectMapper.writeValueAsString(environmentData); - saveEncryptedObject(ConfigConstants.ENVIRONMENT_DATA_FILE, environmentDataData); + String serializedPlainTextEnvironmentData = configObjectMapper.writeValueAsString(environmentData); + encryptAndSaveObject(ConfigConstants.ENVIRONMENT_DATA_FILE, serializedPlainTextEnvironmentData, environmentData); } catch (JsonProcessingException e) { throw new RuntimeException("Unable to convert the environment data to JSON. Aborting save...", e); } } /** - * List under a path as-if it were a folder + * List keys in config bucket under a path as-if it were a folder */ - public Set listUnderPartialPath(final String path) { - initConfigStoreService(); - - return configStoreService.listUnderPartialPath(path); + public Set listUnderPartialPath(String path) { + return getStoreServiceForRegion(configRegion, getDecryptedEnvironmentData()).listUnderPartialPath(path); } - private void saveEncryptedObject(final String path, final String value) { - initEncryptedConfigStoreService(); + private void encryptAndSaveObject(String path, + String plaintextSerializedObject, + EnvironmentData environmentData) { - encryptedConfigStoreService.put(path, value); - } + List environmentDataKmsCmkArns = new LinkedList<>(); + environmentData.getRegionData().forEach((region, regionData) -> + regionData.getEnvironmentDataKmsCmkArn().ifPresent(environmentDataKmsCmkArns::add)); + MasterKeyProvider encryptProvider = initializeKeyProvider(environmentDataKmsCmkArns); - private Optional getEncryptedObject(final String path) { - initEncryptedConfigStoreService(); + String encryptedObject = encryptionService.encrypt(encryptProvider, plaintextSerializedObject); - return encryptedConfigStoreService.get(path); + environmentData.getConfigRegions().forEach(region -> { + try { + getStoreServiceForRegion(region, environmentData).put(path, encryptedObject); + } catch (Exception e) { + logger.error(Chalk.on( + String.format("Failed to save object at path: %s for region: %s, cross region data may be " + + "out of sync and require manual fixing", path, region) + ).bold().red().toString()); + } + }); } - private void initEncryptedConfigStoreService() { - if (encryptedConfigStoreService == null) { - KMSEncryptionMaterialsProvider materialProvider = - new KMSEncryptionMaterialsProvider(getBaseStackOutputs().getConfigFileKeyId()); - - AmazonS3EncryptionClient encryptionClient = - new AmazonS3EncryptionClient( - new DefaultAWSCredentialsProviderChain(), - materialProvider, - new CryptoConfiguration() - .withAwsKmsRegion(Region.getRegion(environmentMetadata.getRegions()))) - .withRegion(Region.getRegion(environmentMetadata.getRegions())); - - encryptedConfigStoreService = new S3StoreService(encryptionClient, environmentMetadata.getBucketName(), ""); + private StoreService getStoreServiceForRegion(Regions region, EnvironmentData environmentData) { + if (!storeServiceMap.containsKey(region)) { + String bucket = environmentData.getRegionData().get(region).getConfigBucket() + .orElseThrow(() -> new RuntimeException("bucket not configured for region: " + region)); + storeServiceMap.put(region, new S3StoreService(amazonS3ClientFactory.getClient(region), bucket, "")); } + return storeServiceMap.get(region); } - private void initConfigStoreService() { - if (configStoreService == null) { - configStoreService = new S3StoreService(s3Client, environmentMetadata.getBucketName(), ""); - } + private Optional getEncryptedObject(String path) { + return getStoreServiceForRegion(configRegion, getDecryptedEnvironmentData()).get(path); } private String buildCertFilePath(String identityManagementCertName, String filename) { return String.format("certificates/%s/%s", identityManagementCertName, filename); } - public Optional getMetricsTopicArn() { - synchronized (cerberusEnvironmentDataLock) { - CerberusEnvironmentData environment = getDecryptedEnvironmentData(); - if (StringUtils.isNoneBlank(environment.getMetricsTopicArn())) { - return Optional.of(environment.getMetricsTopicArn()); + private String findConfigBucketInSuppliedConfigRegion() { + AmazonS3Client s3Client = amazonS3ClientFactory.getClient(configRegion); + List buckets = s3Client.listBuckets(); + String envBucket = null; + for (Bucket bucket : buckets) { + String bucketName = bucket.getName(); + if (StringUtils.contains(bucket.getName(), ConfigConstants.CONFIG_BUCKET_KEY)) { + String bucketLocationResult = s3Client.getBucketLocation(bucketName); + Regions bucketRegion = StringUtils.equals("US", bucketLocationResult) ? Regions.US_EAST_1 : Regions.fromName(bucketLocationResult); + if (configRegion.equals(bucketRegion)) { + String tokenizedEnvName = StringUtils.replaceAll(environmentName, "_", "-"); + if (StringUtils.startsWith(bucketName, tokenizedEnvName)) { + envBucket = bucketName; + break; + } + } } } - Optional metricsTopicArn = cloudFormationService.searchStacksForOutput(CERBERUS_METRICS_TOPIC_ARN_STACK_OUTPUT_KEY); - metricsTopicArn.ifPresent(this::storeMetricsTopicArn); + if (StringUtils.isBlank(envBucket)) { + throw new RuntimeException("Failed to find config bucket for region: " + configRegion); + } - return metricsTopicArn; + return envBucket; } - private void storeMetricsTopicArn(String arn) { - synchronized (cerberusEnvironmentDataLock) { - CerberusEnvironmentData environment = getDecryptedEnvironmentData(); - environment.setMetricsTopicArn(arn); - saveEnvironmentData(environment); + @SuppressWarnings("unchecked") + private MasterKeyProvider initializeKeyProvider(List cmkArns) { + List> providers = cmkArns.stream() + .map(KmsMasterKeyProvider::new) + .collect(Collectors.toList()); + return (MasterKeyProvider) MultipleProviderFactory.buildMultiProvider(providers); + } + + public Regions getPrimaryRegion() { + return getDecryptedEnvironmentData().getPrimaryRegion(); + } + + public List getConfigEnabledRegions() { + return getDecryptedEnvironmentData().getConfigRegions(); + } + + /** + * Initializes the config state for a Cerberus Environment, called once at environment creation + * + * @param adminRoleArn The admin role arn for the Cerberus environment, needed for KMS policies + * @param cmsIamRoleArn The CMS role arn for the Cerberus environment, needed for KMS policies + * @param primaryRegion The primary region that will serve traffic and have the data store and cms asg + * @param regionConfigOutputsMap The outputs that have the config buckets and KMS CMKs for encrypting sensitive config + */ + public void initializeEnvironment(String adminRoleArn, + String cmsIamRoleArn, + 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()); + + EnvironmentData environmentData = new EnvironmentData(); + environmentData.setEnvironmentName(environmentName); + environmentData.setAdminIamRoleArn(adminRoleArn); + environmentData.setCmsIamRoleArn(cmsIamRoleArn); + environmentData.setRootIamRoleArn(rootUserArn); + regionConfigOutputsMap.forEach((region, output) -> { + RegionData regionData = new RegionData(); + if (region.equals(primaryRegion)) { + regionData.setPrimary(true); + } + regionData.setConfigBucket(output.getConfigBucketName()); + regionData.setEnvironmentDataKmsCmkArn(output.getEnvironmentDataKmsCmkArn()); + regionData.setCmsSecureDataKmsCmkArn(output.getCmsSecureDataKmsCmkArn()); + environmentData.addRegionData(region, regionData); + }); + + saveEnvironmentData(environmentData); + } + + public String getConfigBucketForRegion(Regions region) { + if (!getDecryptedEnvironmentData().getRegionData().containsKey(region)) { + throw new RuntimeException("There is no region data for region: " + region.getName()); + } + RegionData data = getDecryptedEnvironmentData().getRegionData().get(region); + return data.getConfigBucket().orElseThrow(() -> + new RuntimeException("There is no config bucket configured for region: " + region.getName())); + } + + public String getCmsCmkForRegion(Regions region) { + if (!getDecryptedEnvironmentData().getRegionData().containsKey(region)) { + throw new RuntimeException("There is no region data for region: " + region.getName()); + } + RegionData data = getDecryptedEnvironmentData().getRegionData().get(region); + return data.getCmsSecureDataKmsCmkArn().orElseThrow(() -> + new RuntimeException("There is no cms cmk configured for region: " + region.getName())); + } + + public String getEnvironmentDataSecureDataKmsCmkRegion(Regions region) { + if (!getDecryptedEnvironmentData().getRegionData().containsKey(region)) { + throw new RuntimeException("There is no region data for region: " + region.getName()); } + RegionData data = getDecryptedEnvironmentData().getRegionData().get(region); + return data.getEnvironmentDataKmsCmkArn().orElseThrow(() -> + new RuntimeException("There is no cms cmk configured for region: " + region.getName())); + } + + public EnvironmentData getEnvironmentData() { + return getDecryptedEnvironmentData(); } } diff --git a/src/main/java/com/nike/cerberus/util/CiphertextUtils.java b/src/main/java/com/nike/cerberus/util/CiphertextUtils.java new file mode 100644 index 00000000..e452d4af --- /dev/null +++ b/src/main/java/com/nike/cerberus/util/CiphertextUtils.java @@ -0,0 +1,88 @@ +package com.nike.cerberus.util; + +import com.amazonaws.encryptionsdk.ParsedCiphertext; +import com.amazonaws.encryptionsdk.model.KeyBlob; +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; +import java.util.List; + +/** + * Utility methods for working with the 'AWS Encryption SDK Message Format'. + * + * http://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/message-format.html + */ +public class CiphertextUtils { + + private static final String KMS_PROVIDER_ID = "aws-kms"; + + /** + * Parse a Ciphertext in the 'AWS Encryption SDK Message Format' to an object. + */ + public static ParsedCiphertext parse(String ciphertext) { + byte[] bytes = Base64.getDecoder().decode(ciphertext); + return new ParsedCiphertext(bytes); + } + + /** + * Parse CMK ARNs out of the supplied ciphertext in the 'AWS Encryption SDK Message Format'. + */ + public static List getCustomerMasterKeyArns(ParsedCiphertext parsedCiphertext) { + List cmkArns = Lists.newArrayList(); + for (KeyBlob keyBlob : parsedCiphertext.getEncryptedKeyBlobs()) { + if (KMS_PROVIDER_ID.equals(keyBlob.getProviderId())) { + try { + cmkArns.add(new String(keyBlob.getProviderInformation(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Failed to create new string from key blob provider information", e); + } + } + + } + return cmkArns; + } + + /** + * Convert a ParsedCiphertext in the 'AWS Encryption SDK Message Format' to pretty usage JSON. + */ + public static String toJson(ParsedCiphertext parsedCiphertext) { + Gson gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + + return gson.toJson(toJsonObject(parsedCiphertext)); + } + + /** + * Convert a ParsedCiphertext in the 'AWS Encryption SDK Message Format' to a JsonObject. + */ + public static JsonObject toJsonObject(ParsedCiphertext parsedCiphertext) { + JsonObject o = new JsonObject(); + o.addProperty("version", parsedCiphertext.getVersion()); + o.addProperty("type", parsedCiphertext.getType().toString()); + o.addProperty("cryptoAlgoId", parsedCiphertext.getCryptoAlgoId().toString()); + o.addProperty("messageId", Base64.getEncoder().encodeToString(parsedCiphertext.getMessageId())); + o.add("encryptionContext", new Gson().toJsonTree(parsedCiphertext.getEncryptionContextMap())); + o.addProperty("keyBlobCount", parsedCiphertext.getEncryptedKeyBlobCount()); + JsonArray keyBlobs = new JsonArray(); + for (KeyBlob keyBlob : parsedCiphertext.getEncryptedKeyBlobs()) { + JsonObject blob = new JsonObject(); + blob.addProperty("providerId", keyBlob.getProviderId()); + try { + blob.addProperty("providerInfo", new String(keyBlob.getProviderInformation(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Failed to create new string from key blob provider information", e); + } + blob.addProperty("isComplete:", keyBlob.isComplete()); + keyBlobs.add(blob); + } + o.add("keyBlobs", keyBlobs); + o.addProperty("contentType", parsedCiphertext.getContentType().toString()); + return o; + } +} diff --git a/src/main/resources/cloudformation/base.yaml b/src/main/resources/cloudformation/config.yaml similarity index 52% rename from src/main/resources/cloudformation/base.yaml rename to src/main/resources/cloudformation/config.yaml index 21a97ea6..80fcda45 100644 --- a/src/main/resources/cloudformation/base.yaml +++ b/src/main/resources/cloudformation/config.yaml @@ -1,25 +1,24 @@ AWSTemplateFormatVersion: '2010-09-09' -Description: Creates the necessary IAM roles, S3 buckets, and KMS keys for Cerberus +Description: Creates the necessary S3 buckets, and KMS CMK for Cerberus Outputs: configBucketDomainName: Value: !GetAtt 'CerberusConfigBucket.DomainName' configBucketName: Value: !Ref 'CerberusConfigBucket' - configFileKeyId: - Value: !Ref 'ConfigFileKey' - cmsIamRoleArn: - Value: !GetAtt 'CmsIamRole.Arn' - cmsIamRoleName: - Value: !Ref 'CmsIamRole' - Export: - Name: !Sub "${AWS::StackName}-cmsIamRoleName" - cmsKmsPolicyId: - Description: The policy id for CMS managing KMS keys - Value: !Ref 'CmsKmsPolicy' + environmentDataKmsCmkArn: + Value: !GetAtt 'EnvironmentDataKmsCmk.Arn' + cmsSecureDataKmsCmkArn: + Value: !GetAtt 'CmsSecureDataKmsCmk.Arn' Parameters: + cmsIamRoleArn: + Description: The ARN for for the management service IAM Role. + Type: String accountAdminArn: Description: The ARN for a IAM user, group or role that can create this stack. Type: String + environmentName: + Description: The Cerberus environment name. + Type: String Resources: CerberusConfigBucket: Properties: @@ -28,6 +27,7 @@ Resources: Status: Enabled Type: AWS::S3::Bucket CerberusConfigBucketAccessPolicy: + Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref 'CerberusConfigBucket' PolicyDocument: @@ -37,7 +37,7 @@ Resources: Effect: Allow Principal: AWS: - - !GetAtt 'CmsIamRole.Arn' + !Ref 'cmsIamRoleArn' Resource: - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket']] - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /*]] @@ -47,7 +47,7 @@ Resources: Effect: Allow Principal: AWS: - - !GetAtt 'CmsIamRole.Arn' + !Ref 'cmsIamRoleArn' Resource: - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /certificates/*]] - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /certificates]] @@ -55,89 +55,90 @@ Resources: - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /cms]] Sid: Allow-Bucket-Access-For-CMS Version: '2012-10-17' - Type: AWS::S3::BucketPolicy - CmsIamRole: + EnvironmentDataKmsCmk: + Type: AWS::KMS::Key Properties: - AssumeRolePolicyDocument: + Description: - !Join ['', ['Environment: ', !Ref 'environmentName', ' Cerberus encryption key for storing config files in s3 buckets.']] + Enabled: 'true' + KeyPolicy: Statement: - - Action: - - sts:AssumeRole + - + Sid: Allow-Root-User + Action: + - kms:* Effect: Allow Principal: - Service: - - ec2.amazonaws.com - Version: '2012-10-17' - Path: / - Policies: - - PolicyDocument: - Statement: - - Action: - - EC2:Describe* - - cloudformation:SignalResource - Effect: Allow - Resource: '*' - PolicyName: cmsPolicy - Type: AWS::IAM::Role - CmsKmsPolicy: - Properties: - PolicyDocument: - Statement: - - Action: - - kms:CreateAlias - - kms:CreateKey - - kms:DeleteAlias - - kms:DescribeKey - - kms:DisableKey - - kms:DisableKeyRotation - - kms:EnableKey - - kms:EnableKeyRotation - - kms:GetKeyPolicy - - kms:GetKeyRotationStatus - - kms:ListAliases - - kms:ListKeyPolicies - - kms:ListKeys - - kms:PutKeyPolicy - - kms:TagResource - - kms:UpdateAlias - - kms:UpdateKeyDescription + AWS: + - !Join ['', ['arn:aws:iam::', !Ref 'AWS::AccountId', ':root']] + Resource: '*' + - + Sid: Allow-Decrypt-From-Instances + Action: + - kms:Decrypt Effect: Allow - Resource: - - '*' + Principal: + AWS: + - !Ref 'cmsIamRoleArn' + Resource: '*' + - + Sid: Allow-Account-Admin + Action: + - kms:* + Effect: Allow + Principal: + AWS: + !Ref 'accountAdminArn' + Resource: '*' Version: '2012-10-17' - PolicyName: CmsKmsPolicy - Roles: - - Ref: 'CmsIamRole' - Type: AWS::IAM::Policy - ConfigFileKey: + Tags: + - Key: created_by + Value: cerberus_cli + - Key: created_for + Value: cerberus_cli + + CmsSecureDataKmsCmk: + Type: AWS::KMS::Key Properties: - Description: Cerberus encryption key for storing config files in an encrypted + Description: - !Join ['', ['Environment: ', !Ref 'environmentName', ' CMS KMS CMK for storing secure data in RDS.']] state. Enabled: 'true' KeyPolicy: Statement: - - Action: + - + Sid: Allow-Root-User + Action: - kms:* Effect: Allow Principal: AWS: - !Join ['', ['arn:aws:iam::', !Ref 'AWS::AccountId', ':root']] Resource: '*' - Sid: Allow-Root-User - - Action: + - + Sid: Allow-Decrypt-From-Cms-Instances + Action: + - kms:Encrypt - kms:Decrypt + - kms:ReEncryptTo + - kms:ReEncryptFrom + - kms:GenerateDataKey + - kms:GenerateDataKeyWithoutPlaintext Effect: Allow Principal: AWS: - - !GetAtt 'CmsIamRole.Arn' + - !Ref 'cmsIamRoleArn' Resource: '*' - Sid: Allow-Decrypt-From-Instances - - Action: + - + Sid: Allow-Account-Admin + Action: - kms:* Effect: Allow Principal: AWS: - Ref: 'accountAdminArn' + !Ref 'accountAdminArn' Resource: '*' - Sid: Allow-Account-Admin Version: '2012-10-17' - Type: AWS::KMS::Key \ No newline at end of file + Tags: + - Key: created_by + Value: cerberus_cli + - Key: created_for + Value: cerberus_cms diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml index 94e5741e..66d77dcc 100644 --- a/src/main/resources/cloudformation/database.yaml +++ b/src/main/resources/cloudformation/database.yaml @@ -29,7 +29,7 @@ Parameters: AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' Description: The third availability zone for the DB Instances Type: String - cmsDbInstanceSize: + cmsDbInstanceClass: Default: db.r3.large Description: MySQL DB instance class Type: String @@ -85,7 +85,7 @@ Resources: Properties: AvailabilityZone: !Ref 'cmsDbInstanceAz1' DBClusterIdentifier: !Ref CmsDatabaseCluster - DBInstanceClass: !Ref 'cmsDbInstanceSize' + DBInstanceClass: !Ref 'cmsDbInstanceClass' DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' Engine: aurora @@ -95,7 +95,7 @@ Resources: Properties: AvailabilityZone: !Ref 'cmsDbInstanceAz2' DBClusterIdentifier: !Ref CmsDatabaseCluster - DBInstanceClass: !Ref 'cmsDbInstanceSize' + DBInstanceClass: !Ref 'cmsDbInstanceClass' DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' Engine: aurora diff --git a/src/main/resources/cloudformation/iam-roles.yaml b/src/main/resources/cloudformation/iam-roles.yaml new file mode 100644 index 00000000..b901d690 --- /dev/null +++ b/src/main/resources/cloudformation/iam-roles.yaml @@ -0,0 +1,58 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Creates the necessary IAM roles, S3 buckets, and KMS keys for Cerberus +Outputs: + cmsIamRoleArn: + Value: !GetAtt 'CmsIamRole.Arn' + Export: + Name: !Sub "${AWS::StackName}-cmsIamRoleArn" + cmsIamRoleName: + Value: !Ref 'CmsIamRole' + Export: + Name: !Sub "${AWS::StackName}-cmsIamRoleName" +Resources: + CmsIamRole: + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: + - sts:AssumeRole + Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Version: '2012-10-17' + Path: / + Policies: + - PolicyName: "cms-cf-signals-policy" + PolicyDocument: + Statement: + - Action: + - EC2:Describe* + - cloudformation:SignalResource + Effect: Allow + Resource: '*' + - PolicyName: "cms-kms-policy" + PolicyDocument: + Statement: + - Action: + - kms:CreateAlias + - kms:CreateKey + - kms:DeleteAlias + - kms:DescribeKey + - kms:DisableKey + - kms:DisableKeyRotation + - kms:EnableKey + - kms:EnableKeyRotation + - kms:GetKeyPolicy + - kms:GetKeyRotationStatus + - kms:ListAliases + - kms:ListKeyPolicies + - kms:ListKeys + - kms:PutKeyPolicy + - kms:TagResource + - kms:UpdateAlias + - kms:UpdateKeyDescription + Effect: Allow + Resource: + - '*' + Type: AWS::IAM::Role \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index bd4f86fa..c1469df7 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -22,22 +22,21 @@ import com.nike.cerberus.command.StackDelegate; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; -import com.nike.cerberus.command.core.CreateBaseCommand; -import com.nike.cerberus.command.core.UpdateStackCommand; +import com.nike.cerberus.command.core.InitializeEnvironmentCommand; import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import java.io.InputStream; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -@Ignore("This is completely redone in next PR") public class EnvironmentConfigToArgsMapperTest { private EnvironmentConfig environmentConfig; @@ -50,6 +49,11 @@ public void before() throws Exception { environmentConfig = mapper.readValue(yamlStream, EnvironmentConfig.class); } + @Test + public void test_that_the_example_yaml_can_be_deserialized() { + assertNotNull(environmentConfig); + } + @Test public void test_that_mapper_copies_pre_command_flags_with_flag() { String commandName = "I-don't-exist"; @@ -85,18 +89,21 @@ public void test_that_mapper_copies_pre_command_flags_without_flag() { @Test - public void test_create_base() { - String commandName = CreateBaseCommand.COMMAND_NAME; + public void test_initialize_environment() { + String commandName = InitializeEnvironmentCommand.COMMAND_NAME; String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; String[] expected = { "-f", "/path/to/environment.yaml", commandName, - "--admin-role-arn", "arn:aws:iam::111111111:role/onelogin-roles-OneLoginAdminRole-2222222222", - "--owner-email", "obvisouly.fake@nike.com", - "--costcenter", "11111", - "--owner-group", "engineering team name" + InitializeEnvironmentCommand.ADMIN_ROLE_ARN_LONG_ARG, "arn:aws:iam::111111111:role/admin", + InitializeEnvironmentCommand.REGION_LONG_ARG, "us-west-2", + InitializeEnvironmentCommand.REGION_LONG_ARG, "us-east-1", + InitializeEnvironmentCommand.PRIMARY_REGION, "us-west-2", + "-TownerEmail=obvisouly.fake@nike.com", + "-TcostCenter=11111", + "-TownerGroup=engineering-team-name" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -113,8 +120,7 @@ public void test_upload_cert_without_overwrite() { String[] expected = { "-f", "/path/to/environment.yaml", commandName, - "--stack-name", "cms", - "--cert-path", "/home/fieldju/development/cerberus_environments/demo/certs/" + UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG, "/path/to/certs/" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -126,14 +132,12 @@ public void test_upload_cert_without_overwrite() { public void test_upload_cert_with_overwrite() { String commandName = UploadCertificateFilesCommand.COMMAND_NAME; - String[] userInput = {"-f", "/path/to/environment.yaml", commandName, "--stack-name", "cms", "--overwrite"}; + String[] userInput = {"-f", "/path/to/environment.yaml", commandName}; String[] expected = { "-f", "/path/to/environment.yaml", commandName, - "--stack-name", "cms", - "--cert-path", "/home/fieldju/development/cerberus_environments/demo/certs/", - "--overwrite" + "--cert-dir-path", "/path/to/certs/", }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -153,9 +157,9 @@ public void test_create_management_service_cluster() { StackDelegate.AMI_ID_LONG_ARG, "ami-3333", StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", -// TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", -// TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", -// TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name" + "-TownerEmail=obvisouly.fake@nike.com", + "-TcostCenter=11111", + "-TownerGroup=engineering-team-name" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); @@ -173,11 +177,7 @@ public void test_vpc_access_whitelist() { "-f", "/path/to/environment.yaml", commandName, WhitelistCidrForVpcAccessCommand.CIDR_LONG_ARG, "50.39.106.150/32", - WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "443", - WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "8080", - WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "8200", - WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "8500", - WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "8400", + WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "8443", WhitelistCidrForVpcAccessCommand.PORT_LONG_ARG, "22" }; @@ -195,60 +195,12 @@ public void test_create_cms_config() { String[] expected = { "-f", "/path/to/environment.yaml", commandName, - CreateCmsConfigCommand.ADMIN_GROUP_LONG_ARG, "lst-cerberus-admins", + CreateCmsConfigCommand.ADMIN_GROUP_LONG_ARG, "cerberus-admins", CreateCmsConfigCommand.PROPERTY_SHORT_ARG, "cms.auth.connector=com.nike.cerberus.auth.connector.onelogin.OneLoginAuthConnector", CreateCmsConfigCommand.PROPERTY_SHORT_ARG, "auth.connector.onelogin.api_region=us", CreateCmsConfigCommand.PROPERTY_SHORT_ARG, "auth.connector.onelogin.client_id=123", CreateCmsConfigCommand.PROPERTY_SHORT_ARG, "auth.connector.onelogin.client_secret=312", - CreateCmsConfigCommand.PROPERTY_SHORT_ARG, "auth.connector.onelogin.subdomain=nike" - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - - @Test - public void test_update_stack_with_no_overwrite_flag_or_dyn_props() { - String commandName = UpdateStackCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName, EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms"}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms", - StackDelegate.AMI_ID_LONG_ARG, "ami-3333", - StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", - StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", -// TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", -// TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", -// TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", - }; - - String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); - - assertArgsAreEqual(expected, actual, commandName); - } - - @Test - public void test_update_stack_with_overwrite_flag_and_dyn_props() { - String commandName = UpdateStackCommand.COMMAND_NAME; - - String[] userInput = {"-f", "/path/to/environment.yaml", commandName, EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms", "--overwrite-template", "-P", "k=v"}; - - String[] expected = { - "-f", "/path/to/environment.yaml", - commandName, - EnvironmentConfigToArgsMapper.STACK_NAME_KEY, "cms", - StackDelegate.AMI_ID_LONG_ARG, "ami-3333", - StackDelegate.INSTANCE_SIZE_LONG_ARG, "m3.medium", - StackDelegate.KEY_PAIR_NAME_LONG_ARG, "cerberus-test", -// TagParametersDelegate.COST_CENTER_LONG_ARG, "11111", -// TagParametersDelegate.OWNER_EMAIL_LONG_ARG, "obvisouly.fake@nike.com", -// TagParametersDelegate.OWNER_GROUP_LONG_ARG, "engineering team name", - UpdateStackCommand.OVERWRITE_TEMPLATE_LONG_ARG, - UpdateStackCommand.PARAMETER_SHORT_ARG, "k=v", + CreateCmsConfigCommand.PROPERTY_SHORT_ARG, "auth.connector.onelogin.subdomain=example" }; String[] actual = EnvironmentConfigToArgsMapper.getArgs(environmentConfig, userInput); diff --git a/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java b/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java new file mode 100644 index 00000000..e66a47b2 --- /dev/null +++ b/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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.environment; + +import com.amazonaws.regions.Regions; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nike.cerberus.module.CerberusModule; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class EnvironmentDataTest { + + @Test + public void test_that_environment_data_can_be_serialized_and_deserialized() throws IOException { + EnvironmentData expected = new EnvironmentData(); + expected.setEnvironmentName("test-env-name"); + expected.setAdminIamRoleArn("admin-role"); + expected.setCmsIamRoleArn("cms-role"); + expected.setRootIamRoleArn("root-role"); + RegionData regionData = new RegionData(); + regionData.setPrimary(true); + regionData.setConfigBucket("foo"); + regionData.setEnvironmentDataKmsCmkArn("bar"); + regionData.setCmsSecureDataKmsCmkArn("bam"); + expected.addRegionData(Regions.US_WEST_2, regionData); + + ObjectMapper mapper = CerberusModule.configObjectMapper(); + + String serializedEnvData = mapper.writeValueAsString(expected); + + EnvironmentData actual = mapper.readValue(serializedEnvData, EnvironmentData.class); + + assertEquals(expected.getEnvironmentName(), actual.getEnvironmentName()); + assertEquals(expected.getAdminIamRoleArn(), actual.getAdminIamRoleArn()); + assertEquals(expected.getCmsIamRoleArn(), actual.getCmsIamRoleArn()); + assertEquals(expected.getRootIamRoleArn(), actual.getRootIamRoleArn()); + assertEquals(expected.getRegionData().size(), actual.getRegionData().size()); + assertEquals(expected.getPrimaryRegion(), actual.getPrimaryRegion()); + } + +} diff --git a/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java b/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java index 3d1df1d1..2ad8e631 100644 --- a/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/AmiTagCheckServiceTest.java @@ -18,25 +18,45 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.DescribeImagesRequest; import com.amazonaws.services.ec2.model.DescribeImagesResult; import com.amazonaws.services.ec2.model.Filter; import com.amazonaws.services.ec2.model.Image; +import com.nike.cerberus.store.ConfigStore; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; public class AmiTagCheckServiceTest { + @Mock + private ConfigStore configStore; + + @Mock + private AmazonEC2Client ec2Client; + + @Mock + private AwsClientFactory amazonEC2ClientFactory; + + @Before + public void before() { + initMocks(this); + when(amazonEC2ClientFactory.getClient(any())).thenReturn(ec2Client); + } + @Test public void isAmiWithTagExistTrue() { - AmazonEC2 ec2Client = mock(AmazonEC2.class); - AmiTagCheckService amiTagCheckService = new AmiTagCheckService(ec2Client); + AmiTagCheckService amiTagCheckService = new AmiTagCheckService(amazonEC2ClientFactory, configStore); String amiId = "ami-1234abcd"; String tagName = "sometag"; @@ -58,7 +78,7 @@ public void isAmiWithTagExistTrue() { @Test public void isAmiWithTagExistFalse() { AmazonEC2 ec2Client = mock(AmazonEC2.class); - AmiTagCheckService amiTagCheckService = new AmiTagCheckService(ec2Client); + AmiTagCheckService amiTagCheckService = new AmiTagCheckService(amazonEC2ClientFactory, configStore); String amiId = "ami-1234abcd"; String tagName = "sometag"; @@ -80,7 +100,7 @@ public void isAmiWithTagExistFalse() { @Test public void isAmiWithTagExistNotFound() { AmazonEC2 ec2Client = mock(AmazonEC2.class); - AmiTagCheckService amiTagCheckService = new AmiTagCheckService(ec2Client); + AmiTagCheckService amiTagCheckService = new AmiTagCheckService(amazonEC2ClientFactory, configStore); String amiId = "ami-1234abcd"; String tagName = "sometag"; @@ -103,7 +123,7 @@ public void isAmiWithTagExistNotFound() { @Test public void isAmiWithTagExistThrowException() { AmazonEC2 ec2Client = mock(AmazonEC2.class); - AmiTagCheckService amiTagCheckService = new AmiTagCheckService(ec2Client); + AmiTagCheckService amiTagCheckService = new AmiTagCheckService(amazonEC2ClientFactory, configStore); String amiId = "ami-1234abcd"; String tagName = "sometag"; diff --git a/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java b/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java index 0c58fc0d..7b6101f9 100644 --- a/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/AutoScalingServiceTest.java @@ -16,7 +16,7 @@ package com.nike.cerberus.service; -import com.amazonaws.services.autoscaling.AmazonAutoScaling; +import com.amazonaws.services.autoscaling.AmazonAutoScalingClient; import com.amazonaws.services.autoscaling.model.AutoScalingGroup; import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsRequest; import com.amazonaws.services.autoscaling.model.DescribeAutoScalingGroupsResult; @@ -24,34 +24,49 @@ import com.amazonaws.services.autoscaling.model.ExitStandbyRequest; import com.amazonaws.services.autoscaling.model.Instance; import com.amazonaws.services.autoscaling.model.UpdateAutoScalingGroupRequest; -import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.DescribeInstancesRequest; import com.amazonaws.services.ec2.model.DescribeInstancesResult; import com.amazonaws.services.ec2.model.Reservation; +import com.nike.cerberus.store.ConfigStore; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import java.util.List; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; public class AutoScalingServiceTest { - private AmazonAutoScaling autoScalingClient; + @Mock + private ConfigStore configStore; - private AmazonEC2 ec2Client; + @Mock + private AmazonAutoScalingClient autoScalingClient; + + @Mock + private AmazonEC2Client ec2Client; + + @Mock + private AwsClientFactory amazonEC2ClientFactory; + + @Mock + private AwsClientFactory amazonAutoScalingClientFactory; private AutoScalingService autoScalingService; @Before public void setup() { - autoScalingClient = mock(AmazonAutoScaling.class); - ec2Client = mock(AmazonEC2.class); + initMocks(this); + when(amazonEC2ClientFactory.getClient(any())).thenReturn(ec2Client); + when(amazonAutoScalingClientFactory.getClient(any())).thenReturn(autoScalingClient); - autoScalingService = new AutoScalingService(autoScalingClient, ec2Client); + autoScalingService = new AutoScalingService(amazonAutoScalingClientFactory, amazonEC2ClientFactory, configStore); } @Test diff --git a/src/test/java/com/nike/cerberus/service/CertificateServiceTest.java b/src/test/java/com/nike/cerberus/service/CertificateServiceTest.java new file mode 100644 index 00000000..aa9197ff --- /dev/null +++ b/src/test/java/com/nike/cerberus/service/CertificateServiceTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 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.services.route53.AmazonRoute53; +import com.amazonaws.services.route53.AmazonRoute53Client; +import com.nike.cerberus.store.ConfigStore; +import com.nike.cerberus.util.UuidSupplier; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.io.File; +import java.io.IOException; +import java.security.KeyPair; +import java.util.UUID; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +public class CertificateServiceTest { + + @Mock private ConsoleService console; + @Mock private AmazonRoute53Client route53; + @Mock AwsClientFactory route53ClientFactory; + @Mock private UuidSupplier uuidSupplier; + @Mock private ConfigStore configStore; + @Mock private IdentityManagementService identityManagementService; + + private CertificateService certificateService; + + @Before + public void before() { + initMocks(this); + + when(route53ClientFactory.getClient(any())).thenReturn(route53); + certificateService = new CertificateService( + console, + route53ClientFactory, + uuidSupplier, + configStore, + identityManagementService, + "test", + "us-west-2" + ); + } + + /** + * @throws IOException + */ + @Test + public void test_that_createPKCS8PrivateKeyPemFileFromKeyPair_creates_pkcs8_file() throws IOException { + File certDir = new File("build/certs/" + UUID.randomUUID().toString()); + FileUtils.forceMkdir(certDir); + KeyPair privateKey = certificateService.loadOrCreateKeyPair( + new File(certDir.getAbsolutePath() + File.separator + CertificateService.DOMAIN_PKCS1_KEY_FILE)); + + certificateService.createPKCS8PrivateKeyPemFileFromKeyPair(privateKey, certDir); + + File pkcs8 = new File(certDir.getAbsolutePath() + File.separator + CertificateService.DOMAIN_PKCS8_KEY_FILE); + + assertTrue(pkcs8.exists()); + } + +} diff --git a/src/test/java/com/nike/cerberus/service/Ec2ServiceTest.java b/src/test/java/com/nike/cerberus/service/Ec2ServiceTest.java index 19976453..44db04ca 100644 --- a/src/test/java/com/nike/cerberus/service/Ec2ServiceTest.java +++ b/src/test/java/com/nike/cerberus/service/Ec2ServiceTest.java @@ -18,6 +18,7 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.services.ec2.AmazonEC2; +import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.AvailabilityZone; import com.amazonaws.services.ec2.model.AvailabilityZoneState; import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult; @@ -32,8 +33,10 @@ import com.amazonaws.services.ec2.model.KeyPairInfo; import com.amazonaws.services.ec2.model.RebootInstancesRequest; import com.amazonaws.services.ec2.model.Reservation; +import com.nike.cerberus.store.ConfigStore; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import java.util.List; @@ -44,21 +47,30 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; public class Ec2ServiceTest { - private AmazonEC2 ec2Client; + @Mock + ConfigStore configStore; + + @Mock + AmazonEC2Client ec2Client; + + @Mock + AwsClientFactory amazonEC2ClientFactory; private Ec2Service ec2Service; @Before public void setup() { - ec2Client = mock(AmazonEC2.class); - - ec2Service = new Ec2Service(ec2Client); + initMocks(this); + when(amazonEC2ClientFactory.getClient(any())).thenReturn(ec2Client); + ec2Service = new Ec2Service(amazonEC2ClientFactory, configStore); } @Test diff --git a/src/test/resources/environment.yaml b/src/test/resources/environment.yaml index 6613a6a4..47f57ae1 100644 --- a/src/test/resources/environment.yaml +++ b/src/test/resources/environment.yaml @@ -1,17 +1,12 @@ -version: 1 - # The name of the environment environment-name: demo -# The primary region the environment will run in, this is where the ASG and all region specific resources will be created. -primary-region: us-west-2 - # Optional map of tags that will be applied to any resource created by the CloudFormation Service in this CLI global-tags: # Name: cerberus-{env} is automatically added to this map - cost-center: 11111 - owner-email: obvisouly.fake@nike.com - owner-group: engineering-team-name + costCenter: 11111 + ownerEmail: obvisouly.fake@nike.com + ownerGroup: engineering-team-name # A IAM role ARN that will be given elevated privileges for the KMS CMK created. admin-role-arn: arn:aws:iam::111111111:role/admin @@ -20,7 +15,7 @@ admin-role-arn: arn:aws:iam::111111111:role/admin # By default the following records will be created # {environment-name}.{base-domain} ex: demo.example.com # origin.{environment-name}.{base-domain} ex: origin.demo.example.com -# {environment-name}.{primary-primaryRegion}.{base-domain} ex: demo.us-west-2.example.com +# {environment-name}.{region}.{base-domain} ex: demo.us-west-2.example.com # In this example: # demo.example.com points to origin.demo.example.com # origin.demo.example points to demo.us-west-2.example.com @@ -30,10 +25,9 @@ base-domain-name: example.com # Edge domain name defaults to {environment}.{base-domain} edge-domain-name-override: new-demo.example.com + # Origin domain name defaults to origin.{environment-name}.{base-domain} override it here origin-domain-name-override: origin.new-demo.demo.com -# Load balancer domain name defaults to {environment-name}.{primary-primaryRegion}.{base-domain} override it here -load-balancer-domain-name-override: new-demo.us-west-2.demo.com # Add additional subject names to the generated cert # For example if your standing up a new architecture edition of Cerberus and you still have the existing env up you can add those domains @@ -44,6 +38,9 @@ additional-subject-names: - demo.us-west-2.example.com - demo.us-east-1.example.com +# The hosted zone id that will allow the registration of the above domain names +hosted-zone-id: X5CT6JROG9F2DR + # The SSL Policy that will get applied to the application load balancer # see http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html # If this is not defined than we default to 'TLS-1-2-2017-01' in the old cerberus arch we defaulted to @@ -51,9 +48,6 @@ additional-subject-names: # 1.1 support use ELBSecurityPolicy-2016-08 # load-balancer-ssl-policy-override: ELBSecurityPolicy-2016-08 -# The hosted zone id that will allow the registration of the above domain names -hosted-zone-id: X5CT6JROG9F2DR - # If you wish to use your own certs set this to false or else fill out the ACME api information # Else if you are providing your own keys and certs the following files are required. # @@ -95,26 +89,24 @@ acme-api-url: acme://letsencrypt.org enable-le-cert-fix: true # Contact email to send to ACME provider when creating certs acme-contact-email: justin.field@example.com -# Folder to save generated files -local-folder-to-store-certs: /path/to/saved/certs/demo/certs/ +# The path to the certs, if you are generating the certs using an ACME provider +# If you wish to provide your own certs set generate to false and +# see the comment header above generate-keys-and-certs for required files that must be in this folder. +certificate-directory: /path/to/certs/ -# Cerberus Management Service Config +# Here we can define the ports and CIDRs that we want to allow ingress into the Cerberus VPC for this environment +# You can add your current ip or proxy boxes here and use the above proxy config +vpc-access-whitelist: + ports: + - 8443 #CMS SSL Port for health check rolling reboot command + - 22 #SSH + cidrs: + - 50.39.106.150/32 + +# Global Cerberus Management Service config management-service: - # The path to the certs, if you are generating the certs using an ACME provider - # just simply make this the same value as local-folder-to-store-certs or use the path to the certs you - # created outside this CLI. See the comment header above generate-keys-and-certs for required files. - cert-path: /path/to/saved/certs/demo/certs/ - # The ami id for CMS, see https://github.com/Nike-Inc/cerberus-util-scripts to bake an ami - ami-id: ami-3333 - instance-size: m3.medium - key-pair-name: cerberus-test # Group that has admin privileges in CMS. admin-group: cerberus-admins - # Cerberus uses the Amazon encryption library backed by KMS to encrypt secure data, this library supports encrypting - # data with data keys created by multiple KMS CMKs to make the data more highly available in case of region outage. - # Cerberus requires at least 1 additional region for CMK creation, the primary region is used automatically - additional-encryption-cmk-regions: - - us-east-1 # Dynamic parameters for setting additional properties in the CMS # See https://github.com/Nike-Inc/cerberus-management-service#configurable-properties # Also see https://github.com/Nike-Inc/cerberus-management-service/blob/master/src/main/resources/cms.conf @@ -128,11 +120,28 @@ management-service: - auth.connector.onelogin.client_secret=312 - auth.connector.onelogin.subdomain=example -# Here we can define the ports and CIDRs that we want to allow ingress into the Cerberus VPC for this environment -# You can add your current ip or proxy boxes here and use the above proxy config -vpc-access-whitelist: - ports: - - 8443 #CMS SSL Port for health check rolling reboot command - - 22 #SSH - cidrs: - - 50.39.106.150/32 \ No newline at end of file + +region-specific-configuration: + us-west-2: + primary: true + rds: + size: db.r3.large + management-service: + # The ami id to use for CMS, see https://github.com/Nike-Inc/cerberus-util-scripts to bake an AMI + ami-id: ami-3333 + # The instance size to use for the CMS ASG + instance-size: m3.medium + # The ssh key pair to use for the instances + key-pair-name: cerberus-test + # The desired instance count for the ASG, defaults to 3 + #desired-instances: 3 + # The max instance count for the ASG, defaults to 3 + #max-instances: 4 + # The desired instance count for the ASG, defaults to 3 + #min-instances: 3 + # Load balancer domain names default to {environment-name}.{region}.{base-domain} override it here + load-balancer-domain-name-override: new-demo.us-west-2.demo.com + # Cerberus uses the Amazon encryption library backed by KMS to encrypt secure data, this library supports encrypting + # data with data keys created by multiple KMS CMKs to make the data more highly available in case of region outage. + # Cerberus Management Service requires at least 2 regions for CMK creation. + us-east-1: ~ \ No newline at end of file From 2125d2de760390a1d59eb66901db247936224b93 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Fri, 8 Dec 2017 13:57:00 -0800 Subject: [PATCH 49/69] rename variables for better readabilty and clarity --- .../domain/cloudformation/ConfigOutputs.java | 20 +++++++++---------- .../domain/environment/EnvironmentData.java | 8 ++++---- .../domain/environment/RegionData.java | 20 +++++++++---------- .../com/nike/cerberus/store/ConfigStore.java | 12 +++++------ src/main/resources/cloudformation/config.yaml | 14 +++++++------ .../environment/EnvironmentDataTest.java | 4 ++-- 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java index 02d4b3a3..5424822a 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/ConfigOutputs.java @@ -22,9 +22,9 @@ public class ConfigOutputs { private String configBucketName; - private String environmentDataKmsCmkArn; + private String configCmkArn; - private String cmsSecureDataKmsCmkArn; + private String managementServiceCmkArn; public String getConfigBucketDomainName() { return configBucketDomainName; @@ -44,19 +44,19 @@ public ConfigOutputs setConfigBucketName(String configBucketName) { return this; } - public String getEnvironmentDataKmsCmkArn() { - return environmentDataKmsCmkArn; + public String getConfigCmkArn() { + return configCmkArn; } - public void setEnvironmentDataKmsCmkArn(String environmentDataKmsCmkArn) { - this.environmentDataKmsCmkArn = environmentDataKmsCmkArn; + public void setConfigCmkArn(String configCmkArn) { + this.configCmkArn = configCmkArn; } - public String getCmsSecureDataKmsCmkArn() { - return cmsSecureDataKmsCmkArn; + public String getManagementServiceCmkArn() { + return managementServiceCmkArn; } - public void setCmsSecureDataKmsCmkArn(String cmsSecureDataKmsCmkArn) { - this.cmsSecureDataKmsCmkArn = cmsSecureDataKmsCmkArn; + public void setManagementServiceCmkArn(String managementServiceCmkArn) { + this.managementServiceCmkArn = managementServiceCmkArn; } } diff --git a/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java b/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java index f2e8e7f4..11c876d4 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java +++ b/src/main/java/com/nike/cerberus/domain/environment/EnvironmentData.java @@ -118,7 +118,7 @@ public void addRegionData(Regions region, RegionData rData) { public List getConfigRegions() { List configRegions = new LinkedList<>(); regionData.forEach((region, rData) -> { - if (rData.getEnvironmentDataKmsCmkArn().isPresent() + if (rData.getConfigCmkArn().isPresent() && rData.getConfigBucket().isPresent()) { configRegions.add(region); } @@ -134,10 +134,10 @@ public Regions getPrimaryRegion() { .getKey(); } - public List getCmsCmkArns() { + public List getManagementServiceCmkArns() { return regionData.entrySet().stream() - .filter(entry -> entry.getValue().getCmsSecureDataKmsCmkArn().isPresent()) - .map(entry -> entry.getValue().getCmsSecureDataKmsCmkArn().get()) + .filter(entry -> entry.getValue().getManagementServiceCmkArn().isPresent()) + .map(entry -> entry.getValue().getManagementServiceCmkArn().get()) .collect(Collectors.toList()); } } diff --git a/src/main/java/com/nike/cerberus/domain/environment/RegionData.java b/src/main/java/com/nike/cerberus/domain/environment/RegionData.java index a8343878..fec4f4fd 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/RegionData.java +++ b/src/main/java/com/nike/cerberus/domain/environment/RegionData.java @@ -22,8 +22,8 @@ public class RegionData { private boolean primary; private String configBucket; - private String environmentDataKmsCmkArn; - private String cmsSecureDataKmsCmkArn; + private String configCmkArn; + private String managementServiceCmkArn; public boolean isPrimary() { return primary; @@ -41,20 +41,20 @@ public void setConfigBucket(String configBucket) { this.configBucket = configBucket; } - public Optional getEnvironmentDataKmsCmkArn() { - return Optional.ofNullable(environmentDataKmsCmkArn); + public Optional getConfigCmkArn() { + return Optional.ofNullable(configCmkArn); } - public void setEnvironmentDataKmsCmkArn(String environmentDataKmsCmkArn) { - this.environmentDataKmsCmkArn = environmentDataKmsCmkArn; + public void setConfigCmkArn(String configCmkArn) { + this.configCmkArn = configCmkArn; } - public Optional getCmsSecureDataKmsCmkArn() { - return Optional.ofNullable(cmsSecureDataKmsCmkArn); + public Optional getManagementServiceCmkArn() { + return Optional.ofNullable(managementServiceCmkArn); } - public void setCmsSecureDataKmsCmkArn(String cmsSecureDataKmsCmkArn) { - this.cmsSecureDataKmsCmkArn = cmsSecureDataKmsCmkArn; + public void setManagementServiceCmkArn(String managementServiceCmkArn) { + this.managementServiceCmkArn = managementServiceCmkArn; } } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index c45c45f2..8e7f6ea2 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -275,7 +275,7 @@ private Properties generateBaseCmsSystemProperties() { properties.put(JDBC_PASSWORD_KEY, cmsDatabasePassword); properties.put(CMS_ENV_NAME, environmentName); properties.put(CMS_CERTIFICATE_TO_USE, getCertificationInformationList().getLast().getCertificateName()); - properties.put(CMK_ARNS_KEY, StringUtils.join(data.getCmsCmkArns(), ",")); + properties.put(CMK_ARNS_KEY, StringUtils.join(data.getManagementServiceCmkArns(), ",")); return properties; } @@ -522,7 +522,7 @@ private void encryptAndSaveObject(String path, List environmentDataKmsCmkArns = new LinkedList<>(); environmentData.getRegionData().forEach((region, regionData) -> - regionData.getEnvironmentDataKmsCmkArn().ifPresent(environmentDataKmsCmkArns::add)); + regionData.getConfigCmkArn().ifPresent(environmentDataKmsCmkArns::add)); MasterKeyProvider encryptProvider = initializeKeyProvider(environmentDataKmsCmkArns); String encryptedObject = encryptionService.encrypt(encryptProvider, plaintextSerializedObject); @@ -627,8 +627,8 @@ public void initializeEnvironment(String adminRoleArn, regionData.setPrimary(true); } regionData.setConfigBucket(output.getConfigBucketName()); - regionData.setEnvironmentDataKmsCmkArn(output.getEnvironmentDataKmsCmkArn()); - regionData.setCmsSecureDataKmsCmkArn(output.getCmsSecureDataKmsCmkArn()); + regionData.setConfigCmkArn(output.getConfigCmkArn()); + regionData.setManagementServiceCmkArn(output.getManagementServiceCmkArn()); environmentData.addRegionData(region, regionData); }); @@ -649,7 +649,7 @@ public String getCmsCmkForRegion(Regions region) { throw new RuntimeException("There is no region data for region: " + region.getName()); } RegionData data = getDecryptedEnvironmentData().getRegionData().get(region); - return data.getCmsSecureDataKmsCmkArn().orElseThrow(() -> + return data.getManagementServiceCmkArn().orElseThrow(() -> new RuntimeException("There is no cms cmk configured for region: " + region.getName())); } @@ -658,7 +658,7 @@ public String getEnvironmentDataSecureDataKmsCmkRegion(Regions region) { throw new RuntimeException("There is no region data for region: " + region.getName()); } RegionData data = getDecryptedEnvironmentData().getRegionData().get(region); - return data.getEnvironmentDataKmsCmkArn().orElseThrow(() -> + return data.getConfigCmkArn().orElseThrow(() -> new RuntimeException("There is no cms cmk configured for region: " + region.getName())); } diff --git a/src/main/resources/cloudformation/config.yaml b/src/main/resources/cloudformation/config.yaml index 80fcda45..e59242af 100644 --- a/src/main/resources/cloudformation/config.yaml +++ b/src/main/resources/cloudformation/config.yaml @@ -5,10 +5,10 @@ Outputs: Value: !GetAtt 'CerberusConfigBucket.DomainName' configBucketName: Value: !Ref 'CerberusConfigBucket' - environmentDataKmsCmkArn: - Value: !GetAtt 'EnvironmentDataKmsCmk.Arn' - cmsSecureDataKmsCmkArn: - Value: !GetAtt 'CmsSecureDataKmsCmk.Arn' + configCmkArn: + Value: !GetAtt 'ConfigCmk.Arn' + managementServiceCmkArn: + Value: !GetAtt 'ManagementServiceCmk.Arn' Parameters: cmsIamRoleArn: Description: The ARN for for the management service IAM Role. @@ -55,7 +55,8 @@ Resources: - !Join ['', ['arn:aws:s3:::', !Ref 'CerberusConfigBucket', /cms]] Sid: Allow-Bucket-Access-For-CMS Version: '2012-10-17' - EnvironmentDataKmsCmk: + # KMS Customer Master Key for this CLI to use when saving config + ConfigCmk: Type: AWS::KMS::Key Properties: Description: - !Join ['', ['Environment: ', !Ref 'environmentName', ' Cerberus encryption key for storing config files in s3 buckets.']] @@ -96,7 +97,8 @@ Resources: - Key: created_for Value: cerberus_cli - CmsSecureDataKmsCmk: + # Cerberus Management Service KMS Customer Master Key + ManagementServiceCmk: Type: AWS::KMS::Key Properties: Description: - !Join ['', ['Environment: ', !Ref 'environmentName', ' CMS KMS CMK for storing secure data in RDS.']] diff --git a/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java b/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java index e66a47b2..e5b40077 100644 --- a/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java +++ b/src/test/java/com/nike/cerberus/domain/environment/EnvironmentDataTest.java @@ -37,8 +37,8 @@ public void test_that_environment_data_can_be_serialized_and_deserialized() thro RegionData regionData = new RegionData(); regionData.setPrimary(true); regionData.setConfigBucket("foo"); - regionData.setEnvironmentDataKmsCmkArn("bar"); - regionData.setCmsSecureDataKmsCmkArn("bam"); + regionData.setConfigCmkArn("bar"); + regionData.setManagementServiceCmkArn("bam"); expected.addRegionData(Regions.US_WEST_2, regionData); ObjectMapper mapper = CerberusModule.configObjectMapper(); From b36c80623edae708d608aeb5390b5c143f89db16 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Mon, 11 Dec 2017 17:37:06 -0800 Subject: [PATCH 50/69] Fix naming bag with YAML and invalid config YAML --- .../cerberus/domain/environment/Stack.java | 2 +- src/main/resources/cloudformation/config.yaml | 68 +++++++++---------- .../resources/cloudformation/iam-roles.yaml | 2 +- 3 files changed, 34 insertions(+), 38 deletions(-) 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 042cfd9b..d86b5674 100644 --- a/src/main/java/com/nike/cerberus/domain/environment/Stack.java +++ b/src/main/java/com/nike/cerberus/domain/environment/Stack.java @@ -32,7 +32,7 @@ */ public class Stack implements Comparable { - public static final Stack IAM_ROLES = new Stack("base-iam-roles", "base-iam-roles.yaml", false); + public static final Stack IAM_ROLES = new Stack("iam-roles", "iam-roles.yaml", false); public static final Stack CONFIG = new Stack("config", "config.yaml", false); public static final Stack VPC = new Stack("vpc", "vpc.yaml", false); public static final Stack SECURITY_GROUPS = new Stack("security-groups", "security-groups.yaml", false); diff --git a/src/main/resources/cloudformation/config.yaml b/src/main/resources/cloudformation/config.yaml index e59242af..354ea265 100644 --- a/src/main/resources/cloudformation/config.yaml +++ b/src/main/resources/cloudformation/config.yaml @@ -1,5 +1,5 @@ AWSTemplateFormatVersion: '2010-09-09' -Description: Creates the necessary S3 buckets, and KMS CMK for Cerberus +Description: Creates the S3 config bucket and KMS CMK for Cerberus Outputs: configBucketDomainName: Value: !GetAtt 'CerberusConfigBucket.DomainName' @@ -59,7 +59,7 @@ Resources: ConfigCmk: Type: AWS::KMS::Key Properties: - Description: - !Join ['', ['Environment: ', !Ref 'environmentName', ' Cerberus encryption key for storing config files in s3 buckets.']] + Description: !Join ['', ['Environment: ', !Ref 'environmentName', ' Cerberus encryption key for storing config files in s3 buckets.']] Enabled: 'true' KeyPolicy: Statement: @@ -101,43 +101,39 @@ Resources: ManagementServiceCmk: Type: AWS::KMS::Key Properties: - Description: - !Join ['', ['Environment: ', !Ref 'environmentName', ' CMS KMS CMK for storing secure data in RDS.']] - state. + Description: !Join ['', ['Environment: ', !Ref 'environmentName', ' CMS KMS CMK for storing secure data in RDS.']] Enabled: 'true' KeyPolicy: Statement: - - - Sid: Allow-Root-User - Action: - - kms:* - Effect: Allow - Principal: - AWS: - - !Join ['', ['arn:aws:iam::', !Ref 'AWS::AccountId', ':root']] - Resource: '*' - - - Sid: Allow-Decrypt-From-Cms-Instances - Action: - - kms:Encrypt - - kms:Decrypt - - kms:ReEncryptTo - - kms:ReEncryptFrom - - kms:GenerateDataKey - - kms:GenerateDataKeyWithoutPlaintext - Effect: Allow - Principal: - AWS: - - !Ref 'cmsIamRoleArn' - Resource: '*' - - - Sid: Allow-Account-Admin - Action: - - kms:* - Effect: Allow - Principal: - AWS: - !Ref 'accountAdminArn' - Resource: '*' + - Sid: Allow-Root-User + Action: + - kms:* + Effect: Allow + Principal: + AWS: + - !Join ['', ['arn:aws:iam::', !Ref 'AWS::AccountId', ':root']] + Resource: '*' + - Sid: Allow-Decrypt-From-Cms-Instances + Action: + - kms:Encrypt + - kms:Decrypt + - kms:ReEncryptTo + - kms:ReEncryptFrom + - kms:GenerateDataKey + - kms:GenerateDataKeyWithoutPlaintext + Effect: Allow + Principal: + AWS: + - !Ref 'cmsIamRoleArn' + Resource: '*' + - Sid: Allow-Account-Admin + Action: + - kms:* + Effect: Allow + Principal: + AWS: + !Ref 'accountAdminArn' + Resource: '*' Version: '2012-10-17' Tags: - Key: created_by diff --git a/src/main/resources/cloudformation/iam-roles.yaml b/src/main/resources/cloudformation/iam-roles.yaml index b901d690..7eacff2a 100644 --- a/src/main/resources/cloudformation/iam-roles.yaml +++ b/src/main/resources/cloudformation/iam-roles.yaml @@ -1,5 +1,5 @@ AWSTemplateFormatVersion: '2010-09-09' -Description: Creates the necessary IAM roles, S3 buckets, and KMS keys for Cerberus +Description: Creates the necessary IAM roles for Cerberus Outputs: cmsIamRoleArn: Value: !GetAtt 'CmsIamRole.Arn' From 0259e7d0a85db28a041f61529a3c2323a552d978 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Thu, 14 Dec 2017 09:36:09 -0800 Subject: [PATCH 51/69] Feature/mr snapshot copier command (#110) * Added command for copying RDS Cluster snapshots from primary region to secondary regions. * Update CopyRdsSnapshotsOperation.java * Update CopyRdsSnapshotsOperation.java * PR Feedback --- gradle/dependencies.gradle | 1 + .../com/nike/cerberus/cli/CerberusRunner.java | 4 +- .../cli/EnvironmentConfigToArgsMapper.java | 2 +- .../command/rds/CopyRdsSnapshotsCommand.java | 57 ++++++ .../{core => rds}/CreateDatabaseCommand.java | 6 +- .../composite/CreateEnvironmentOperation.java | 2 +- .../rds/CopyRdsSnapshotsOperation.java | 189 ++++++++++++++++++ .../CreateDatabaseOperation.java | 5 +- .../rds/CopyRdsSnapshotsOperationTest.java | 95 +++++++++ 9 files changed, 352 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/rds/CopyRdsSnapshotsCommand.java rename src/main/java/com/nike/cerberus/command/{core => rds}/CreateDatabaseCommand.java (91%) create mode 100644 src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java rename src/main/java/com/nike/cerberus/operation/{core => rds}/CreateDatabaseOperation.java (96%) create mode 100644 src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index ca56bf97..91492b8e 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -35,6 +35,7 @@ allprojects { compile group: 'com.amazonaws', name: 'aws-java-sdk-lambda', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: awsSDKVersion compile group: 'com.amazonaws', name: 'aws-java-sdk-elasticloadbalancingv2', version: awsSDKVersion + compile group: 'com.amazonaws', name: 'aws-java-sdk-rds', version: awsSDKVersion // https://mvnrepository.com/artifact/com.amazonaws/aws-encryption-sdk-java compile group: 'com.amazonaws', name: 'aws-encryption-sdk-java', version: '1.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 2a200689..58b5df57 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -35,7 +35,8 @@ import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; -import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand; +import com.nike.cerberus.command.rds.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateRoute53Command; @@ -193,6 +194,7 @@ private void registerAllCommands() { registerCommand(new DeleteEnvironmentCommand()); registerCommand(new RotateCertificatesCommand()); registerCommand(new DeleteOldestCertificatesCommand()); + registerCommand(new CopyRdsSnapshotsCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 0a39ca62..96eae6a2 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -23,7 +23,7 @@ import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; -import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.rds.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateRoute53Command; diff --git a/src/main/java/com/nike/cerberus/command/rds/CopyRdsSnapshotsCommand.java b/src/main/java/com/nike/cerberus/command/rds/CopyRdsSnapshotsCommand.java new file mode 100644 index 00000000..0b20aded --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/rds/CopyRdsSnapshotsCommand.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 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.rds; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.rds.CopyRdsSnapshotsOperation; + +import static com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand.COMMAND_NAME; + +@Parameters( + commandNames = COMMAND_NAME, + commandDescription = "Copies RDS cluster snapshots in the primary for this environment to the secondary regions" +) +public class CopyRdsSnapshotsCommand implements Command { + + public static final String COMMAND_NAME = "copy-rds-snapshots"; + + public static final String DAYS_LONG_ARG = "--days"; + + @Parameter( + names = DAYS_LONG_ARG, + description = "How old a RDS cluster snapshot can be and still be considered for copying, defaults to 1 day" + ) + private long days = 1; + + public long getDays() { + return days; + } + + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CopyRdsSnapshotsOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java b/src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java similarity index 91% rename from src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java rename to src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java index 13a19f2b..4dea6f6f 100644 --- a/src/main/java/com/nike/cerberus/command/core/CreateDatabaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.nike.cerberus.command.core; +package com.nike.cerberus.command.rds; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -22,9 +22,9 @@ import com.nike.cerberus.command.Command; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.CreateDatabaseOperation; +import com.nike.cerberus.operation.rds.CreateDatabaseOperation; -import static com.nike.cerberus.command.core.CreateDatabaseCommand.COMMAND_NAME; +import static com.nike.cerberus.command.rds.CreateDatabaseCommand.COMMAND_NAME; /** * Command to create the database for Cerberus. diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java index d0ef3744..75ad4138 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java @@ -21,7 +21,7 @@ import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; -import com.nike.cerberus.command.core.CreateDatabaseCommand; +import com.nike.cerberus.command.rds.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.command.core.CreateRoute53Command; diff --git a/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java b/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java new file mode 100644 index 00000000..4d3586c6 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2017 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.rds; + +import com.amazonaws.regions.Regions; +import com.amazonaws.services.rds.AmazonRDS; +import com.amazonaws.services.rds.AmazonRDSClient; +import com.amazonaws.services.rds.model.CopyDBClusterSnapshotRequest; +import com.amazonaws.services.rds.model.DBClusterSnapshot; +import com.amazonaws.services.rds.model.DescribeDBClusterSnapshotsRequest; +import com.amazonaws.services.rds.model.DescribeDBClusterSnapshotsResult; +import com.amazonaws.services.rds.model.Tag; +import com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.AwsClientFactory; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.store.ConfigStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + +/** + * Operation for CopyRdsSnapshotsCommand + * + * Copies rds cluster snapshots in the primary for this environment to the secondary regions + */ +public class CopyRdsSnapshotsOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ConfigStore configStore; + private final String environmentName; + private final AwsClientFactory amazonRDSClientFactory; + private final CloudFormationService cloudFormationService; + + @Inject + public CopyRdsSnapshotsOperation(ConfigStore configStore, + @Named(ENV_NAME) String environmentName, + AwsClientFactory amazonRDSClientFactory, + CloudFormationService cloudFormationService) { + + this.configStore = configStore; + this.environmentName = environmentName; + this.amazonRDSClientFactory = amazonRDSClientFactory; + this.cloudFormationService = cloudFormationService; + } + + @Override + public void run(CopyRdsSnapshotsCommand command) { + Regions primaryRegion = configStore.getPrimaryRegion(); + List snapshotsInPrimaryRegion = getDbSnapshots(primaryRegion).stream() + .filter(dbSnapshot -> + wasSnapshotGeneratedFromCmsCluster(dbSnapshot) && isSnapshotNewerThanGivenDays(dbSnapshot, command.getDays()) + ).collect(Collectors.toList()); + + configStore.getConfigEnabledRegions().forEach(region -> { + if (region.equals(primaryRegion)) { + return; + } + + List toRegionSnapshots = getDbSnapshots(region).stream() + .filter(this::wasSnapshotGeneratedFromCmsCluster) + .collect(Collectors.toList()); + + snapshotsInPrimaryRegion.forEach(fromSnapshot -> { + boolean needsCopying = toRegionSnapshots.stream().noneMatch(toSnapshot -> + getIdentifier(fromSnapshot).equals(getIdentifier(toSnapshot))); + if (needsCopying) { + log.info("Preparing to initiate copy of RDS DB Snapshot: {} located in region: {} to region: {}", + fromSnapshot.getDBClusterSnapshotIdentifier(), primaryRegion, region); + + copySnapshot(fromSnapshot, primaryRegion, region); + } else { + log.info("Snapshot: {} already copied to region: {}, skipping...", getIdentifier(fromSnapshot), region); + } + }); + }); + log.info("Finished issuing copy api calls to aws, it can take a long time for copies to finish."); + } + + /** + * Copies a snapshot from one region to another + * @param fromSnapshot The snapshot that is getting copied + * @param fromRegion The from region + * @param toRegion The to region + */ + private void copySnapshot(DBClusterSnapshot fromSnapshot, Regions fromRegion, Regions toRegion) { + AmazonRDS rds = amazonRDSClientFactory.getClient(toRegion); + rds.copyDBClusterSnapshot(new CopyDBClusterSnapshotRequest() + .withCopyTags(true) + .withSourceDBClusterSnapshotIdentifier(fromSnapshot.getDBClusterSnapshotArn()) + .withTargetDBClusterSnapshotIdentifier(getIdentifier(fromSnapshot)) + .withSourceRegion(fromRegion.getName()) + .withKmsKeyId(configStore.getEnvironmentData().getRegionData().get(toRegion).getConfigCmkArn().get()) + .withTags( + new Tag().withKey("created_by").withValue("cerberus_cli") + ) + ); + } + + /** + * Strips the rds: that aws prepends to automatic snapshots + * @param dbSnapshot the snapshot + * @return a string that can be used to identify automatic and manual copies rds:foo will return foo + */ + protected String getIdentifier(DBClusterSnapshot dbSnapshot) { + return dbSnapshot.getDBClusterSnapshotIdentifier().replace("rds:", ""); + } + + /** + * @return True if the snapshot was created for the cms database cluster for this environment + */ + protected boolean wasSnapshotGeneratedFromCmsCluster(DBClusterSnapshot dbSnapshot) { + String identifier = getIdentifier(dbSnapshot); + return identifier.startsWith(environmentName) && identifier.contains("cmsdatabasecluster"); + } + + /** + * @param dbSnapshot The snapshot under question + * @param days How many days to compare to + * @return True if the snapshot under question is newer than the days passed in + */ + protected boolean isSnapshotNewerThanGivenDays(DBClusterSnapshot dbSnapshot, long days) { + return ZonedDateTime.ofInstant(dbSnapshot.getSnapshotCreateTime().toInstant(), ZoneId.of("UTC")) + .isAfter(Instant.now().atZone(ZoneId.of("UTC")).minus(days, ChronoUnit.DAYS)); + } + + /** + * @return All the RDS Cluster Snapshots for a region + */ + protected List getDbSnapshots(Regions region) { + AmazonRDS rds = amazonRDSClientFactory.getClient(region); + List cmsDatabaseClusterSnapshots = new LinkedList<>(); + String next = null; + do { + DescribeDBClusterSnapshotsRequest req = new DescribeDBClusterSnapshotsRequest(); + + if (next != null) { + req.withMarker(next); + } + + DescribeDBClusterSnapshotsResult res = rds.describeDBClusterSnapshots(req); + + cmsDatabaseClusterSnapshots.addAll(res.getDBClusterSnapshots()); + next = res.getMarker(); + } while (next != null); + + return cmsDatabaseClusterSnapshots; + } + + @Override + public boolean isRunnable(CopyRdsSnapshotsCommand command) { + boolean isRunnable = true; + + if (cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.DATABASE.getFullName(environmentName))) { + log.error("The Database stuck must exist in the primary region in order to duplicate snapshots"); + isRunnable = false; + } + + return isRunnable; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java similarity index 96% rename from src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java rename to src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java index 61514849..79d167b3 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package com.nike.cerberus.operation.core; +package com.nike.cerberus.operation.rds; import com.amazonaws.regions.Regions; -import com.nike.cerberus.command.core.CreateDatabaseCommand; -import com.nike.cerberus.domain.cloudformation.DatabaseOutputs; +import com.nike.cerberus.command.rds.CreateDatabaseCommand; import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; import com.nike.cerberus.domain.cloudformation.VpcParameters; diff --git a/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java b/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java new file mode 100644 index 00000000..10efde87 --- /dev/null +++ b/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 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.rds; + +import com.amazonaws.services.rds.AmazonRDSClient; +import com.amazonaws.services.rds.model.DBClusterSnapshot; +import com.nike.cerberus.service.AwsClientFactory; +import com.nike.cerberus.store.ConfigStore; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.Date; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CopyRdsSnapshotsOperationTest { + + @Mock + private ConfigStore configStore; + + private final static String envName = "test"; + + @Mock + private AwsClientFactory clientFactory; + + private CopyRdsSnapshotsOperation operation; + + @Before + public void before() { + operation = new CopyRdsSnapshotsOperation(configStore, envName, clientFactory, cloudFormationService); + } + + @Test + public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_true_if_id_matches() { + boolean actual = operation.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() + .withDBClusterSnapshotIdentifier("rds:test-cerberus-database-cmsdatabasecluster-jns1lasdf9d-2017-12-12-18-07")); + + assertTrue(actual); + } + + @Test + public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_false_for_a_different_env() { + boolean actual = operation.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() + .withDBClusterSnapshotIdentifier("rds:dev-cerberus-database-cmsdatabasecluster-jns1lasdf9d-2017-12-12-18-07")); + + assertFalse(actual); + } + + @Test + public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_false_if_id_does_not_matche() { + boolean actual = operation.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() + .withDBClusterSnapshotIdentifier("rds:some-other-db-cluster-jns1lasdf9d-2017-12-12-18-07")); + + assertFalse(actual); + } + + @Test + public void test_that_isSnapshotNewerThan24Hours_returns_true_if_snapshot_is_newer_than_24h() { + DBClusterSnapshot ss = new DBClusterSnapshot().withSnapshotCreateTime(Date.from(Instant.now().atZone(ZoneId.of("UTC")).toInstant())); + + boolean actual = operation.isSnapshotNewerThanGivenDays(ss, 1); + + assertTrue(actual); + } + + @Test + public void test_that_isSnapshotNewerThan24Hours_returns_false_if_snapshot_is_older_than_24h() { + DBClusterSnapshot ss = new DBClusterSnapshot().withSnapshotCreateTime(Date.from(Instant.now().atZone(ZoneId.of("UTC")) + .minus(1, ChronoUnit.WEEKS).toInstant())); + + boolean actual = operation.isSnapshotNewerThanGivenDays(ss, 1); + + assertFalse(actual); + } + +} From 89a0c90bf4888b6cd00ca48849096819941de62a Mon Sep 17 00:00:00 2001 From: Justin Field Date: Thu, 14 Dec 2017 10:03:42 -0800 Subject: [PATCH 52/69] add mock service to Copy RDS Snapshot test --- .../cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java b/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java index 10efde87..5ea4744a 100644 --- a/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java +++ b/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java @@ -19,6 +19,7 @@ import com.amazonaws.services.rds.AmazonRDSClient; import com.amazonaws.services.rds.model.DBClusterSnapshot; import com.nike.cerberus.service.AwsClientFactory; +import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; import org.junit.Before; import org.junit.Test; @@ -37,6 +38,9 @@ public class CopyRdsSnapshotsOperationTest { @Mock private ConfigStore configStore; + @Mock + private CloudFormationService cloudFormationService; + private final static String envName = "test"; @Mock From 7ec2327883b2153a0b144180ff538644edb42917 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Thu, 14 Dec 2017 10:05:59 -0800 Subject: [PATCH 53/69] Register generate-certificates-and-rotate command --- src/main/java/com/nike/cerberus/cli/CerberusRunner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 58b5df57..4c47c1aa 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -32,6 +32,7 @@ import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; +import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.composite.RotateCertificatesCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; @@ -195,6 +196,7 @@ private void registerAllCommands() { registerCommand(new RotateCertificatesCommand()); registerCommand(new DeleteOldestCertificatesCommand()); registerCommand(new CopyRdsSnapshotsCommand()); + registerCommand(new GenerateAndRotateCertificatesCommand()); } /** From ba862d8804224ac803a7164bc5bfee1317431dfe Mon Sep 17 00:00:00 2001 From: Justin Field Date: Fri, 15 Dec 2017 11:28:45 -0800 Subject: [PATCH 54/69] Added logic to save the acme account key when creating an account to generate certs, added command to rotate said key. Fixed bugs related to running generate and rotate certs command without yaml and made --no-tty generic and not specific to the generate command (#112) --- .../com/nike/cerberus/ConfigConstants.java | 2 + .../com/nike/cerberus/cli/CerberusRunner.java | 10 +- .../cli/EnvironmentConfigToArgsMapper.java | 13 +- .../cerberus/command/CerberusCommand.java | 7 + .../DeleteOldestCertificatesCommand.java | 15 ++- ...CertificatesCommandParametersDelegate.java | 49 +++++++ .../GenerateAndRotateCertificatesCommand.java | 6 +- .../RotateAcmeAccountPrivateKeyCommand.java | 59 +++++++++ .../RotateCertificatesCommand.java | 23 ++-- .../UploadCertificateFilesCommand.java | 6 +- ...ificateFilesCommandParametersDelegate.java | 2 +- ...ificateFilesCommandParametersDelegate.java | 12 -- .../nike/cerberus/module/CerberusModule.java | 5 + .../DeleteOldestCertificatesOperation.java | 15 ++- ...enerateAndRotateCertificatesOperation.java | 88 ++++++++----- .../RotateAcmeAccountPrivateKeyOperation.java | 60 +++++++++ .../RotateCertificatesOperation.java | 37 +++++- .../UploadCertificatesFilesOperation.java | 4 +- .../composite/CreateEnvironmentOperation.java | 2 +- .../GenerateCertificateFilesOperation.java | 11 +- .../cerberus/service/CertificateService.java | 120 ++++++++++++++++-- .../com/nike/cerberus/store/ConfigStore.java | 34 +++++ .../EnvironmentConfigToArgsMapperTest.java | 4 +- 23 files changed, 482 insertions(+), 102 deletions(-) rename src/main/java/com/nike/cerberus/command/{core => certificates}/DeleteOldestCertificatesCommand.java (68%) create mode 100644 src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommandParametersDelegate.java rename src/main/java/com/nike/cerberus/command/{composite => certificates}/GenerateAndRotateCertificatesCommand.java (88%) create mode 100644 src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java rename src/main/java/com/nike/cerberus/command/{composite => certificates}/RotateCertificatesCommand.java (68%) rename src/main/java/com/nike/cerberus/command/{core => certificates}/UploadCertificateFilesCommand.java (88%) rename src/main/java/com/nike/cerberus/command/{core => certificates}/UploadCertificateFilesCommandParametersDelegate.java (96%) rename src/main/java/com/nike/cerberus/operation/{core => certificates}/DeleteOldestCertificatesOperation.java (79%) rename src/main/java/com/nike/cerberus/operation/{composite => certificates}/GenerateAndRotateCertificatesOperation.java (60%) create mode 100644 src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java rename src/main/java/com/nike/cerberus/operation/{composite => certificates}/RotateCertificatesOperation.java (69%) rename src/main/java/com/nike/cerberus/operation/{core => certificates}/UploadCertificatesFilesOperation.java (94%) diff --git a/src/main/java/com/nike/cerberus/ConfigConstants.java b/src/main/java/com/nike/cerberus/ConfigConstants.java index 5cfc86cf..7bf8741c 100644 --- a/src/main/java/com/nike/cerberus/ConfigConstants.java +++ b/src/main/java/com/nike/cerberus/ConfigConstants.java @@ -42,6 +42,8 @@ public class ConfigConstants { public static final String CERT_PART_PUBKEY = "pubkey.pem"; + public static final String CERT_ACME_ACCOUNT_PRIVATE_KEY = "certificates/acme/account-private-key-pkcs1.pem"; + public static final String CMS_ENV_CONFIG_PATH = "cms/environment.properties"; public static final String VERSION_PROPERTY = "cli.version"; diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 4c47c1aa..896c25ca 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -27,14 +27,15 @@ import com.nike.cerberus.ConfigConstants; import com.nike.cerberus.command.CerberusCommand; import com.nike.cerberus.command.Command; +import com.nike.cerberus.command.certificates.RotateAcmeAccountPrivateKeyCommand; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; import com.nike.cerberus.command.composite.CreateEnvironmentCommand; import com.nike.cerberus.command.composite.DeleteEnvironmentCommand; -import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; +import com.nike.cerberus.command.certificates.GenerateAndRotateCertificatesCommand; import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; -import com.nike.cerberus.command.composite.RotateCertificatesCommand; +import com.nike.cerberus.command.certificates.RotateCertificatesCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; import com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand; import com.nike.cerberus.command.rds.CreateDatabaseCommand; @@ -44,14 +45,14 @@ import com.nike.cerberus.command.core.CreateSecurityGroupsCommand; import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; -import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; +import com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommand; import com.nike.cerberus.command.core.DeleteStackCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.PrintStackInfoCommand; import com.nike.cerberus.command.core.RestoreCerberusBackupCommand; import com.nike.cerberus.command.core.RebootCmsCommand; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.ViewConfigCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; @@ -197,6 +198,7 @@ private void registerAllCommands() { registerCommand(new DeleteOldestCertificatesCommand()); registerCommand(new CopyRdsSnapshotsCommand()); registerCommand(new GenerateAndRotateCertificatesCommand()); + registerCommand(new RotateAcmeAccountPrivateKeyCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index 96eae6a2..c31565ff 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -16,12 +16,13 @@ package com.nike.cerberus.cli; +import com.google.common.collect.Lists; import com.nike.cerberus.command.StackDelegate; import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; -import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; -import com.nike.cerberus.command.composite.RotateCertificatesCommand; +import com.nike.cerberus.command.certificates.GenerateAndRotateCertificatesCommand; +import com.nike.cerberus.command.certificates.RotateCertificatesCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; import com.nike.cerberus.command.rds.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; @@ -32,8 +33,8 @@ import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; -import com.nike.cerberus.command.core.UploadCertificateFilesCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommand; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.cloudformation.TagParametersDelegate; import com.nike.cerberus.domain.input.EnvironmentConfig; @@ -86,6 +87,10 @@ public static String[] getArgs(EnvironmentConfig environmentConfig, String[] pas } public static List getArgsForCommand(EnvironmentConfig environmentConfig, String commandName, String[] passedArgs) { + if (environmentConfig == null) { + return Lists.newArrayList(passedArgs); + } + List args = new LinkedList<>(); switch (commandName) { case InitializeEnvironmentCommand.COMMAND_NAME: diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index bc5d3e6d..8b1adddb 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -84,6 +84,9 @@ public class CerberusCommand { @Parameter(names = {"--version", "-v"}, description = "Prints the version of the CLI.") private boolean version; + @Parameter(names = {"--no-tty"}, description = "Flag to set when no tty is availible, ex: running on a Continuous Integration (CI) server") + boolean noTty = false; + @ParametersDelegate private ProxyDelegate proxyDelegate = new ProxyDelegate(); @@ -174,6 +177,10 @@ public boolean isVersion() { return version; } + public boolean isTty() { + return ! noTty; + } + public ProxyDelegate getProxyDelegate() { return proxyDelegate; } diff --git a/src/main/java/com/nike/cerberus/command/core/DeleteOldestCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommand.java similarity index 68% rename from src/main/java/com/nike/cerberus/command/core/DeleteOldestCertificatesCommand.java rename to src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommand.java index 96867cf3..5c6cd505 100644 --- a/src/main/java/com/nike/cerberus/command/core/DeleteOldestCertificatesCommand.java +++ b/src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommand.java @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.nike.cerberus.command.core; +package com.nike.cerberus.command.certificates; import com.beust.jcommander.Parameters; +import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.DeleteOldestCertificatesOperation; +import com.nike.cerberus.operation.certificates.DeleteOldestCertificatesOperation; -import static com.nike.cerberus.command.core.DeleteOldestCertificatesCommand.COMMAND_NAME; +import static com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommand.COMMAND_NAME; @Parameters( commandNames = { @@ -33,6 +34,14 @@ public class DeleteOldestCertificatesCommand implements Command { public static final String COMMAND_NAME = "delete-oldest-certificates"; + @ParametersDelegate + DeleteOldestCertificatesCommandParametersDelegate deleteParametersDelegate = + new DeleteOldestCertificatesCommandParametersDelegate(); + + public DeleteOldestCertificatesCommandParametersDelegate getDeleteParametersDelegate() { + return deleteParametersDelegate; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommandParametersDelegate.java b/src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommandParametersDelegate.java new file mode 100644 index 00000000..d9a0643b --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/certificates/DeleteOldestCertificatesCommandParametersDelegate.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 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.certificates; + +import com.beust.jcommander.Parameter; +import com.nike.cerberus.command.validator.UploadCertFilesPathValidator; + +import java.nio.file.Path; + +public class DeleteOldestCertificatesCommandParametersDelegate { + + public static final String REVOKE_LONG_ARG = "--revoke-certificates"; + + public static final String ACME_API_LONG_ARG = "--acme-api-url"; + + @Parameter( + names = REVOKE_LONG_ARG, + description = "Provide this flag and an acme-url to revoke the certificates when deleting the old certs" + ) + private boolean revokeCertificates; + + public boolean isRevokeCertificates() { + return revokeCertificates; + } + + @Parameter( + names = ACME_API_LONG_ARG, + description = "The ACME API URL to use when attempting to revoke the certificates" + ) + private String acmeUrl; + + public String getAcmeUrl() { + return acmeUrl; + } +} \ No newline at end of file diff --git a/src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/certificates/GenerateAndRotateCertificatesCommand.java similarity index 88% rename from src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java rename to src/main/java/com/nike/cerberus/command/certificates/GenerateAndRotateCertificatesCommand.java index 642fd72b..a7f0463f 100644 --- a/src/main/java/com/nike/cerberus/command/composite/GenerateAndRotateCertificatesCommand.java +++ b/src/main/java/com/nike/cerberus/command/certificates/GenerateAndRotateCertificatesCommand.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.nike.cerberus.command.composite; +package com.nike.cerberus.command.certificates; import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.composite.GenerateAndRotateCertificatesOperation; +import com.nike.cerberus.operation.certificates.GenerateAndRotateCertificatesOperation; -import static com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand.COMMAND_NAME; +import static com.nike.cerberus.command.certificates.GenerateAndRotateCertificatesCommand.COMMAND_NAME; @Parameters( commandNames = { diff --git a/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java b/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java new file mode 100644 index 00000000..eed4c2d7 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 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.certificates; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.certificates.RotateAcmeAccountPrivateKeyOperation; + +import static com.nike.cerberus.command.certificates.RotateAcmeAccountPrivateKeyCommand.COMMAND_NAME; + +@Parameters( + commandNames = { + COMMAND_NAME + }, + commandDescription = "Rotates the ACME Account private key." +) +public class RotateAcmeAccountPrivateKeyCommand implements Command { + + public static final String COMMAND_NAME = "rotate-acme-account-key"; + + public static final String ACME_URL_LONG_ARG = "--acme-url"; + + @Parameter( + names = ACME_URL_LONG_ARG, + description = "The ACME API URL to use.", + required = true + ) + private String acmeUrl; + + public String getAcmeUrl() { + return acmeUrl; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return RotateAcmeAccountPrivateKeyOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java b/src/main/java/com/nike/cerberus/command/certificates/RotateCertificatesCommand.java similarity index 68% rename from src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java rename to src/main/java/com/nike/cerberus/command/certificates/RotateCertificatesCommand.java index 7f56e381..69ceaf78 100644 --- a/src/main/java/com/nike/cerberus/command/composite/RotateCertificatesCommand.java +++ b/src/main/java/com/nike/cerberus/command/certificates/RotateCertificatesCommand.java @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.nike.cerberus.command.composite; +package com.nike.cerberus.command.certificates; import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; -import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.composite.RotateCertificatesOperation; +import com.nike.cerberus.operation.certificates.RotateCertificatesOperation; -import static com.nike.cerberus.command.composite.RotateCertificatesCommand.COMMAND_DESCRIPTION; -import static com.nike.cerberus.command.composite.RotateCertificatesCommand.COMMAND_NAME; +import static com.nike.cerberus.command.certificates.RotateCertificatesCommand.COMMAND_DESCRIPTION; +import static com.nike.cerberus.command.certificates.RotateCertificatesCommand.COMMAND_NAME; @Parameters( commandNames = COMMAND_NAME, @@ -36,11 +35,19 @@ public class RotateCertificatesCommand implements Command { public static final String COMMAND_DESCRIPTION = "Rotates the certificates used by the ALB and CMS."; @ParametersDelegate - private UploadCertificateFilesCommandParametersDelegate uploadCertificateFilesCommandParametersDelegate + private UploadCertificateFilesCommandParametersDelegate uploadParametersDelegate = new UploadCertificateFilesCommandParametersDelegate(); - public UploadCertificateFilesCommandParametersDelegate getUploadCertificateFilesCommandParametersDelegate() { - return uploadCertificateFilesCommandParametersDelegate; + public UploadCertificateFilesCommandParametersDelegate getUploadParametersDelegate() { + return uploadParametersDelegate; + } + + @ParametersDelegate + DeleteOldestCertificatesCommandParametersDelegate deleteParametersDelegate = + new DeleteOldestCertificatesCommandParametersDelegate(); + + public DeleteOldestCertificatesCommandParametersDelegate getDeleteParametersDelegate() { + return deleteParametersDelegate; } @Override diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java b/src/main/java/com/nike/cerberus/command/certificates/UploadCertificateFilesCommand.java similarity index 88% rename from src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java rename to src/main/java/com/nike/cerberus/command/certificates/UploadCertificateFilesCommand.java index 91f6f26d..32eab223 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommand.java +++ b/src/main/java/com/nike/cerberus/command/certificates/UploadCertificateFilesCommand.java @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.nike.cerberus.command.core; +package com.nike.cerberus.command.certificates; import com.beust.jcommander.Parameters; import com.beust.jcommander.ParametersDelegate; import com.nike.cerberus.command.Command; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.operation.core.UploadCertificatesFilesOperation; +import com.nike.cerberus.operation.certificates.UploadCertificatesFilesOperation; -import static com.nike.cerberus.command.core.UploadCertificateFilesCommand.COMMAND_NAME; +import static com.nike.cerberus.command.certificates.UploadCertificateFilesCommand.COMMAND_NAME; /** * Command for uploading a certificate for a specific component. diff --git a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java b/src/main/java/com/nike/cerberus/command/certificates/UploadCertificateFilesCommandParametersDelegate.java similarity index 96% rename from src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java rename to src/main/java/com/nike/cerberus/command/certificates/UploadCertificateFilesCommandParametersDelegate.java index 4e1e92bc..a7ae4c94 100644 --- a/src/main/java/com/nike/cerberus/command/core/UploadCertificateFilesCommandParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/command/certificates/UploadCertificateFilesCommandParametersDelegate.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.nike.cerberus.command.core; +package com.nike.cerberus.command.certificates; import com.beust.jcommander.Parameter; import com.nike.cerberus.command.validator.UploadCertFilesPathValidator; diff --git a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java index ff396601..3de3a958 100644 --- a/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java +++ b/src/main/java/com/nike/cerberus/command/core/GenerateCertificateFilesCommandParametersDelegate.java @@ -33,7 +33,6 @@ public class GenerateCertificateFilesCommandParametersDelegate { public static final String CERT_FOLDER_LONG_ARG = "--local-certificate-directory"; public static final String ACME_API_LONG_ARG = "--acme-api-url"; public static final String CONTACT_EMAIL_LONG_ARG = "--contact-email"; - public static final String NO_TTY_LONG_ARG = "--no-tty"; public static final String ACCEPT_ACME_TOS = "--no-tty-force-acme-tos-accept"; public static final String LETS_ENCRYPT_ACME_API_URI = "acme://letsencrypt.org"; @@ -132,13 +131,6 @@ public class GenerateCertificateFilesCommandParametersDelegate { ) private String contactEmail; - @Parameter( - names = { - NO_TTY_LONG_ARG - }, - description = "Flag to supply when running command in an environment without TTY, such as a build job" - ) - private boolean isNoTty = false; @Parameter( names = { @@ -189,10 +181,6 @@ public String getContactEmail() { return contactEmail; } - public boolean isTty() { - return ! isNoTty; - } - public boolean isAutoAcceptAcmeTos() { return autoAcceptAcmeTos; } diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index a86719b8..51ec5343 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -69,6 +69,7 @@ public class CerberusModule extends AbstractModule { public static final String CONFIG_OBJECT_MAPPER = "configObjectMapper"; public static final String ENV_NAME = "environmentName"; public static final String CONFIG_REGION = "configRegionName"; + public static final String IS_TTY = "isTty"; private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -80,11 +81,14 @@ public class CerberusModule extends AbstractModule { private final EnvironmentConfig environmentConfig; + private final boolean isTty; + public CerberusModule(CerberusCommand cerberusCommand) { proxyDelegate = cerberusCommand.getProxyDelegate(); environmentName = cerberusCommand.getEnvironmentName(); configRegionName = cerberusCommand.getConfigRegion(); environmentConfig = cerberusCommand.getEnvironmentConfig(); + isTty = cerberusCommand.isTty(); } /** @@ -96,6 +100,7 @@ protected void configure() { OptionalBinder.newOptionalBinder(binder(), EnvironmentConfig.class); bind(EnvironmentConfig.class).toProvider(Providers.of(environmentConfig)); + bindConstant().annotatedWith(Names.named(IS_TTY)).to(isTty); bindConstant().annotatedWith(Names.named(ENV_NAME)).to(environmentName); bindConstant().annotatedWith(Names.named(CONFIG_REGION)).to(configRegionName); diff --git a/src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/certificates/DeleteOldestCertificatesOperation.java similarity index 79% rename from src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java rename to src/main/java/com/nike/cerberus/operation/certificates/DeleteOldestCertificatesOperation.java index 114480a6..5ee5dee0 100644 --- a/src/main/java/com/nike/cerberus/operation/core/DeleteOldestCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/certificates/DeleteOldestCertificatesOperation.java @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.nike.cerberus.operation.core; +package com.nike.cerberus.operation.certificates; import com.google.inject.Inject; -import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; +import com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommand; import com.nike.cerberus.domain.environment.CertificateInformation; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CertificateService; import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +53,9 @@ public void run(DeleteOldestCertificatesCommand command) { List certificateInformationList = configStore.getCertificationInformationList(); int indexBeforeLast = certificateInformationList.size() - 1; certificateInformationList.subList(0, indexBeforeLast).forEach(certificateInformation -> { - certificateService.deleteCertificate(certificateInformation.getCertificateName()); + certificateService.deleteCertificate(certificateInformation.getCertificateName(), + command.getDeleteParametersDelegate().isRevokeCertificates(), + command.getDeleteParametersDelegate().getAcmeUrl()); }); } @@ -68,6 +71,12 @@ public boolean isRunnable(DeleteOldestCertificatesCommand command) { isRunnable = false; } + if (command.getDeleteParametersDelegate().isRevokeCertificates() && + StringUtils.isBlank(command.getDeleteParametersDelegate().getAcmeUrl())) { + log.error("You must provide an ACME api url if you wish to revoke the cert"); + isRunnable = false; + } + return isRunnable; } } diff --git a/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/certificates/GenerateAndRotateCertificatesOperation.java similarity index 60% rename from src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java rename to src/main/java/com/nike/cerberus/operation/certificates/GenerateAndRotateCertificatesOperation.java index 509e7ee6..d76fe2e3 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/GenerateAndRotateCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/certificates/GenerateAndRotateCertificatesOperation.java @@ -14,22 +14,27 @@ * limitations under the License. */ -package com.nike.cerberus.operation.composite; +package com.nike.cerberus.operation.certificates; import com.google.common.collect.Lists; -import com.nike.cerberus.command.composite.GenerateAndRotateCertificatesCommand; -import com.nike.cerberus.command.composite.RotateCertificatesCommand; +import com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommandParametersDelegate; +import com.nike.cerberus.command.certificates.GenerateAndRotateCertificatesCommand; +import com.nike.cerberus.command.certificates.RotateCertificatesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate; import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.composite.ChainableCommand; +import com.nike.cerberus.operation.composite.CompositeOperation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; import javax.inject.Named; import java.util.List; -import static com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG; +import static com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommandParametersDelegate.REVOKE_LONG_ARG; +import static com.nike.cerberus.command.certificates.UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG; import static com.nike.cerberus.command.core.GenerateCertificateFilesCommandParametersDelegate.*; import static com.nike.cerberus.module.CerberusModule.ENV_NAME; @@ -57,30 +62,10 @@ protected List getCompositeCommandChain(GenerateAndRotateCerti ChainableCommand.Builder generateCertificateFilesCommandBuilder = ChainableCommand.Builder.create() .withCommand(new GenerateCertificateFilesCommand()) - .withOption( - BASE_DOMAIN_LONG_ARG, - parameters.getBaseDomainName() - ) - .withOption( - EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, - parameters.getEdgeDomainNameOverride() - ) - .withOption( - ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, - parameters.getOriginDomainNameOverride() - ) - .withOption( - LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, - parameters.getLoadBalancerDomainNameOverride() - ) .withOption( HOSTED_ZONE_ID_LONG_ARG, parameters.getHostedZoneId() ) - .withOption( - ENABLE_LE_CERTFIX_LONG_ARG, - String.valueOf(parameters.enableLetsEncryptCertfix()) - ) .withOption( CERT_FOLDER_LONG_ARG, parameters.getCertDir() @@ -94,17 +79,45 @@ protected List getCompositeCommandChain(GenerateAndRotateCerti parameters.getContactEmail() ); - if (!parameters.isTty()) { - generateCertificateFilesCommandBuilder.withAdditionalArg(NO_TTY_LONG_ARG); - } + if (parameters.enableLetsEncryptCertfix()) { + generateCertificateFilesCommandBuilder.withAdditionalArg(ENABLE_LE_CERTFIX_LONG_ARG); + } + + if (StringUtils.isNotBlank(parameters.getBaseDomainName())) { + generateCertificateFilesCommandBuilder.withOption( + BASE_DOMAIN_LONG_ARG, + parameters.getBaseDomainName() + ); + } + + if (StringUtils.isNotBlank(parameters.getEdgeDomainNameOverride())) { + generateCertificateFilesCommandBuilder.withOption( + EDGE_DOMAIN_NAME_OVERRIDE_LONG_ARG, + parameters.getEdgeDomainNameOverride() + ); + } + + if (StringUtils.isNotBlank(parameters.getOriginDomainNameOverride())) { + generateCertificateFilesCommandBuilder.withOption( + ORIGIN_DOMAIN_NAME_OVERRIDE_LONG_ARG, + parameters.getOriginDomainNameOverride() + ); + } + + if (StringUtils.isNotBlank(parameters.getLoadBalancerDomainNameOverride())) { + generateCertificateFilesCommandBuilder.withOption( + LOAD_BALANCER_DOMAIN_NAME_OVERRIDE_LONG_ARG, + parameters.getLoadBalancerDomainNameOverride() + ); + } - if (parameters.isAutoAcceptAcmeTos()) { - generateCertificateFilesCommandBuilder.withAdditionalArg(ACCEPT_ACME_TOS); - } + if (parameters.isAutoAcceptAcmeTos()) { + generateCertificateFilesCommandBuilder.withAdditionalArg(ACCEPT_ACME_TOS); + } - parameters.getSubjectAlternativeNames().forEach(name -> { - generateCertificateFilesCommandBuilder.withOption(SUBJECT_ALT_NAME_LONG_ARG, name); - }); + parameters.getSubjectAlternativeNames().forEach(name -> { + generateCertificateFilesCommandBuilder.withOption(SUBJECT_ALT_NAME_LONG_ARG, name); + }); return Lists.newArrayList( // Generate the Certificates @@ -115,6 +128,10 @@ protected List getCompositeCommandChain(GenerateAndRotateCerti .withOption( CERT_PATH_LONG_ARG, parameters.getCertDir()) + .withAdditionalArg(REVOKE_LONG_ARG) + .withOption( + DeleteOldestCertificatesCommandParametersDelegate.ACME_API_LONG_ARG, + parameters.getAcmeApiUrl()) .build() ); } @@ -135,4 +152,9 @@ public boolean isRunnable(GenerateAndRotateCertificatesCommand command) { return isRunnable; } + + @Override + public boolean isEnvironmentConfigRequired() { + return false; + } } diff --git a/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java b/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java new file mode 100644 index 00000000..9fdec741 --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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.certificates; + +import com.nike.cerberus.command.certificates.RotateAcmeAccountPrivateKeyCommand; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CertificateService; +import com.nike.cerberus.store.ConfigStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + +public class RotateAcmeAccountPrivateKeyOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ConfigStore configStore; + private final CertificateService certificateService; + + @Inject + public RotateAcmeAccountPrivateKeyOperation(ConfigStore configStore, + CertificateService certificateService) { + this.configStore = configStore; + this.certificateService = certificateService; + } + + @Override + public void run(RotateAcmeAccountPrivateKeyCommand command) { + log.info("Preparing to rotate ACME account private key."); + certificateService.rotateAcmeAccountKeyPair(command.getAcmeUrl()); + log.info("ACME account private key rotated."); + } + + @Override + public boolean isRunnable(RotateAcmeAccountPrivateKeyCommand command) { + boolean isRunnable = true; + + if (!configStore.getAcmeAccountKeyPair().isPresent()) { + log.error("There is no saved private key to rotate"); + isRunnable = false; + } + + return isRunnable; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java b/src/main/java/com/nike/cerberus/operation/certificates/RotateCertificatesOperation.java similarity index 69% rename from src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java rename to src/main/java/com/nike/cerberus/operation/certificates/RotateCertificatesOperation.java index 70b7418c..fa0c4090 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/RotateCertificatesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/certificates/RotateCertificatesOperation.java @@ -14,24 +14,29 @@ * limitations under the License. */ -package com.nike.cerberus.operation.composite; +package com.nike.cerberus.operation.certificates; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.nike.cerberus.command.cms.UpdateCmsConfigCommand; -import com.nike.cerberus.command.composite.RotateCertificatesCommand; -import com.nike.cerberus.command.core.DeleteOldestCertificatesCommand; +import com.nike.cerberus.command.certificates.RotateCertificatesCommand; +import com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommand; import com.nike.cerberus.command.core.RebootCmsCommand; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommand; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.composite.ChainableCommand; +import com.nike.cerberus.operation.composite.CompositeOperation; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.store.ConfigStore; +import org.apache.commons.lang3.StringUtils; import javax.inject.Named; import java.util.List; +import static com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommandParametersDelegate.ACME_API_LONG_ARG; +import static com.nike.cerberus.command.certificates.DeleteOldestCertificatesCommandParametersDelegate.REVOKE_LONG_ARG; import static com.nike.cerberus.module.CerberusModule.ENV_NAME; /** @@ -55,11 +60,18 @@ public RotateCertificatesOperation(CloudFormationService cloudFormationService, @Override protected List getCompositeCommandChain(RotateCertificatesCommand compositeCommand) { + ChainableCommand.Builder delete = ChainableCommand.Builder.create().withCommand(new DeleteOldestCertificatesCommand()); + if (compositeCommand.getDeleteParametersDelegate().isRevokeCertificates()) { + delete + .withAdditionalArg(REVOKE_LONG_ARG) + .withOption(ACME_API_LONG_ARG, compositeCommand.getDeleteParametersDelegate().getAcmeUrl()); + } + return Lists.newArrayList( // Add the cert and key files to S3 ChainableCommand.Builder.create().withCommand(new UploadCertificateFilesCommand()) .withAdditionalArg(UploadCertificateFilesCommandParametersDelegate.CERT_PATH_LONG_ARG) - .withAdditionalArg(compositeCommand.getUploadCertificateFilesCommandParametersDelegate() + .withAdditionalArg(compositeCommand.getUploadParametersDelegate() .getCertPath().toString()) .build(), @@ -76,7 +88,7 @@ protected List getCompositeCommandChain(RotateCertificatesComm ChainableCommand.Builder.create().withCommand(new RebootCmsCommand()).build(), // Delete all certs except the latest (there should just be the one) - ChainableCommand.Builder.create().withCommand(new DeleteOldestCertificatesCommand()).build() + delete.build() ); } @@ -94,6 +106,17 @@ public boolean isRunnable(RotateCertificatesCommand command) { isRunnable = false; } + if (command.getDeleteParametersDelegate().isRevokeCertificates() && + StringUtils.isBlank(command.getDeleteParametersDelegate().getAcmeUrl())) { + log.error("You must provide an ACME api url if you wish to revoke the cert while rotating"); + isRunnable = false; + } + return isRunnable; } + + @Override + public boolean isEnvironmentConfigRequired() { + return false; + } } diff --git a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java b/src/main/java/com/nike/cerberus/operation/certificates/UploadCertificatesFilesOperation.java similarity index 94% rename from src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java rename to src/main/java/com/nike/cerberus/operation/certificates/UploadCertificatesFilesOperation.java index 9894472b..ccd2c4db 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UploadCertificatesFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/certificates/UploadCertificatesFilesOperation.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.nike.cerberus.operation.core; +package com.nike.cerberus.operation.certificates; -import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommand; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.CertificateService; import com.nike.cerberus.store.ConfigStore; diff --git a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java index 75ad4138..629cef3b 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/CreateEnvironmentOperation.java @@ -29,7 +29,7 @@ import com.nike.cerberus.command.core.CreateVpcCommand; import com.nike.cerberus.command.core.CreateWafCommand; import com.nike.cerberus.command.core.GenerateCertificateFilesCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommand; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommand; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import java.util.List; diff --git a/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java index 6b02bfa4..2dc66462 100644 --- a/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/GenerateCertificateFilesOperation.java @@ -33,10 +33,10 @@ import javax.inject.Named; import java.io.File; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import static com.nike.cerberus.module.CerberusModule.ENV_NAME; +import static com.nike.cerberus.module.CerberusModule.IS_TTY; import static com.nike.cerberus.service.ConsoleService.DefaultAction.NO; import static com.nike.cerberus.service.ConsoleService.DefaultAction.YES; @@ -51,17 +51,20 @@ public class GenerateCertificateFilesOperation implements Operation * TODO: Update TOS * TODO: Rotate ACME user private key @@ -109,9 +112,6 @@ public class CertificateService { private final Logger log = LoggerFactory.getLogger(getClass()); - // File name of the PKCS #1 private key pem for the ACME registration, this represents a "User" - public static final String USER_KEY_FILE = "acme-user-private-key-pkcs1.pem"; - // File name of the PKCS #1 private key pem for the Cerberus domain public static final String DOMAIN_PKCS1_KEY_FILE = "domain-private-key-pkcs1.pem"; @@ -181,10 +181,18 @@ public CertificateService(ConsoleService console, * it somewhere. If you need to get access to your account later, reconnect to it via * {@link Registration#bind(Session, URI)} by using the stored location. * - * @param session {@link Session} to bind with + * @param acmeServerUrl The ACME API URL + * @param contactEmail The contact info for the account + * @param autoAcceptTos whether or not to auto accept the TOS from the ACME provider * @return {@link Registration} connected to your account */ - protected Registration findOrRegisterAccount(Session session, String contactEmail, boolean autoAcceptTos) throws AcmeException, IOException { + protected Registration findOrRegisterAccount(String acmeServerUrl, + String contactEmail, + boolean autoAcceptTos) throws AcmeException, IOException { + + KeyPair userKeyPair = loadOrCreateAcmeAccountKeyPair(); + Session session = new Session(acmeServerUrl, userKeyPair); + Registration reg; try { // Try to create a new Registration. @@ -293,6 +301,91 @@ protected KeyPair loadOrCreateKeyPair(File file) throws IOException { } } + /** + * Loads a key pair from specified file. If the file does not exist, + * a new key pair is generated and saved. + * + * @return {@link KeyPair}. + */ + protected KeyPair loadOrCreateAcmeAccountKeyPair() { + return configStore.getAcmeAccountKeyPair().orElseGet(() -> { + KeyPair keyPair = KeyPairUtils.createKeyPair(KEY_SIZE); + configStore.storeAcmeUserKeyPair(keyPair); + return keyPair; + }); + } + + /** + * Retrieves an account that should already exist for the private key stored in the config store. + * @param acmeServerUrl The ACME server url. + * @return The registration object for the already registered account + */ + protected Registration getExistingAccount(String acmeServerUrl) { + KeyPair currentKeyPair = configStore.getAcmeAccountKeyPair().orElseThrow(() -> + new RuntimeException("There is no current ACME account key pair to rotate")); + + Session session = new Session(acmeServerUrl, currentKeyPair); + + Registration account = null; + try { + // Try to create a new Registration. + new RegistrationBuilder().create(session); + throw new RuntimeException("An account for the specified ACME user account private key does not exist"); + } catch (AcmeConflictException ex) { + account = Registration.bind(session, ex.getLocation()); + } catch (AcmeException e) { + log.error("Failed to lookup account", e); + } + + return account; + } + + /** + * Rotates the ACME account private key. + * @param acmeServerUrl The ACME server url. + */ + public void rotateAcmeAccountKeyPair(String acmeServerUrl) { + Registration account = getExistingAccount(acmeServerUrl); + + KeyPair newKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE); + try { + account.changeKey(newKeyPair); + } catch (AcmeException e) { + throw new RuntimeException("Failed to rotate keypair on ACME account", e); + } + configStore.storeAcmeUserKeyPair(newKeyPair); + } + + /** + * Revokes a certificate from the ACME Provider. + * @param certificateName The certificate name of the cert, that was saved when the cert was generated + * @param acmeServerUrl The ACME server url. + */ + protected void revokeCertificate(String certificateName, String acmeServerUrl) { + String certificateContents = configStore.getCertPart(certificateName, CERT_PART_CERT).orElseThrow(() -> + new RuntimeException("Failed to download the requested cert")); + + X509Certificate certificateToRevoke; + try { + certificateToRevoke = CertificateUtils.readX509Certificate(new StringInputStream(certificateContents)); + } catch (IOException e) { + throw new RuntimeException("Failed to parse x509 cert", e); + } + + Registration account = getExistingAccount(acmeServerUrl); + try { + for (Iterator it = account.getCertificates(); it.hasNext(); ) { + Certificate certificate = it.next(); + + if (certificate.download().equals(certificateToRevoke)) { + certificate.revoke(RevocationReason.UNSPECIFIED); + break; + } + } + } catch (AcmeException e) { + throw new RuntimeException("Failed to revoke certificate", e); + } + } /** * Generates the certs needed for a Cerberus Environment @@ -315,10 +408,7 @@ public void generateCerts(File certDir, names.add(commonName); // the first entry counts as the common name names.addAll(subjectAlternativeNames); - KeyPair userKeyPair = loadOrCreateKeyPair(new File(certDir.getAbsolutePath() + File.separator + USER_KEY_FILE)); - - Session session = new Session(acmeServerUrl, userKeyPair); - Registration registration = findOrRegisterAccount(session, contactEmail, autoAcceptTos); + Registration registration = findOrRegisterAccount(acmeServerUrl, contactEmail, autoAcceptTos); Map> domainChallengeCollectionMap = new HashMap<>(); for (String name : names) { @@ -612,9 +702,11 @@ private String getPath() { /** * Deletes a certificate by name from S3 and from the identity management service and from the environment config * - * @param certificateName the certificate to delete + * @param certificateName The certificate to delete + * @param revokeCertificates Flag to revoke the certificate with an ACME server + * @param acmeServerUrl The ACME server to use when revoking a certificate */ - public void deleteCertificate(String certificateName) { + public void deleteCertificate(String certificateName, boolean revokeCertificates, String acmeServerUrl) { log.info("Attempting to delete certificate with name: {}", certificateName); try { identityManagementService.deleteServerCertificate(certificateName); @@ -623,6 +715,10 @@ public void deleteCertificate(String certificateName) { " you may need to manually delete. MSG: {}", certificateName, e.getMessage()); } + if (revokeCertificates) { + revokeCertificate(certificateName, acmeServerUrl); + } + configStore.deleteCertificate(certificateName); } } diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 8e7f6ea2..5f2034b8 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -50,6 +50,8 @@ import com.nike.cerberus.service.StoreService; import com.nike.cerberus.util.CloudFormationObjectMapper; import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.shredzone.acme4j.util.KeyPairUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +60,8 @@ import javax.inject.Singleton; import java.io.IOException; import java.io.StringReader; +import java.io.StringWriter; +import java.security.KeyPair; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -173,6 +177,32 @@ public void storeCert(CertificateInformation certificateInformation, saveEnvironmentData(environmentData); } + public Optional getAcmeAccountKeyPair() { + Optional serializedKeyPair = + getEncryptedObject(CERT_ACME_ACCOUNT_PRIVATE_KEY).map(encryptionService::decrypt); + + if (!serializedKeyPair.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(KeyPairUtils.readKeyPair(new StringReader(serializedKeyPair.get()))); + } catch (IOException e) { + throw new RuntimeException("Failed to read keypair from serialized data", e); + } + } + + public void storeAcmeUserKeyPair(KeyPair keyPair) { + EnvironmentData environmentData = getDecryptedEnvironmentData(); + StringWriter stringWriter = new StringWriter(); + try (JcaPEMWriter jw = new JcaPEMWriter(stringWriter)) { + jw.writeObject(keyPair); + } catch (IOException e) { + throw new RuntimeException("Failed to write key pair", e); + } + encryptAndSaveObject(CERT_ACME_ACCOUNT_PRIVATE_KEY, stringWriter.toString(), environmentData); + } + public LinkedList getCertificationInformationList() { return getDecryptedEnvironmentData().getCertificateData(); } @@ -548,6 +578,10 @@ private StoreService getStoreServiceForRegion(Regions region, EnvironmentData en return storeServiceMap.get(region); } + /** + * @param path the path to the encrypted text + * @return The serialized cipher text from s3 + */ private Optional getEncryptedObject(String path) { return getStoreServiceForRegion(configRegion, getDecryptedEnvironmentData()).get(path); } diff --git a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java index c1469df7..f0aa3b0c 100644 --- a/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java +++ b/src/test/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapperTest.java @@ -23,8 +23,8 @@ import com.nike.cerberus.command.cms.CreateCmsClusterCommand; import com.nike.cerberus.command.cms.CreateCmsConfigCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommand; -import com.nike.cerberus.command.core.UploadCertificateFilesCommandParametersDelegate; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommand; +import com.nike.cerberus.command.certificates.UploadCertificateFilesCommandParametersDelegate; import com.nike.cerberus.command.core.WhitelistCidrForVpcAccessCommand; import com.nike.cerberus.domain.input.EnvironmentConfig; import org.apache.commons.lang3.StringUtils; From d8ee439d1dba3bbd8a0d0dd264e72788849d0e5b Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 19 Dec 2017 15:25:10 -0800 Subject: [PATCH 55/69] update highlander version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a317a055..b23ed3fd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,4 @@ group=com.nike artifactId=cerberus-lifecycle-cli -version=3.3.1 +version=HIGHLANDER From bf3a3792e326bc682702fc2615c686990e00a9b0 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 19 Dec 2017 15:35:43 -0800 Subject: [PATCH 56/69] adjust message --- src/main/java/com/nike/cerberus/command/CerberusCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index 8b1adddb..03a42daf 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -158,8 +158,8 @@ public String getConfigRegion() { EnvironmentalVarRegion; if (StringUtils.isBlank(calculatedRegion)) { - log.error("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options, options must go before the command"); - calculatedRegion = Regions.DEFAULT_REGION.toString(); + log.error("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options as well as primary region config. Using the default region"); + calculatedRegion = Regions.DEFAULT_REGION.getName(); } return calculatedRegion; From 17d2e997da4ea970cd31612f176771040850aa8a Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 19 Dec 2017 15:43:36 -0800 Subject: [PATCH 57/69] fix descriptions for region and env name in base command --- src/main/java/com/nike/cerberus/command/CerberusCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index 03a42daf..d49ec9f4 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -135,7 +135,7 @@ public String getEnvironmentName() { EnvironmentalVarEnvironment; if (StringUtils.isBlank(calculatedEnv)) { - throw new RuntimeException("Failed to determine environment, checked 'CERBERUS_CLI_ENV' env var and -e, --env, --environment command options, Will attempt to use the AWS Default region (us-west-2"); + throw new RuntimeException("Failed to determine environment, checked 'CERBERUS_CLI_ENV' env var and -e, --env, --environment command options, options must go before the command"); } @@ -158,7 +158,7 @@ public String getConfigRegion() { EnvironmentalVarRegion; if (StringUtils.isBlank(calculatedRegion)) { - log.error("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options as well as primary region config. Using the default region"); + log.warn("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options as well as primary region config. Will attempt to use the AWS Default region (us-west-2)"); calculatedRegion = Regions.DEFAULT_REGION.getName(); } From f57fbbb20e7841f1793fa23f0beb352a0eb30525 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 20 Dec 2017 11:48:50 -0800 Subject: [PATCH 58/69] enable le encrypt hard coded cert for rotate acme key command --- .../RotateAcmeAccountPrivateKeyCommand.java | 18 +++++++++++++++++- .../RotateAcmeAccountPrivateKeyOperation.java | 11 +++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java b/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java index eed4c2d7..03978e3d 100644 --- a/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java +++ b/src/main/java/com/nike/cerberus/command/certificates/RotateAcmeAccountPrivateKeyCommand.java @@ -34,7 +34,8 @@ public class RotateAcmeAccountPrivateKeyCommand implements Command { public static final String COMMAND_NAME = "rotate-acme-account-key"; - public static final String ACME_URL_LONG_ARG = "--acme-url"; + public static final String ACME_URL_LONG_ARG = "--acme-api-url"; + public static final String ENABLE_LE_CERTFIX_LONG_ARG = "--enable-letsecrypt-certfix"; @Parameter( names = ACME_URL_LONG_ARG, @@ -47,6 +48,21 @@ public String getAcmeUrl() { return acmeUrl; } + @Parameter( + names = { + ENABLE_LE_CERTFIX_LONG_ARG + }, + description = "This command uses the acme4j client to communicate with the ACME server, " + + "it supports uses a hardcoded Let'sEncrypt cert to get around SSL errors, you can use this " + + "flag if your truststore is not configured to trust LE certificates, which can be found " + + " here: https://letsencrypt.org/certificates/" + ) + private boolean enableLetsEncryptCertfix = false; + + public boolean isEnableLetsEncryptCertfix() { + return enableLetsEncryptCertfix; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java b/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java index 9fdec741..9cf493e6 100644 --- a/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java +++ b/src/main/java/com/nike/cerberus/operation/certificates/RotateAcmeAccountPrivateKeyOperation.java @@ -42,6 +42,17 @@ public RotateAcmeAccountPrivateKeyOperation(ConfigStore configStore, @Override public void run(RotateAcmeAccountPrivateKeyCommand command) { log.info("Preparing to rotate ACME account private key."); + + + // Enable the use of the hard coded lets encrypt cert if enabled + if (command.isEnableLetsEncryptCertfix()) { + log.warn("Setting acme4j.le.certfix system property to 'true', this only works if you use the special " + + "acme:// lets encrypt addr. See: https://shredzone.org/maven/acme4j/usage/session.html" + + " and https://shredzone.org/maven/acme4j/provider.html"); + + System.setProperty("acme4j.le.certfix", "true"); + } + certificateService.rotateAcmeAccountKeyPair(command.getAcmeUrl()); log.info("ACME account private key rotated."); } From bf814e462cc4c6b88f970f2afe72406756f49568 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 10 Jan 2018 14:14:30 -0800 Subject: [PATCH 59/69] fix merge conflicts --- src/main/java/com/nike/cerberus/module/CerberusModule.java | 2 ++ .../nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/nike/cerberus/module/CerberusModule.java b/src/main/java/com/nike/cerberus/module/CerberusModule.java index 51ec5343..8ee35715 100644 --- a/src/main/java/com/nike/cerberus/module/CerberusModule.java +++ b/src/main/java/com/nike/cerberus/module/CerberusModule.java @@ -24,6 +24,7 @@ import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient; import com.amazonaws.services.kms.AWSKMSClient; import com.amazonaws.services.lambda.AWSLambdaClient; +import com.amazonaws.services.rds.AmazonRDSClient; import com.amazonaws.services.route53.AmazonRoute53Client; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; @@ -120,6 +121,7 @@ private void bindAwsClientFactories() { bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); + bind(new TypeLiteral>() {}).toInstance(new AwsClientFactory() {}); } /** diff --git a/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java b/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java index 4d3586c6..15f3778d 100644 --- a/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java @@ -178,7 +178,7 @@ protected List getDbSnapshots(Regions region) { public boolean isRunnable(CopyRdsSnapshotsCommand command) { boolean isRunnable = true; - if (cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + if (! cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.DATABASE.getFullName(environmentName))) { log.error("The Database stuck must exist in the primary region in order to duplicate snapshots"); isRunnable = false; From d280b85863a601d43abbf181eec9e9318943033c Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 10 Jan 2018 14:25:46 -0800 Subject: [PATCH 60/69] fix typo --- src/main/java/com/nike/cerberus/command/CerberusCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/nike/cerberus/command/CerberusCommand.java b/src/main/java/com/nike/cerberus/command/CerberusCommand.java index d49ec9f4..779c8fb5 100644 --- a/src/main/java/com/nike/cerberus/command/CerberusCommand.java +++ b/src/main/java/com/nike/cerberus/command/CerberusCommand.java @@ -158,7 +158,7 @@ public String getConfigRegion() { EnvironmentalVarRegion; if (StringUtils.isBlank(calculatedRegion)) { - log.warn("Failed to determine environment, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options as well as primary region config. Will attempt to use the AWS Default region (us-west-2)"); + log.warn("Failed to determine region, checked 'CERBERUS_CLI_REGION' env var and -r, --region command options as well as primary region config. Will attempt to use the AWS Default region (us-west-2)"); calculatedRegion = Regions.DEFAULT_REGION.getName(); } From 904c538028e85ad8cfe740dd5f996e59b33fc68a Mon Sep 17 00:00:00 2001 From: Justin Field Date: Thu, 11 Jan 2018 14:27:21 -0800 Subject: [PATCH 61/69] Add snapshot cleanup command --- .../com/nike/cerberus/cli/CerberusRunner.java | 2 + .../rds/CleanUpRdsSnapshotsCommand.java | 68 +++++++++ .../rds/CleanUpRdsSnapshotsOperation.java | 99 ++++++++++++ .../rds/CopyRdsSnapshotsOperation.java | 101 ++---------- .../com/nike/cerberus/service/RdsService.java | 144 ++++++++++++++++++ 5 files changed, 324 insertions(+), 90 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/command/rds/CleanUpRdsSnapshotsCommand.java create mode 100644 src/main/java/com/nike/cerberus/operation/rds/CleanUpRdsSnapshotsOperation.java create mode 100644 src/main/java/com/nike/cerberus/service/RdsService.java diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 896c25ca..036cd781 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -37,6 +37,7 @@ import com.nike.cerberus.command.composite.PrintAllStackInformationCommand; import com.nike.cerberus.command.certificates.RotateCertificatesCommand; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; +import com.nike.cerberus.command.rds.CleanUpRdsSnapshotsCommand; import com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand; import com.nike.cerberus.command.rds.CreateDatabaseCommand; import com.nike.cerberus.command.core.CreateEdgeDomainRecordCommand; @@ -199,6 +200,7 @@ private void registerAllCommands() { registerCommand(new CopyRdsSnapshotsCommand()); registerCommand(new GenerateAndRotateCertificatesCommand()); registerCommand(new RotateAcmeAccountPrivateKeyCommand()); + registerCommand(new CleanUpRdsSnapshotsCommand()); } /** diff --git a/src/main/java/com/nike/cerberus/command/rds/CleanUpRdsSnapshotsCommand.java b/src/main/java/com/nike/cerberus/command/rds/CleanUpRdsSnapshotsCommand.java new file mode 100644 index 00000000..8d2055f7 --- /dev/null +++ b/src/main/java/com/nike/cerberus/command/rds/CleanUpRdsSnapshotsCommand.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 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.rds; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.nike.cerberus.command.Command; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.operation.rds.CleanUpRdsSnapshotsOperation; + +import static com.nike.cerberus.command.rds.CleanUpRdsSnapshotsCommand.COMMAND_NAME; + +@Parameters( + commandNames = COMMAND_NAME, + commandDescription = "Copies RDS cluster snapshots in the primary for this environment to the secondary regions" +) +public class CleanUpRdsSnapshotsCommand implements Command { + + public static final String COMMAND_NAME = "cleanup-rds-snapshots"; + + public static final String DAYS_LONG_ARG = "--days"; + + public static final String DRY_LONG_ARG = "--dry"; + + @Parameter( + names = DAYS_LONG_ARG, + description = "How old cross region snapshot copies can be before they are deleted" + ) + private int days = 14; + + public int getDays() { + return days; + } + + @Parameter( + names = DRY_LONG_ARG, + description = "Add this flag to list the snapshots that would be deleted" + ) + boolean dryRun = false; + + public boolean isDryRun() { + return dryRun; + } + + @Override + public String getCommandName() { + return COMMAND_NAME; + } + + @Override + public Class> getOperationClass() { + return CleanUpRdsSnapshotsOperation.class; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/rds/CleanUpRdsSnapshotsOperation.java b/src/main/java/com/nike/cerberus/operation/rds/CleanUpRdsSnapshotsOperation.java new file mode 100644 index 00000000..40de8c2f --- /dev/null +++ b/src/main/java/com/nike/cerberus/operation/rds/CleanUpRdsSnapshotsOperation.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 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.rds; + +import com.amazonaws.regions.Regions; +import com.nike.cerberus.command.rds.CleanUpRdsSnapshotsCommand; +import com.nike.cerberus.domain.environment.Stack; +import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.RdsService; +import com.nike.cerberus.store.ConfigStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + +public class CleanUpRdsSnapshotsOperation implements Operation { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ConfigStore configStore; + private final String environmentName; + private final RdsService rdsService; + private final CloudFormationService cloudFormationService; + + @Inject + public CleanUpRdsSnapshotsOperation(ConfigStore configStore, + @Named(ENV_NAME) String environmentName, + RdsService rdsService, + CloudFormationService cloudFormationService) { + + this.configStore = configStore; + this.environmentName = environmentName; + this.rdsService = rdsService; + this.cloudFormationService = cloudFormationService; + } + + @Override + public void run(CleanUpRdsSnapshotsCommand command) { + Regions primaryRegion = configStore.getPrimaryRegion(); + Date oldestAcceptableSnapshotCreationDate = Date.from(Instant.now().minus(command.getDays(), ChronoUnit.DAYS)); + + // Go through each config region + configStore.getConfigEnabledRegions().stream() + // and filter out the primary region, because rds cleans those snapshots automatically + .filter(region -> ! region.equals(primaryRegion)) + .forEach(region -> + // in each region list all the snapshots + rdsService.getDbSnapshots(region).stream() + // filter out snapshots that are not from the cluster / environment under question + // and are older than the number of acceptable days + .filter(snapshot -> + rdsService.wasSnapshotGeneratedFromCmsCluster(snapshot) && + snapshot.getSnapshotCreateTime().before(oldestAcceptableSnapshotCreationDate)) + // delete the snapshots + .forEach(snapshot -> { + if (command.isDryRun()) { + log.info("snapshot: {} with creation date: {} in region: {}", + snapshot.getDBClusterSnapshotIdentifier(), snapshot.getSnapshotCreateTime(), region); + } else { + rdsService.deleteSnapshot(snapshot, region); + } + })); + } + + @Override + public boolean isRunnable(CleanUpRdsSnapshotsCommand command) { + boolean isRunnable = true; + + if (! cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), + Stack.DATABASE.getFullName(environmentName))) { + log.error("The Database stuck must exist in the primary region in order to have snapshots to clean up"); + isRunnable = false; + } + + return isRunnable; + } +} diff --git a/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java b/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java index 15f3778d..b8110cef 100644 --- a/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java +++ b/src/main/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperation.java @@ -17,29 +17,18 @@ package com.nike.cerberus.operation.rds; import com.amazonaws.regions.Regions; -import com.amazonaws.services.rds.AmazonRDS; -import com.amazonaws.services.rds.AmazonRDSClient; -import com.amazonaws.services.rds.model.CopyDBClusterSnapshotRequest; import com.amazonaws.services.rds.model.DBClusterSnapshot; -import com.amazonaws.services.rds.model.DescribeDBClusterSnapshotsRequest; -import com.amazonaws.services.rds.model.DescribeDBClusterSnapshotsResult; -import com.amazonaws.services.rds.model.Tag; import com.nike.cerberus.command.rds.CopyRdsSnapshotsCommand; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; -import com.nike.cerberus.service.AwsClientFactory; import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.RdsService; import com.nike.cerberus.store.ConfigStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -56,27 +45,27 @@ public class CopyRdsSnapshotsOperation implements Operation amazonRDSClientFactory; + private final RdsService rdsService; private final CloudFormationService cloudFormationService; @Inject public CopyRdsSnapshotsOperation(ConfigStore configStore, @Named(ENV_NAME) String environmentName, - AwsClientFactory amazonRDSClientFactory, + RdsService rdsService, CloudFormationService cloudFormationService) { this.configStore = configStore; this.environmentName = environmentName; - this.amazonRDSClientFactory = amazonRDSClientFactory; + this.rdsService = rdsService; this.cloudFormationService = cloudFormationService; } @Override public void run(CopyRdsSnapshotsCommand command) { Regions primaryRegion = configStore.getPrimaryRegion(); - List snapshotsInPrimaryRegion = getDbSnapshots(primaryRegion).stream() + List snapshotsInPrimaryRegion = rdsService.getDbSnapshots(primaryRegion).stream() .filter(dbSnapshot -> - wasSnapshotGeneratedFromCmsCluster(dbSnapshot) && isSnapshotNewerThanGivenDays(dbSnapshot, command.getDays()) + rdsService.wasSnapshotGeneratedFromCmsCluster(dbSnapshot) && rdsService.isSnapshotNewerThanGivenDays(dbSnapshot, command.getDays()) ).collect(Collectors.toList()); configStore.getConfigEnabledRegions().forEach(region -> { @@ -84,95 +73,27 @@ public void run(CopyRdsSnapshotsCommand command) { return; } - List toRegionSnapshots = getDbSnapshots(region).stream() - .filter(this::wasSnapshotGeneratedFromCmsCluster) + List toRegionSnapshots = rdsService.getDbSnapshots(region).stream() + .filter(rdsService::wasSnapshotGeneratedFromCmsCluster) .collect(Collectors.toList()); snapshotsInPrimaryRegion.forEach(fromSnapshot -> { boolean needsCopying = toRegionSnapshots.stream().noneMatch(toSnapshot -> - getIdentifier(fromSnapshot).equals(getIdentifier(toSnapshot))); + rdsService.getIdentifier(fromSnapshot).equals(rdsService.getIdentifier(toSnapshot))); if (needsCopying) { log.info("Preparing to initiate copy of RDS DB Snapshot: {} located in region: {} to region: {}", fromSnapshot.getDBClusterSnapshotIdentifier(), primaryRegion, region); - copySnapshot(fromSnapshot, primaryRegion, region); + rdsService.copySnapshot(fromSnapshot, primaryRegion, region); } else { - log.info("Snapshot: {} already copied to region: {}, skipping...", getIdentifier(fromSnapshot), region); + log.info("Snapshot: {} already copied to region: {}, skipping...", rdsService.getIdentifier(fromSnapshot), region); } }); }); log.info("Finished issuing copy api calls to aws, it can take a long time for copies to finish."); } - /** - * Copies a snapshot from one region to another - * @param fromSnapshot The snapshot that is getting copied - * @param fromRegion The from region - * @param toRegion The to region - */ - private void copySnapshot(DBClusterSnapshot fromSnapshot, Regions fromRegion, Regions toRegion) { - AmazonRDS rds = amazonRDSClientFactory.getClient(toRegion); - rds.copyDBClusterSnapshot(new CopyDBClusterSnapshotRequest() - .withCopyTags(true) - .withSourceDBClusterSnapshotIdentifier(fromSnapshot.getDBClusterSnapshotArn()) - .withTargetDBClusterSnapshotIdentifier(getIdentifier(fromSnapshot)) - .withSourceRegion(fromRegion.getName()) - .withKmsKeyId(configStore.getEnvironmentData().getRegionData().get(toRegion).getConfigCmkArn().get()) - .withTags( - new Tag().withKey("created_by").withValue("cerberus_cli") - ) - ); - } - - /** - * Strips the rds: that aws prepends to automatic snapshots - * @param dbSnapshot the snapshot - * @return a string that can be used to identify automatic and manual copies rds:foo will return foo - */ - protected String getIdentifier(DBClusterSnapshot dbSnapshot) { - return dbSnapshot.getDBClusterSnapshotIdentifier().replace("rds:", ""); - } - - /** - * @return True if the snapshot was created for the cms database cluster for this environment - */ - protected boolean wasSnapshotGeneratedFromCmsCluster(DBClusterSnapshot dbSnapshot) { - String identifier = getIdentifier(dbSnapshot); - return identifier.startsWith(environmentName) && identifier.contains("cmsdatabasecluster"); - } - - /** - * @param dbSnapshot The snapshot under question - * @param days How many days to compare to - * @return True if the snapshot under question is newer than the days passed in - */ - protected boolean isSnapshotNewerThanGivenDays(DBClusterSnapshot dbSnapshot, long days) { - return ZonedDateTime.ofInstant(dbSnapshot.getSnapshotCreateTime().toInstant(), ZoneId.of("UTC")) - .isAfter(Instant.now().atZone(ZoneId.of("UTC")).minus(days, ChronoUnit.DAYS)); - } - - /** - * @return All the RDS Cluster Snapshots for a region - */ - protected List getDbSnapshots(Regions region) { - AmazonRDS rds = amazonRDSClientFactory.getClient(region); - List cmsDatabaseClusterSnapshots = new LinkedList<>(); - String next = null; - do { - DescribeDBClusterSnapshotsRequest req = new DescribeDBClusterSnapshotsRequest(); - - if (next != null) { - req.withMarker(next); - } - DescribeDBClusterSnapshotsResult res = rds.describeDBClusterSnapshots(req); - - cmsDatabaseClusterSnapshots.addAll(res.getDBClusterSnapshots()); - next = res.getMarker(); - } while (next != null); - - return cmsDatabaseClusterSnapshots; - } @Override public boolean isRunnable(CopyRdsSnapshotsCommand command) { diff --git a/src/main/java/com/nike/cerberus/service/RdsService.java b/src/main/java/com/nike/cerberus/service/RdsService.java new file mode 100644 index 00000000..7cab8191 --- /dev/null +++ b/src/main/java/com/nike/cerberus/service/RdsService.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018 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.rds.AmazonRDS; +import com.amazonaws.services.rds.AmazonRDSClient; +import com.amazonaws.services.rds.model.CopyDBClusterSnapshotRequest; +import com.amazonaws.services.rds.model.DBClusterSnapshot; +import com.amazonaws.services.rds.model.DeleteDBClusterSnapshotRequest; +import com.amazonaws.services.rds.model.DescribeDBClusterSnapshotsRequest; +import com.amazonaws.services.rds.model.DescribeDBClusterSnapshotsResult; +import com.amazonaws.services.rds.model.Tag; +import com.nike.cerberus.store.ConfigStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.LinkedList; +import java.util.List; + +import static com.nike.cerberus.module.CerberusModule.ENV_NAME; + +public class RdsService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final AwsClientFactory amazonRDSClientFactory; + private final ConfigStore configStore; + private final String environmentName; + + @Inject + public RdsService(AwsClientFactory amazonRDSClientFactory, + ConfigStore configStore, + @Named(ENV_NAME) String environmentName) { + + this.amazonRDSClientFactory = amazonRDSClientFactory; + this.configStore = configStore; + this.environmentName = environmentName; + } + + /** + * Copies a snapshot from one region to another + * @param fromSnapshot The snapshot that is getting copied + * @param fromRegion The from region + * @param toRegion The to region + */ + public void copySnapshot(DBClusterSnapshot fromSnapshot, Regions fromRegion, Regions toRegion) { + AmazonRDS rds = amazonRDSClientFactory.getClient(toRegion); + rds.copyDBClusterSnapshot(new CopyDBClusterSnapshotRequest() + .withCopyTags(true) + .withSourceDBClusterSnapshotIdentifier(fromSnapshot.getDBClusterSnapshotArn()) + .withTargetDBClusterSnapshotIdentifier(getIdentifier(fromSnapshot)) + .withSourceRegion(fromRegion.getName()) + .withKmsKeyId(configStore.getEnvironmentData().getRegionData().get(toRegion).getConfigCmkArn().get()) + .withTags( + new Tag().withKey("created_by").withValue("cerberus_cli") + ) + ); + } + + /** + * Strips the rds: that aws prepends to automatic snapshots + * @param dbSnapshot the snapshot + * @return a string that can be used to identify automatic and manual copies rds:foo will return foo + */ + public String getIdentifier(DBClusterSnapshot dbSnapshot) { + return dbSnapshot.getDBClusterSnapshotIdentifier().replace("rds:", ""); + } + + /** + * @return True if the snapshot was created for the cms database cluster for this environment + */ + public boolean wasSnapshotGeneratedFromCmsCluster(DBClusterSnapshot dbSnapshot) { + String identifier = getIdentifier(dbSnapshot); + return identifier.startsWith(environmentName) && identifier.contains("cmsdatabasecluster"); + } + + /** + * @param dbSnapshot The snapshot under question + * @param days How many days to compare to + * @return True if the snapshot under question is newer than the days passed in + */ + public boolean isSnapshotNewerThanGivenDays(DBClusterSnapshot dbSnapshot, long days) { + return ZonedDateTime.ofInstant(dbSnapshot.getSnapshotCreateTime().toInstant(), ZoneId.of("UTC")) + .isAfter(Instant.now().atZone(ZoneId.of("UTC")).minus(days, ChronoUnit.DAYS)); + } + + /** + * @return All the RDS Cluster Snapshots for a region + */ + public List getDbSnapshots(Regions region) { + AmazonRDS rds = amazonRDSClientFactory.getClient(region); + List cmsDatabaseClusterSnapshots = new LinkedList<>(); + String next = null; + do { + DescribeDBClusterSnapshotsRequest req = new DescribeDBClusterSnapshotsRequest(); + + if (next != null) { + req.withMarker(next); + } + + DescribeDBClusterSnapshotsResult res = rds.describeDBClusterSnapshots(req); + + cmsDatabaseClusterSnapshots.addAll(res.getDBClusterSnapshots()); + next = res.getMarker(); + } while (next != null); + + return cmsDatabaseClusterSnapshots; + } + + /** + * Deletes a RDS DB Cluster Snapshot in a given region. + * + * @param snapshot The snapshot to delete + * @param region The region that the snapshot is in + */ + public void deleteSnapshot(DBClusterSnapshot snapshot, Regions region) { + log.info("preparing to delete snapshot: {} with creation date: {} in region: {}", + snapshot.getDBClusterSnapshotIdentifier(), snapshot.getSnapshotCreateTime(), region); + + amazonRDSClientFactory.getClient(region).deleteDBClusterSnapshot(new DeleteDBClusterSnapshotRequest() + .withDBClusterSnapshotIdentifier(snapshot.getDBClusterSnapshotIdentifier())); + } +} From 9b6b67740c447365364ecf059c041767ff43c29c Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Thu, 11 Jan 2018 16:11:07 -0800 Subject: [PATCH 62/69] Implement special cases for update stack command --- .../operation/core/UpdateStackOperation.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index 5e446fea..dbe6924a 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -18,8 +18,10 @@ import com.amazonaws.AmazonServiceException; import com.nike.cerberus.command.core.UpdateStackCommand; +import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; +import com.nike.cerberus.service.AmiTagCheckService; import com.nike.cerberus.service.CloudFormationService; import com.nike.cerberus.service.Ec2UserDataService; import com.nike.cerberus.store.ConfigStore; @@ -46,18 +48,19 @@ public class UpdateStackOperation implements Operation { private final Ec2UserDataService ec2UserDataService; private final String environmentName; private final ConfigStore configStore; + private final AmiTagCheckService amiTagCheckService; @Inject public UpdateStackOperation(CloudFormationService cloudFormationService, Ec2UserDataService ec2UserDataService, @Named(ENV_NAME) String environmentName, + AmiTagCheckService amiTagCheckService, ConfigStore configStore) { this.cloudFormationService = cloudFormationService; this.ec2UserDataService = ec2UserDataService; this.environmentName = environmentName; - - + this.amiTagCheckService = amiTagCheckService; this.configStore = configStore; } @@ -75,10 +78,17 @@ public void run(UpdateStackCommand command) { } if (Stack.CMS.equals(command.getStack())) { - // TODO: tag check + command.getDynamicParameters().forEach((key, value) -> { + if (key.equals("amiId")) { + amiTagCheckService.validateAmiTagForStack(value, Stack.CMS); + } + }); } else if (Stack.DATABASE.equals(command.getStack())) { - // TODO: implement storing password if it was changed - //configStore.storeCmsDatabasePassword(databasePassword); + command.getDynamicParameters().forEach((key, value) -> { + if (key.equals("cmsDbMasterPassword")) { + configStore.storeCmsDatabasePassword(value); + } + }); } else if (Stack.LOAD_BALANCER.equals(command.getStack())) { parameters.put("sslCertificateArn", configStore.getCertificationInformationList() .getLast().getIdentityManagementCertificateArn()); From e30bc567c7bcffb29b8a840d3036a333c68560a3 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Fri, 12 Jan 2018 10:38:07 -0800 Subject: [PATCH 63/69] add log message for every command, so that it is easier to tell that command is running when a command logs nothing --- src/main/java/com/nike/cerberus/cli/CerberusRunner.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java index 036cd781..9d182bf4 100644 --- a/src/main/java/com/nike/cerberus/cli/CerberusRunner.java +++ b/src/main/java/com/nike/cerberus/cli/CerberusRunner.java @@ -62,6 +62,8 @@ import com.nike.cerberus.module.PropsModule; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.util.LocalEnvironmentValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; @@ -99,6 +101,9 @@ public void run(String[] args) { commander.parse(args); configureLogging(cerberusCommand.isDebug()); + + final Logger log = LoggerFactory.getLogger(getClass()); + String commandName = commander.getParsedCommand(); Command command = commandMap.get(commandName); @@ -116,7 +121,9 @@ public void run(String[] args) { Operation operation = injector.getInstance(command.getOperationClass()); if (operation.isRunnable(command)) { + log.info("Running command: {}", commandName); operation.run(command); + log.info("Finished command: {}", commandName); } else { throw new RuntimeException("Command not runnable"); } From 09dbae4fa137325e16560d05b798e959ae1bec50 Mon Sep 17 00:00:00 2001 From: Shaun Ford Date: Fri, 12 Jan 2018 10:40:16 -0800 Subject: [PATCH 64/69] Fix unit test --- ...OperationTest.java => RdsServiceTest.java} | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) rename src/test/java/com/nike/cerberus/operation/rds/{CopyRdsSnapshotsOperationTest.java => RdsServiceTest.java} (76%) diff --git a/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java b/src/test/java/com/nike/cerberus/operation/rds/RdsServiceTest.java similarity index 76% rename from src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java rename to src/test/java/com/nike/cerberus/operation/rds/RdsServiceTest.java index 5ea4744a..5d9cab5b 100644 --- a/src/test/java/com/nike/cerberus/operation/rds/CopyRdsSnapshotsOperationTest.java +++ b/src/test/java/com/nike/cerberus/operation/rds/RdsServiceTest.java @@ -19,7 +19,7 @@ import com.amazonaws.services.rds.AmazonRDSClient; import com.amazonaws.services.rds.model.DBClusterSnapshot; import com.nike.cerberus.service.AwsClientFactory; -import com.nike.cerberus.service.CloudFormationService; +import com.nike.cerberus.service.RdsService; import com.nike.cerberus.store.ConfigStore; import org.junit.Before; import org.junit.Test; @@ -33,29 +33,26 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -public class CopyRdsSnapshotsOperationTest { +public class RdsServiceTest { @Mock private ConfigStore configStore; - @Mock - private CloudFormationService cloudFormationService; - private final static String envName = "test"; - @Mock - private AwsClientFactory clientFactory; + private RdsService rdsService; - private CopyRdsSnapshotsOperation operation; + @Mock + private AwsClientFactory rdsClientFactory; @Before public void before() { - operation = new CopyRdsSnapshotsOperation(configStore, envName, clientFactory, cloudFormationService); + rdsService = new RdsService(rdsClientFactory, configStore, envName); } @Test public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_true_if_id_matches() { - boolean actual = operation.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() + boolean actual = rdsService.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() .withDBClusterSnapshotIdentifier("rds:test-cerberus-database-cmsdatabasecluster-jns1lasdf9d-2017-12-12-18-07")); assertTrue(actual); @@ -63,7 +60,7 @@ public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_true_if_id_matc @Test public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_false_for_a_different_env() { - boolean actual = operation.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() + boolean actual = rdsService.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() .withDBClusterSnapshotIdentifier("rds:dev-cerberus-database-cmsdatabasecluster-jns1lasdf9d-2017-12-12-18-07")); assertFalse(actual); @@ -71,7 +68,7 @@ public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_false_for_a_dif @Test public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_false_if_id_does_not_matche() { - boolean actual = operation.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() + boolean actual = rdsService.wasSnapshotGeneratedFromCmsCluster(new DBClusterSnapshot() .withDBClusterSnapshotIdentifier("rds:some-other-db-cluster-jns1lasdf9d-2017-12-12-18-07")); assertFalse(actual); @@ -81,7 +78,7 @@ public void test_that_wasSnapshotGeneratedFromCmsCluster_returns_false_if_id_doe public void test_that_isSnapshotNewerThan24Hours_returns_true_if_snapshot_is_newer_than_24h() { DBClusterSnapshot ss = new DBClusterSnapshot().withSnapshotCreateTime(Date.from(Instant.now().atZone(ZoneId.of("UTC")).toInstant())); - boolean actual = operation.isSnapshotNewerThanGivenDays(ss, 1); + boolean actual = rdsService.isSnapshotNewerThanGivenDays(ss, 1); assertTrue(actual); } @@ -91,7 +88,7 @@ public void test_that_isSnapshotNewerThan24Hours_returns_false_if_snapshot_is_ol DBClusterSnapshot ss = new DBClusterSnapshot().withSnapshotCreateTime(Date.from(Instant.now().atZone(ZoneId.of("UTC")) .minus(1, ChronoUnit.WEEKS).toInstant())); - boolean actual = operation.isSnapshotNewerThanGivenDays(ss, 1); + boolean actual = rdsService.isSnapshotNewerThanGivenDays(ss, 1); assertFalse(actual); } From 6c759de43f487746c52855f36c3b4efca5a660d8 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 16 Jan 2018 10:24:07 -0800 Subject: [PATCH 65/69] There are edge cases where bucket location can not be retreived from every region, but exists in the error object returned from AWS --- .../operation/core/DeleteStackOperation.java | 2 +- .../com/nike/cerberus/store/ConfigStore.java | 31 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java index a6395763..e142b125 100644 --- a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java @@ -79,7 +79,7 @@ public void run(DeleteStackCommand command) { log.info("Deleting stack: {} in region: {}", stackName, region); cloudFormationService.deleteStackAndWait(region, stackName); - log.info("Finished deleting stack: {} in regionL {}", stackName, region); + log.info("Finished deleting stack: {} in region: {}", stackName, region); } private Regions getRegion(DeleteStackCommand command) { diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index 5f2034b8..d3db816e 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -22,6 +22,7 @@ import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory; import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.securitytoken.AWSSecurityTokenService; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; @@ -594,18 +595,34 @@ private String findConfigBucketInSuppliedConfigRegion() { AmazonS3Client s3Client = amazonS3ClientFactory.getClient(configRegion); List buckets = s3Client.listBuckets(); String envBucket = null; + String tokenizedEnvName = StringUtils.replaceAll(environmentName, "_", "-"); for (Bucket bucket : buckets) { String bucketName = bucket.getName(); if (StringUtils.contains(bucket.getName(), ConfigConstants.CONFIG_BUCKET_KEY)) { - String bucketLocationResult = s3Client.getBucketLocation(bucketName); - Regions bucketRegion = StringUtils.equals("US", bucketLocationResult) ? Regions.US_EAST_1 : Regions.fromName(bucketLocationResult); - if (configRegion.equals(bucketRegion)) { - String tokenizedEnvName = StringUtils.replaceAll(environmentName, "_", "-"); - if (StringUtils.startsWith(bucketName, tokenizedEnvName)) { - envBucket = bucketName; - break; + String bucketLocationResult = null; + try { + bucketLocationResult = s3Client.getBucketLocation(bucketName); + } catch (AmazonS3Exception e) { + logger.debug("Failed to get bucket location for bucket: {}, msg: {}", bucketName, e.getMessage()); + if (e.getErrorCode().equals("AuthorizationHeaderMalformed")) { + if (e.getAdditionalDetails().containsKey("Region")) { + bucketLocationResult = e.getAdditionalDetails().get("Region"); + logger.debug("Determined region from S3 Error object, region: {}", bucketLocationResult); + } + } + if (bucketLocationResult == null) { + logger.debug("Failed to determine region for bucket: {} skipping...", bucketName); } } + Regions bucketRegion = StringUtils.equals("US", bucketLocationResult) ? Regions.US_EAST_1 : Regions.fromName(bucketLocationResult); + + logger.debug("Checking that bucket: {} in region: {} Starts with: {} and is in region: {}", bucketName, bucketRegion.getName(), tokenizedEnvName, configRegion.getName()); + + if (configRegion.equals(bucketRegion) && StringUtils.startsWith(bucketName, tokenizedEnvName)) { + logger.info("Found config bucket: {}", bucketName); + envBucket = bucketName; + break; + } } } From 72ba0569dc8d7927774fd57e36e1e2376f7c8592 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Wed, 17 Jan 2018 16:53:09 -0800 Subject: [PATCH 66/69] Make CLI compatable with more than us-west-2 --- .../validator/EnvironmentNameValidator.java | 7 +-- .../LoadBalancerParameters.java | 11 +++++ .../composite/DeleteEnvironmentOperation.java | 12 +++--- .../core/CreateLoadBalancerOperation.java | 43 ++++++++++++++++++- .../operation/core/DeleteStackOperation.java | 21 ++++++--- .../core/InitializeEnvironmentOperation.java | 5 +++ .../resources/cloudformation/database.yaml | 6 --- .../cloudformation/load-balancer.yaml | 5 ++- 8 files changed, 85 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/nike/cerberus/command/validator/EnvironmentNameValidator.java b/src/main/java/com/nike/cerberus/command/validator/EnvironmentNameValidator.java index 7fdf1cc1..99ccbf54 100644 --- a/src/main/java/com/nike/cerberus/command/validator/EnvironmentNameValidator.java +++ b/src/main/java/com/nike/cerberus/command/validator/EnvironmentNameValidator.java @@ -26,12 +26,13 @@ */ public class EnvironmentNameValidator implements IParameterValidator { - private final Pattern pattern = Pattern.compile("\\w+"); + public static final Pattern VALID_NAME_PATTERN = Pattern.compile("[A-Za-z\\d-]+"); + public static final String ALLOWED_DESCRIPTION = "Environment name may only contain alpha-numeric characters and hyphens."; @Override public void validate(final String name, final String value) throws ParameterException { - if (!pattern.matcher(value).matches()) { - throw new ParameterException("Environment name may only contain alpha-numeric characters and underscores."); + if (!VALID_NAME_PATTERN.matcher(value).matches()) { + throw new ParameterException(ALLOWED_DESCRIPTION); } } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java index 85028665..7b9270ba 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/LoadBalancerParameters.java @@ -36,6 +36,8 @@ public class LoadBalancerParameters { private String sslPolicy; + private String elasticLoadBalancingAccountId; + public String getSgStackName() { return sgStackName; } @@ -98,4 +100,13 @@ public LoadBalancerParameters setSslPolicy(String sslPolicy) { this.sslPolicy = sslPolicy; return this; } + + public String getElasticLoadBalancingAccountId() { + return elasticLoadBalancingAccountId; + } + + public LoadBalancerParameters setElasticLoadBalancingAccountId(String elasticLoadBalancingAccountId) { + this.elasticLoadBalancingAccountId = elasticLoadBalancingAccountId; + return this; + } } diff --git a/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java index 7bbe93d0..2f819e76 100644 --- a/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/composite/DeleteEnvironmentOperation.java @@ -52,6 +52,12 @@ protected List getCompositeCommandChain(DeleteEnvironmentComma .build()) ); + chainableCommandList.add(ChainableCommand.Builder.create() + .withCommand(new DeleteStackCommand()) + .withAdditionalArg(DeleteStackCommand.STACK_NAME_LONG_ARG) + .withAdditionalArg(IAM_ROLES.getName()) + .build()); + configStore.getConfigEnabledRegions().forEach(region -> { chainableCommandList.add(ChainableCommand.Builder.create() .withCommand(new DeleteStackCommand()) @@ -60,12 +66,6 @@ protected List getCompositeCommandChain(DeleteEnvironmentComma .build()); }); - chainableCommandList.add(ChainableCommand.Builder.create() - .withCommand(new DeleteStackCommand()) - .withAdditionalArg(DeleteStackCommand.STACK_NAME_LONG_ARG) - .withAdditionalArg(IAM_ROLES.getName()) - .build()); - return chainableCommandList; } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java index 3e844cd3..3c9d10bd 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateLoadBalancerOperation.java @@ -16,6 +16,8 @@ package com.nike.cerberus.operation.core; +import com.amazonaws.regions.Regions; +import com.google.common.collect.ImmutableMap; import com.nike.cerberus.command.core.CreateLoadBalancerCommand; import com.nike.cerberus.domain.cloudformation.LoadBalancerParameters; import com.nike.cerberus.domain.cloudformation.VpcOutputs; @@ -33,6 +35,7 @@ import java.util.Map; import static com.nike.cerberus.module.CerberusModule.ENV_NAME; +import static java.util.stream.Collectors.joining; /** * Creates the base components via CloudFormation used by all of Cerberus. @@ -41,6 +44,28 @@ public class CreateLoadBalancerOperation implements Operation regionToAwsElbAccountIdMap = ImmutableMap.builder() + .put("us-east-1", "127311923021") + .put("us-east-2", "033677994240") + .put("us-west-1", "027434742980") + .put("us-west-2", "797873946194") + .put("ca-central-1", "985666609251") + .put("eu-central-1", "054676820928") + .put("eu-west-1", "156460612806") + .put("eu-west-2", "652711504416") + .put("eu-west-3", "009996457667") + .put("ap-northeast-1", "582318560864") + .put("ap-northeast-2", "600734575887") + .put("ap-southeast-1", "114774131450") + .put("ap-southeast-2", "783225319266") + .put("ap-south-1", "718504428378") + .put("sa-east-1", "507241528517") + .put("us-gov-west-1", "048591011584") + .put("cn-north-1", "638102146993") + .put("cn-northwest-1", "037604701340") + .build(); + private final String environmentName; private final CloudFormationService cloudFormationService; @@ -63,19 +88,33 @@ public CreateLoadBalancerOperation(@Named(ENV_NAME) String environmentName, @Override public void run(CreateLoadBalancerCommand command) { + Regions loadBalancerRegion = configStore.getPrimaryRegion(); + VpcOutputs vpcOutputs = configStore.getVpcStackOutputs(); // Use latest cert, if there happens to be more than one for some reason String sslCertificateArn = configStore.getCertificationInformationList() .getLast().getIdentityManagementCertificateArn(); + if (!regionToAwsElbAccountIdMap.containsKey(loadBalancerRegion.getName())) { + throw new RuntimeException( + String.format("The region: %s was not in the region to AWS ELB Account Id map: [ %s ]", + loadBalancerRegion.getName(), + regionToAwsElbAccountIdMap.entrySet().stream() + .map(entry -> entry.getKey() + "->" + entry.getValue()) + .collect(joining(", "))) + ); + } + LoadBalancerParameters loadBalancerParameters = new LoadBalancerParameters() .setVpcId(vpcOutputs.getVpcId()) .setSslCertificateArn(sslCertificateArn) .setSgStackName(Stack.SECURITY_GROUPS.getFullName(environmentName)) .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) - .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()); + .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()) + .setElasticLoadBalancingAccountId(regionToAwsElbAccountIdMap.get(loadBalancerRegion.getName())); + if (StringUtils.isNotBlank(command.getLoadBalancerSslPolicyOverride())) { loadBalancerParameters.setSslPolicy(command.getLoadBalancerSslPolicyOverride()); @@ -84,7 +123,7 @@ public void run(CreateLoadBalancerCommand command) { Map parameters = cloudFormationObjectMapper.convertValue(loadBalancerParameters); cloudFormationService.createStackAndWait( - configStore.getPrimaryRegion(), + loadBalancerRegion, Stack.LOAD_BALANCER, parameters, true, diff --git a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java index e142b125..9390b417 100644 --- a/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/DeleteStackOperation.java @@ -20,6 +20,7 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.iterable.S3Versions; +import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.S3VersionSummary; import com.google.inject.Inject; import com.nike.cerberus.command.core.DeleteStackCommand; @@ -66,13 +67,19 @@ public void run(DeleteStackCommand command) { cloudFormationService.getStackResources(region, stackName) .forEach(stackResourceSummary -> { if (stackResourceSummary.getResourceType().equals("AWS::S3::Bucket")) { - String bucketName = stackResourceSummary.getPhysicalResourceId(); - log.info("Detected S3 Bucket: {}, emptying contents before deleting stack", bucketName); - AmazonS3 amazonS3 = amazonS3Factory.getClient(region); - for (S3VersionSummary version : S3Versions.inBucket(amazonS3, bucketName)) { - String key = version.getKey(); - String versionId = version.getVersionId(); - amazonS3.deleteVersion(bucketName, key, versionId); + try { + String bucketName = stackResourceSummary.getPhysicalResourceId(); + log.info("Detected S3 Bucket: {}, emptying contents before deleting stack", bucketName); + AmazonS3 amazonS3 = amazonS3Factory.getClient(region); + for (S3VersionSummary version : S3Versions.inBucket(amazonS3, bucketName)) { + String key = version.getKey(); + String versionId = version.getVersionId(); + amazonS3.deleteVersion(bucketName, key, versionId); + } + } catch (AmazonS3Exception e) { + if (! e.getErrorCode().equals("NoSuchBucket")) { + throw e; + } } } }); diff --git a/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java b/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java index 8f4415b3..d79b2e69 100644 --- a/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/InitializeEnvironmentOperation.java @@ -18,6 +18,7 @@ import com.amazonaws.regions.Regions; import com.nike.cerberus.command.core.InitializeEnvironmentCommand; +import com.nike.cerberus.command.validator.EnvironmentNameValidator; import com.nike.cerberus.domain.cloudformation.ConfigParameters; import com.nike.cerberus.domain.cloudformation.ConfigOutputs; import com.nike.cerberus.domain.environment.Stack; @@ -97,6 +98,10 @@ public void run(InitializeEnvironmentCommand command) { public boolean isRunnable(InitializeEnvironmentCommand command) { boolean isRunnable = true; + if (!EnvironmentNameValidator.VALID_NAME_PATTERN.matcher(environmentName).matches()) { + throw new RuntimeException(EnvironmentNameValidator.ALLOWED_DESCRIPTION); + } + if (command.getRegions().size() < 2) { log.error("You must supply at least 2 regions so that config and secure data can be encrypted " + "in a highly available manner"); diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml index 66d77dcc..18b5e70a 100644 --- a/src/main/resources/cloudformation/database.yaml +++ b/src/main/resources/cloudformation/database.yaml @@ -60,10 +60,6 @@ Parameters: Resources: CmsDatabaseCluster: Properties: - AvailabilityZones: - - Ref: 'cmsDbInstanceAz1' - - Ref: 'cmsDbInstanceAz2' - - Ref: 'cmsDbInstanceAz3' BackupRetentionPeriod: 14 DatabaseName: !Ref 'cmsDbName' DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' @@ -83,7 +79,6 @@ Resources: # Aurora decides automatically for you behind the scenes. CmsDbInstance1: Properties: - AvailabilityZone: !Ref 'cmsDbInstanceAz1' DBClusterIdentifier: !Ref CmsDatabaseCluster DBInstanceClass: !Ref 'cmsDbInstanceClass' DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' @@ -93,7 +88,6 @@ Resources: Type: AWS::RDS::DBInstance CmsDbInstance2: Properties: - AvailabilityZone: !Ref 'cmsDbInstanceAz2' DBClusterIdentifier: !Ref CmsDatabaseCluster DBInstanceClass: !Ref 'cmsDbInstanceClass' DBParameterGroupName: !Ref 'CmsDatabaseParamGroup' diff --git a/src/main/resources/cloudformation/load-balancer.yaml b/src/main/resources/cloudformation/load-balancer.yaml index cc739278..5aa9f0a6 100644 --- a/src/main/resources/cloudformation/load-balancer.yaml +++ b/src/main/resources/cloudformation/load-balancer.yaml @@ -43,6 +43,9 @@ Parameters: for more information Type: String Default: ELBSecurityPolicy-TLS-1-2-2017-01 + elasticLoadBalancingAccountId: + Description: The account id for the AWS ELB Account. See https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-access-logs.html + Type: String Resources: ALBListener: Type: AWS::ElasticLoadBalancingV2::Listener @@ -70,7 +73,7 @@ Resources: Effect: Allow Principal: AWS: - - 797873946194 # the AWS-owned ElasticLoadBalancing account for us-west-2 (required) + - !Ref 'elasticLoadBalancingAccountId' Resource: - !Join ['', ['arn:aws:s3:::', !Ref 'ALBLogBucket', /*]] Sid: Allow-ALB-Log-Access From 8d8d6924ff67416e18f38f4989e653fc30ad2eba Mon Sep 17 00:00:00 2001 From: Todd Lisonbee Date: Fri, 19 Jan 2018 13:30:52 -0800 Subject: [PATCH 67/69] Enabling key rotation (#113) --- src/main/resources/cloudformation/config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/cloudformation/config.yaml b/src/main/resources/cloudformation/config.yaml index 354ea265..feaba0fc 100644 --- a/src/main/resources/cloudformation/config.yaml +++ b/src/main/resources/cloudformation/config.yaml @@ -61,6 +61,7 @@ Resources: Properties: Description: !Join ['', ['Environment: ', !Ref 'environmentName', ' Cerberus encryption key for storing config files in s3 buckets.']] Enabled: 'true' + EnableKeyRotation: 'true' KeyPolicy: Statement: - @@ -103,6 +104,7 @@ Resources: Properties: Description: !Join ['', ['Environment: ', !Ref 'environmentName', ' CMS KMS CMK for storing secure data in RDS.']] Enabled: 'true' + EnableKeyRotation: 'true' KeyPolicy: Statement: - Sid: Allow-Root-User From 59d681ae2282e4e71aabd258b304d45596642598 Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 23 Jan 2018 09:05:13 -0800 Subject: [PATCH 68/69] =?UTF-8?q?Modify=20database=20cloudformation=20to?= =?UTF-8?q?=20allow=20snapshot=20restores=20and=20use=20a=20c=E2=80=A6=20(?= =?UTF-8?q?#116)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify database cloudformation to allow snapshot restores and use a cname for cms --- .../cli/EnvironmentConfigToArgsMapper.java | 3 ++ .../command/rds/CreateDatabaseCommand.java | 13 +++++++ .../cloudformation/DatabaseParameters.java | 32 ++++++++++++++++ .../domain/cloudformation/VpcOutputs.java | 22 +++++++++++ .../domain/cloudformation/VpcParameters.java | 11 ++++++ .../domain/input/RdsRegionSpecificInput.java | 9 +++++ .../operation/core/CreateVpcOperation.java | 3 +- .../operation/core/UpdateStackOperation.java | 18 ++++++--- .../rds/CreateDatabaseOperation.java | 20 +++++++--- .../service/CloudFormationService.java | 24 ++++++++++-- .../com/nike/cerberus/store/ConfigStore.java | 10 ++--- .../resources/cloudformation/database.yaml | 38 ++++++++++++++----- src/main/resources/cloudformation/vpc.yaml | 18 +++++++++ src/test/resources/environment.yaml | 2 + 14 files changed, 193 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java index c31565ff..bc88c8bf 100644 --- a/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java +++ b/src/main/java/com/nike/cerberus/cli/EnvironmentConfigToArgsMapper.java @@ -247,6 +247,9 @@ private static List getCreateDatabaseCommandArgs(EnvironmentConfig confi if (config.getPrimaryRegionConfig().getRds().isPresent()) { args.addOption(CreateDatabaseCommand.INSTANCE_CLASS_LONG_ARG, config.getPrimaryRegionConfig().getRds().get().getSize()); + + args.addOption(CreateDatabaseCommand.RESTORE_FROM_SNAPSHOT, + config.getPrimaryRegionConfig().getRds().get().getDbClusterIdentifier()); } args.addAll(getGlobalTags(config)); return args.build(); diff --git a/src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java b/src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java index 4dea6f6f..89dc1b15 100644 --- a/src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java +++ b/src/main/java/com/nike/cerberus/command/rds/CreateDatabaseCommand.java @@ -37,6 +37,8 @@ public class CreateDatabaseCommand implements Command { public static final String INSTANCE_CLASS_LONG_ARG = "--instance-class"; + public static final String RESTORE_FROM_SNAPSHOT = "--restore-from-snapshot-using-identifier"; + @ParametersDelegate private TagParametersDelegate tagsDelegate = new TagParametersDelegate(); @@ -53,6 +55,17 @@ public String getInstanceClass() { return instanceClass; } + @Parameter( + names = RESTORE_FROM_SNAPSHOT, + description = "option for setting a snapshot identifier on the RDS stack to restore from the snapshot " + + "while standing up the new RDS cluster via cloudformation" + ) + String snapshotIdentifier; + + public String getSnapshotIdentifier() { + return snapshotIdentifier; + } + @Override public String getCommandName() { return COMMAND_NAME; diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java index aa064b3e..aa22373b 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/DatabaseParameters.java @@ -43,6 +43,12 @@ public class DatabaseParameters { private String vpcSubnetIdForAz3; + private String snapshotIdentifier; + + private String vpcInternalBaseDomainName; + + private String vpcInternalHostedZoneId; + public String getCmsDbInstanceAz1() { return cmsDbInstanceAz1; } @@ -142,4 +148,30 @@ public DatabaseParameters setVpcSubnetIdForAz3(String vpcSubnetIdForAz3) { return this; } + public String getSnapshotIdentifier() { + return snapshotIdentifier; + } + + public DatabaseParameters setSnapshotIdentifier(String snapshotIdentifier) { + this.snapshotIdentifier = snapshotIdentifier; + return this; + } + + public String getVpcInternalBaseDomainName() { + return vpcInternalBaseDomainName; + } + + public DatabaseParameters setVpcInternalBaseDomainName(String vpcInternalBaseDomainName) { + this.vpcInternalBaseDomainName = vpcInternalBaseDomainName; + return this; + } + + public String getVpcInternalHostedZoneId() { + return vpcInternalHostedZoneId; + } + + public DatabaseParameters setVpcInternalHostedZoneId(String vpcInternalHostedZoneId) { + this.vpcInternalHostedZoneId = vpcInternalHostedZoneId; + return this; + } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java index 9e331dcd..524e67ba 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcOutputs.java @@ -35,6 +35,10 @@ public class VpcOutputs { private String vpcId; + private String vpcInternalBaseDomainName; + + private String vpcInternalHostedZoneId; + public String getSubnetCidrBlockForAz1() { return subnetCidrBlockForAz1; } @@ -97,4 +101,22 @@ public VpcOutputs setVpcId(String vpcId) { this.vpcId = vpcId; return this; } + + public String getVpcInternalBaseDomainName() { + return vpcInternalBaseDomainName; + } + + public VpcOutputs setVpcInternalBaseDomainName(String vpcInternalBaseDomainName) { + this.vpcInternalBaseDomainName = vpcInternalBaseDomainName; + return this; + } + + public String getVpcInternalHostedZoneId() { + return vpcInternalHostedZoneId; + } + + public VpcOutputs setVpcInternalHostedZoneId(String vpcInternalHostedZoneId) { + this.vpcInternalHostedZoneId = vpcInternalHostedZoneId; + return this; + } } diff --git a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java index 22ab9055..01b1d7b5 100644 --- a/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java +++ b/src/main/java/com/nike/cerberus/domain/cloudformation/VpcParameters.java @@ -37,6 +37,8 @@ public class VpcParameters { private String vpcCidrBlock; + private String environmentName; + public String getAz1() { return az1; } @@ -108,4 +110,13 @@ public VpcParameters setVpcCidrBlock(String vpcCidrBlock) { this.vpcCidrBlock = vpcCidrBlock; return this; } + + public String getEnvironmentName() { + return environmentName; + } + + public VpcParameters setEnvironmentName(String environmentName) { + this.environmentName = environmentName; + return this; + } } diff --git a/src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java b/src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java index 343b5e4c..9af28701 100644 --- a/src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java +++ b/src/main/java/com/nike/cerberus/domain/input/RdsRegionSpecificInput.java @@ -21,6 +21,7 @@ */ public class RdsRegionSpecificInput { private String size; + private String dbClusterIdentifier; public String getSize() { return size; @@ -29,4 +30,12 @@ public String getSize() { public void setSize(String size) { this.size = size; } + + public String getDbClusterIdentifier() { + return dbClusterIdentifier; + } + + public void setDbClusterIdentifier(String dbClusterIdentifier) { + this.dbClusterIdentifier = dbClusterIdentifier; + } } diff --git a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java index be8e4cb2..21313661 100644 --- a/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/CreateVpcOperation.java @@ -74,7 +74,8 @@ public void run(CreateVpcCommand command) { VpcParameters vpcParameters = new VpcParameters() .setAz1(azByIdentifier.get(1)) .setAz2(azByIdentifier.get(2)) - .setAz3(azByIdentifier.get(3)); + .setAz3(azByIdentifier.get(3)) + .setEnvironmentName(environmentName); Map parameters = cloudFormationObjectMapper.convertValue(vpcParameters); diff --git a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java index dbe6924a..b182dd10 100644 --- a/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java +++ b/src/main/java/com/nike/cerberus/operation/core/UpdateStackOperation.java @@ -18,7 +18,6 @@ import com.amazonaws.AmazonServiceException; import com.nike.cerberus.command.core.UpdateStackCommand; -import com.nike.cerberus.domain.cloudformation.DatabaseParameters; import com.nike.cerberus.domain.environment.Stack; import com.nike.cerberus.operation.Operation; import com.nike.cerberus.service.AmiTagCheckService; @@ -84,11 +83,18 @@ public void run(UpdateStackCommand command) { } }); } else if (Stack.DATABASE.equals(command.getStack())) { - command.getDynamicParameters().forEach((key, value) -> { - if (key.equals("cmsDbMasterPassword")) { - configStore.storeCmsDatabasePassword(value); - } - }); + Optional dbPasswordOverwrite = command.getDynamicParameters().entrySet().stream() + .filter(entry -> entry.getKey().equals("cmsDbMasterPassword")) + .map(Map.Entry::getValue) + .findFirst(); + + dbPasswordOverwrite.ifPresent(configStore::storeCmsDatabasePassword); + + parameters.put("cmsDbMasterPassword", dbPasswordOverwrite.orElseGet(() -> + configStore.getCmsDatabasePassword().orElseThrow(() -> + new RuntimeException("Unable to find current database password, add new one " + + "with -PcmsDbMasterPassword=xxxxxxx")))); + } else if (Stack.LOAD_BALANCER.equals(command.getStack())) { parameters.put("sslCertificateArn", configStore.getCertificationInformationList() .getLast().getIdentityManagementCertificateArn()); diff --git a/src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java b/src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java index 79d167b3..08e6dac4 100644 --- a/src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java +++ b/src/main/java/com/nike/cerberus/operation/rds/CreateDatabaseOperation.java @@ -82,7 +82,10 @@ public void run(CreateDatabaseCommand command) { .setVpcSubnetIdForAz1(vpcOutputs.getVpcSubnetIdForAz1()) .setVpcSubnetIdForAz2(vpcOutputs.getVpcSubnetIdForAz2()) .setVpcSubnetIdForAz3(vpcOutputs.getVpcSubnetIdForAz3()) - .setCmsDbInstanceClass(command.getInstanceClass()); + .setCmsDbInstanceClass(command.getInstanceClass()) + .setSnapshotIdentifier(command.getSnapshotIdentifier()) + .setVpcInternalBaseDomainName(vpcOutputs.getVpcInternalBaseDomainName()) + .setVpcInternalHostedZoneId(vpcOutputs.getVpcInternalHostedZoneId()); Map parameters = cloudFormationObjectMapper.convertValue(databaseParameters); @@ -99,13 +102,18 @@ public void run(CreateDatabaseCommand command) { @Override public boolean isRunnable(CreateDatabaseCommand command) { - try { - cloudFormationService.getStackId(configStore.getPrimaryRegion(), Stack.SECURITY_GROUPS.getFullName(environmentName)); - } catch (IllegalArgumentException iae) { + boolean isRunnable = true; + + if (!cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.SECURITY_GROUPS.getFullName(environmentName))) { + isRunnable = false; logger.error("The security group stack must exist to create the the data base stack!"); - return false; + } + + if (cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.DATABASE.getFullName(environmentName))) { + isRunnable = false; + logger.error("The database stack already exists, use update-stack"); } - return !cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), Stack.DATABASE.getFullName(environmentName)); + return isRunnable; } } diff --git a/src/main/java/com/nike/cerberus/service/CloudFormationService.java b/src/main/java/com/nike/cerberus/service/CloudFormationService.java index 20fed817..5a91f7cc 100644 --- a/src/main/java/com/nike/cerberus/service/CloudFormationService.java +++ b/src/main/java/com/nike/cerberus/service/CloudFormationService.java @@ -36,7 +36,9 @@ import com.amazonaws.services.cloudformation.model.StackResourceSummary; import com.amazonaws.services.cloudformation.model.StackStatus; import com.amazonaws.services.cloudformation.model.Tag; +import com.amazonaws.services.cloudformation.model.TemplateParameter; import com.amazonaws.services.cloudformation.model.UpdateStackRequest; +import com.amazonaws.services.cloudformation.model.ValidateTemplateRequest; import com.amazonaws.services.cloudformation.waiters.AmazonCloudFormationWaiters; import com.amazonaws.waiters.Waiter; import com.amazonaws.waiters.WaiterHandler; @@ -209,15 +211,19 @@ public void updateStackAndWait(Regions region, log.info("Updating: Cloud Formation Template: {}, Stack Name: {}, Region: {}", stack.getTemplatePath(), stackName, region.getName()); UpdateStackRequest request = new UpdateStackRequest() - .withStackName(stackName) - .withParameters(convertParameters(parameters)); + .withStackName(stackName); if (overwrite) { - request.withTemplateBody(stack.getTemplateText()); + String template = stack.getTemplateText(); + request.withTemplateBody(template); + // filter out params that are no longer in the template + parameters.keySet().retainAll(validateTemplateAndRetrieveParameters(template, region)); } else { request.withUsePreviousTemplate(true); } + request.withParameters(convertParameters(parameters)); + if (iamCapabilities) { request.getCapabilities().add("CAPABILITY_IAM"); } @@ -231,6 +237,18 @@ public void updateStackAndWait(Regions region, } + /** + * Validates and retrieves the set of parameter keys for a template. + * @param templateText The template to validate + * @param region The region that is being used + * @return The set of parameters that a template has. + */ + private Set validateTemplateAndRetrieveParameters(String templateText, Regions region) { + return cloudFormationClientFactory.getClient(region).validateTemplate( + new ValidateTemplateRequest().withTemplateBody(templateText) + ).getParameters().stream().map(TemplateParameter::getParameterKey).collect(Collectors.toSet()); + } + /** * Deletes an existing stack by name in the region provided. * diff --git a/src/main/java/com/nike/cerberus/store/ConfigStore.java b/src/main/java/com/nike/cerberus/store/ConfigStore.java index d3db816e..6f34cf83 100644 --- a/src/main/java/com/nike/cerberus/store/ConfigStore.java +++ b/src/main/java/com/nike/cerberus/store/ConfigStore.java @@ -604,11 +604,11 @@ private String findConfigBucketInSuppliedConfigRegion() { bucketLocationResult = s3Client.getBucketLocation(bucketName); } catch (AmazonS3Exception e) { logger.debug("Failed to get bucket location for bucket: {}, msg: {}", bucketName, e.getMessage()); - if (e.getErrorCode().equals("AuthorizationHeaderMalformed")) { - if (e.getAdditionalDetails().containsKey("Region")) { - bucketLocationResult = e.getAdditionalDetails().get("Region"); - logger.debug("Determined region from S3 Error object, region: {}", bucketLocationResult); - } + if (e.getErrorCode().equals("AuthorizationHeaderMalformed") && + e.getAdditionalDetails().containsKey("Region")) { + + bucketLocationResult = e.getAdditionalDetails().get("Region"); + logger.debug("Determined region from S3 Error object, region: {}", bucketLocationResult); } if (bucketLocationResult == null) { logger.debug("Failed to determine region for bucket: {} skipping...", bucketName); diff --git a/src/main/resources/cloudformation/database.yaml b/src/main/resources/cloudformation/database.yaml index 18b5e70a..671f2cac 100644 --- a/src/main/resources/cloudformation/database.yaml +++ b/src/main/resources/cloudformation/database.yaml @@ -1,6 +1,6 @@ AWSTemplateFormatVersion: '2010-09-09' Conditions: - RegionEqualsEastOne: !Equals [!Ref 'AWS::Region', us-east-1] + HasSnapshotIdentifier: !Not [!Equals [!Ref 'snapshotIdentifier', '']] Description: Creates the database for use by the Cerberus Management Service (CMS) Outputs: cmsDbAddress: @@ -11,7 +11,7 @@ Outputs: Value: !Ref 'CmsDbInstance2' cmsDbJdbcConnectionString: Description: JDBC connection string for cms database - Value: !Join ['', ['jdbc:mysql://', !GetAtt 'CmsDatabaseCluster.Endpoint.Address', ':', + Value: !Join ['', ['jdbc:mysql://', !Ref 'DatabaseCnameRecordSet', ':', !GetAtt 'CmsDatabaseCluster.Endpoint.Port', /, !Ref 'cmsDbName', '?useUnicode=true&characterEncoding=utf8&useLegacyDatetimeCode=false&serverTimezone=UTC']] Parameters: cmsDbInstanceAz1: @@ -57,15 +57,27 @@ Parameters: vpcSubnetIdForAz3: Description: The subnet for the third availability zone Type: String + snapshotIdentifier: + Description: If supplied the database cluster will be created with this snapshot, if supplied on an update then a new RDS cluster will be created and replace the existing RDS Cluster + Type: String + Default: '' + vpcInternalBaseDomainName: + Description: The base domain name for the internal hosted zone for the VPC that the database will be accessed from + Type: String + vpcInternalHostedZoneId: + Description: The hosted zone id for the interal hosted zone for the VPC that the database will be accessed from + Type: String Resources: CmsDatabaseCluster: + Type: AWS::RDS::DBCluster Properties: + SnapshotIdentifier: !If [HasSnapshotIdentifier, !Ref 'snapshotIdentifier', !Ref 'AWS::NoValue'] BackupRetentionPeriod: 14 DatabaseName: !Ref 'cmsDbName' DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' Engine: aurora MasterUserPassword: !Ref 'cmsDbMasterPassword' - MasterUsername: !Ref 'cmsDbMasterUsername' + MasterUsername: !If [HasSnapshotIdentifier, !Ref 'AWS::NoValue', !Ref 'cmsDbMasterUsername'] Port: Fn::ImportValue: !Sub "${sgStackName}-cmsDbPort" PreferredBackupWindow: 13:14-13:44 @@ -73,11 +85,11 @@ Resources: StorageEncrypted: 'true' VpcSecurityGroupIds: - Fn::ImportValue: !Sub "${sgStackName}-cmsDbSgId" - Type: AWS::RDS::DBCluster # Create two DB instances one primary/write instance, and the other a read replica. # The Aurora CloudFormation does not let you choose which is read or write. # Aurora decides automatically for you behind the scenes. CmsDbInstance1: + Type: AWS::RDS::DBInstance Properties: DBClusterIdentifier: !Ref CmsDatabaseCluster DBInstanceClass: !Ref 'cmsDbInstanceClass' @@ -85,8 +97,8 @@ Resources: DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' Engine: aurora PubliclyAccessible: 'false' - Type: AWS::RDS::DBInstance CmsDbInstance2: + Type: AWS::RDS::DBInstance Properties: DBClusterIdentifier: !Ref CmsDatabaseCluster DBInstanceClass: !Ref 'cmsDbInstanceClass' @@ -94,21 +106,29 @@ Resources: DBSubnetGroupName: !Ref 'CmsDatabaseSubnetGroup' Engine: aurora PubliclyAccessible: 'false' - Type: AWS::RDS::DBInstance CmsDatabaseParamGroup: + Type: AWS::RDS::DBParameterGroup Properties: Description: Default parameters for the cms DB Family: aurora5.6 Parameters: log_output: TABLE slow_query_log: 1 - Type: AWS::RDS::DBParameterGroup CmsDatabaseSubnetGroup: + Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: DB Subnet Group for management DB SubnetIds: - Ref: 'vpcSubnetIdForAz1' - Ref: 'vpcSubnetIdForAz2' - Ref: 'vpcSubnetIdForAz3' - Type: AWS::RDS::DBSubnetGroup - \ No newline at end of file + DatabaseCnameRecordSet: + Type: AWS::Route53::RecordSet + Properties: + HostedZoneId: + Ref: 'vpcInternalHostedZoneId' + Name: !Join [., ['rds', !Ref 'vpcInternalBaseDomainName']] + ResourceRecords: + - !GetAtt 'CmsDatabaseCluster.Endpoint.Address' + TTL: 30 + Type: CNAME diff --git a/src/main/resources/cloudformation/vpc.yaml b/src/main/resources/cloudformation/vpc.yaml index 005b2f9b..5f684730 100644 --- a/src/main/resources/cloudformation/vpc.yaml +++ b/src/main/resources/cloudformation/vpc.yaml @@ -24,6 +24,12 @@ Outputs: vpcId: Description: ID of the created VPC Value: !Ref 'CerberusVpc' + vpcInternalBaseDomainName: + Description: The base domain name for the internal hosted zone for this VPC + Value: !Join [., ['internal', !Ref 'AWS::Region', !Ref 'environmentName', 'cerberus.com', '']] + vpcInternalHostedZoneId: + Description: The hosted zone id for the interal hosted zone for this VPC + Value: !Ref 'InternalHostedZone' Parameters: az1: AllowedPattern: '[a-z]{2}-[a-z]+-\d\w' @@ -72,6 +78,9 @@ Parameters: MaxLength: '20' MinLength: '9' Type: String + environmentName: + Description: The Cerberus environment name + Type: String Resources: CerberusDhcpOptions: Properties: @@ -144,3 +153,12 @@ Resources: InternetGatewayId: !Ref 'CerberusInternetGateway' VpcId: !Ref 'CerberusVpc' Type: AWS::EC2::VPCGatewayAttachment + InternalHostedZone: # move to vpc stack + Type: 'AWS::Route53::HostedZone' + Properties: + HostedZoneConfig: + Comment: !Join [' ', ['Internal Hosted Zone for custom internal cname records for', !Ref 'environmentName']] + Name: !Join [., ['internal', !Ref 'AWS::Region', !Ref 'environmentName', 'cerberus.com', '']] + VPCs: + - VPCId: !Ref 'CerberusVpc' + VPCRegion: !Ref 'AWS::Region' diff --git a/src/test/resources/environment.yaml b/src/test/resources/environment.yaml index 47f57ae1..f68fd295 100644 --- a/src/test/resources/environment.yaml +++ b/src/test/resources/environment.yaml @@ -126,6 +126,8 @@ region-specific-configuration: primary: true rds: size: db.r3.large + # To create a new environment from a rds backup snapshot uncomment below and supply the db cluster snapshot identifier + # db-cluster-identifier: foo-bar management-service: # The ami id to use for CMS, see https://github.com/Nike-Inc/cerberus-util-scripts to bake an AMI ami-id: ami-3333 From 69b78acc26840151faaa4b56924ea31c06daa3ce Mon Sep 17 00:00:00 2001 From: Justin Field Date: Tue, 23 Jan 2018 11:54:42 -0800 Subject: [PATCH 69/69] rev version for highlander release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b23ed3fd..00730170 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,4 @@ group=com.nike artifactId=cerberus-lifecycle-cli -version=HIGHLANDER +version=4.0.0