Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for AWS Secrets Manager #947

Merged
merged 28 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d743f15
[maven-release-plugin] prepare release tessera-0.10.1
chris-j-h Oct 24, 2019
399c1ee
Initial implementation of AWS Secrets Manager support
Nov 4, 2019
d5ecb6c
Merge branch 'master' of https://github.com/vgeorgescu/tessera into f…
Nov 4, 2019
f934343
Build runs successfully
Nov 5, 2019
1e7c7af
AWS Secrets Manager implementation for KeyVault added
Nov 12, 2019
e2b0dd3
Merge branch 'master' of https://github.com/vgeorgescu/tessera into f…
Nov 12, 2019
3aeb497
Fixing pom.xml character
Nov 12, 2019
299ca47
updated version to 0.11-SNAPSHOT
Nov 12, 2019
716b7de
updated version to 0.11-SNAPSHOT
Nov 12, 2019
fe3ed2d
updated version to 0.11-SNAPSHOT
Nov 12, 2019
ff0773d
updated version to 0.11-SNAPSHOT
Nov 12, 2019
231db8f
removed AWSCURRRENT
Nov 13, 2019
be105b3
Merge pull request #2 from vgeorgescu/feature/AWSSecretsManagerDone
vgeorgescu Nov 13, 2019
76551fa
Merge branch 'master' of https://github.com/jpmorganchase/tessera int…
Nov 22, 2019
5fb0b7d
tessera/upstream/master update
Nov 25, 2019
44deb87
Merge branch 'master' into feature/AWSSecretManager
Nov 26, 2019
26f5ea2
rearrange code
Nov 26, 2019
96fc164
rearrange code
Nov 26, 2019
783b86f
rearrange code
Nov 26, 2019
f85f8b6
Update config/src/main/java/com/quorum/tessera/config/keypairs/AWSKey…
Emi14 Dec 4, 2019
ad43a85
Update config/src/main/java/com/quorum/tessera/config/keypairs/AWSKey…
Emi14 Dec 4, 2019
9788674
resolved PR conversations
Dec 4, 2019
d178718
calling validator.validate() for AWSKeyVaultConfig
Dec 4, 2019
0bd3aa6
added acceptance tests for AWS Vault
Dec 9, 2019
1b0c090
Merge branches 'feature/AWSSecretManager' and 'master' of https://git…
Dec 12, 2019
37fc957
added gradle files for AWS Secrets Manager Key Vault implementation
Dec 13, 2019
72c9880
Upstream updates to AWS Secrets Manager change (#1)
chris-j-h Jan 8, 2020
82a312c
Merge branch 'master' into feature/AWSSecretsManager2
chris-j-h Jan 8, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ private Optional<KeyVaultConfig> keyVaultConfig() {
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
} else {
} else if (vaultType.equals(KeyVaultType.HASHICORP)) {
if (Objects.isNull(keyOut) || keyOut.isEmpty()) {
throw new CliException(
"At least one -filename must be provided when saving generated keys in a Hashicorp Vault");
Expand All @@ -171,6 +171,21 @@ private Optional<KeyVaultConfig> keyVaultConfig() {
Set<ConstraintViolation<HashicorpKeyVaultConfig>> violations =
validator.validate((HashicorpKeyVaultConfig) keyVaultConfig);

if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
} else {
DefaultKeyVaultConfig awsKeyVaultConfig = new DefaultKeyVaultConfig();
awsKeyVaultConfig.setKeyVaultType(KeyVaultType.AWS);

if (Objects.nonNull(vaultUrl)) {
awsKeyVaultConfig.setProperty("endpoint", vaultUrl);
}

keyVaultConfig = awsKeyVaultConfig;

Set<ConstraintViolation<DefaultKeyVaultConfig>> violations = validator.validate(awsKeyVaultConfig);

if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ public void invalidHashicorpKeyVaultConfigThrowsException() {
}

@Test
public void hashicorpTlsPathsDontExistThrowsException() throws Exception {
public void hashicorpTlsPathsDontExistThrowsException() {
final String vaultUrl = "someurl";
final String approlePath = "someapprole";
final Path nonExistentPath = Paths.get(UUID.randomUUID().toString());
Expand Down Expand Up @@ -479,6 +479,88 @@ public void hashicorpTlsPathsDontExistThrowsException() throws Exception {
verifyNoMoreInteractions(encryptorOptions, keyGenerator);
}

@Test
public void validAWSKeyVaultConfig() {
String endpointUrl = "https://someurl.com";

final DefaultKeyVaultConfig keyVaultConfig = new DefaultKeyVaultConfig();
keyVaultConfig.setKeyVaultType(KeyVaultType.AWS);
keyVaultConfig.setProperty("endpoint", endpointUrl);

final EncryptorOptions encryptorOptions = mock(EncryptorOptions.class);
when(encryptorOptions.parseEncryptorConfig()).thenReturn(null);

command.encryptorOptions = encryptorOptions;
command.vaultType = KeyVaultType.AWS;
command.vaultUrl = endpointUrl;

final KeyGenerator keyGenerator = mock(KeyGenerator.class);
when(keyGeneratorFactory.create(any(), any())).thenReturn(keyGenerator);

CliResult result = command.call();

// verify the correct config is used
verify(keyGeneratorFactory).create(refEq(keyVaultConfig), isNull());
assertThat(result).isEqualToComparingFieldByField(wantResult);

verify(keyGenerator).generate(anyString(), any(), any());
verify(encryptorOptions).parseEncryptorConfig();
verifyNoMoreInteractions(encryptorOptions, keyGenerator);
}

@Test
public void validAWSKeyVaultConfigNoVaultUrl() {
final DefaultKeyVaultConfig keyVaultConfig = new DefaultKeyVaultConfig();
keyVaultConfig.setKeyVaultType(KeyVaultType.AWS);

final EncryptorOptions encryptorOptions = mock(EncryptorOptions.class);
when(encryptorOptions.parseEncryptorConfig()).thenReturn(null);

command.encryptorOptions = encryptorOptions;
command.vaultType = KeyVaultType.AWS;

final KeyGenerator keyGenerator = mock(KeyGenerator.class);
when(keyGeneratorFactory.create(any(), any())).thenReturn(keyGenerator);

CliResult result = command.call();

// verify the correct config is used
verify(keyGeneratorFactory).create(refEq(keyVaultConfig), isNull());
assertThat(result).isEqualToComparingFieldByField(wantResult);

verify(keyGenerator).generate(anyString(), any(), any());
verify(encryptorOptions).parseEncryptorConfig();
verifyNoMoreInteractions(encryptorOptions, keyGenerator);
}

@Test
public void invalidAWSKeyVaultConfigThrowsException() {
final EncryptorOptions encryptorOptions = mock(EncryptorOptions.class);
when(encryptorOptions.parseEncryptorConfig()).thenReturn(null);

command.encryptorOptions = encryptorOptions;
command.vaultType = KeyVaultType.AWS;
command.vaultUrl = "not a valid url";

final KeyGenerator keyGenerator = mock(KeyGenerator.class);
when(keyGeneratorFactory.create(any(), any())).thenReturn(keyGenerator);

Throwable ex = catchThrowable(() -> command.call());

assertThat(ex).isInstanceOf(ConstraintViolationException.class);

Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) ex).getConstraintViolations();

assertThat(violations.size()).isEqualTo(1);

ConstraintViolation violation = violations.iterator().next();

assertThat(violation.getMessage()).isEqualTo("must be a valid AWS service endpoint URL with scheme");

verify(encryptorOptions).parseEncryptorConfig();
verifyNoMoreInteractions(encryptorOptions);
}

@Test
public void vaultUrlButNoVaultTypeThrowsException() {
final EncryptorOptions encryptorOptions = mock(EncryptorOptions.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ public void ifPublicAndPrivateKeyListAreEmptyThenKeyConfigurationIsAllNulls() th
@Test
public void ifPublicKeyListIsEmptyThenKeyConfigurationIsAllNulls() throws IOException {
try (InputStream configData = getClass().getResourceAsStream("/sample-with-only-private-keys.conf")) {
final Throwable throwable = catchThrowable(() -> tomlConfigFactory.createKeyDataBuilder(configData).build());
final Throwable throwable =
catchThrowable(() -> tomlConfigFactory.createKeyDataBuilder(configData).build());

assertThat(throwable).isInstanceOf(ConfigException.class).hasCauseExactlyInstanceOf(RuntimeException.class);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.quorum.tessera.config;

import com.quorum.tessera.config.adapters.MapAdapter;
import com.quorum.tessera.config.constraints.ValidKeyVaultConfig;

import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.*;
Expand All @@ -9,13 +10,12 @@
import java.util.HashMap;
import java.util.Map;

@ValidKeyVaultConfig
@XmlType(name = "keyVaultConfig")
@XmlAccessorType(XmlAccessType.FIELD)
public class DefaultKeyVaultConfig extends ConfigItem implements KeyVaultConfig {

@NotNull
@XmlAttribute
private KeyVaultType keyVaultType;
@NotNull @XmlAttribute private KeyVaultType keyVaultType;

@XmlJavaTypeAdapter(MapAdapter.class)
@XmlElement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.quorum.tessera.config.adapters.KeyDataAdapter;
import com.quorum.tessera.config.adapters.PathAdapter;
import com.quorum.tessera.config.constraints.ValidKeyVaultConfig;
import com.quorum.tessera.config.constraints.ValidPath;
import com.quorum.tessera.config.keypairs.ConfigKeyPair;

Expand Down Expand Up @@ -36,7 +35,7 @@ public class KeyConfiguration extends ConfigItem {
@XmlJavaTypeAdapter(KeyDataAdapter.class)
private List<@Valid ConfigKeyPair> keyData;

@ValidKeyVaultConfig @XmlElement private DefaultKeyVaultConfig keyVaultConfig;
@Valid @XmlElement private DefaultKeyVaultConfig keyVaultConfig;

@Valid @XmlElement private AzureKeyVaultConfig azureKeyVaultConfig;

Expand All @@ -53,6 +52,7 @@ public KeyConfiguration(
this.keyData = keyData;
this.azureKeyVaultConfig = azureKeyVaultConfig;
this.hashicorpKeyVaultConfig = hashicorpKeyVaultConfig;

if (null != azureKeyVaultConfig) {
this.keyVaultConfig = KeyVaultConfigConverter.convert(azureKeyVaultConfig);
} else if (null != hashicorpKeyVaultConfig) {
Expand Down
25 changes: 24 additions & 1 deletion config/src/main/java/com/quorum/tessera/config/KeyData.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ public class KeyData extends ConfigItem {
@XmlElement
private String hashicorpVaultSecretVersion;

public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String azureVaultPublicKeyVersion, String azureVaultPrivateKeyVersion, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName, String hashicorpVaultSecretVersion) {
@XmlElement
private String awsSecretsManagerPublicKeyId;

@XmlElement
private String awsSecretsManagerPrivateKeyId;

public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path privateKeyPath, Path publicKeyPath, String azureVaultPublicKeyId, String azureVaultPrivateKeyId, String azureVaultPublicKeyVersion, String azureVaultPrivateKeyVersion, String hashicorpVaultPublicKeyId, String hashicorpVaultPrivateKeyId, String hashicorpVaultSecretEngineName, String hashicorpVaultSecretName, String hashicorpVaultSecretVersion, String awsSecretsManagerPublicKeyId, String awsSecretsManagerPrivateKeyId) {
this.config = config;
this.privateKey = privateKey;
this.publicKey = publicKey;
Expand All @@ -73,6 +79,8 @@ public KeyData(KeyDataConfig config, String privateKey, String publicKey, Path p
this.hashicorpVaultSecretEngineName = hashicorpVaultSecretEngineName;
this.hashicorpVaultSecretName = hashicorpVaultSecretName;
this.hashicorpVaultSecretVersion = hashicorpVaultSecretVersion;
this.awsSecretsManagerPublicKeyId = awsSecretsManagerPublicKeyId;
this.awsSecretsManagerPrivateKeyId = awsSecretsManagerPrivateKeyId;
}

public KeyData() {
Expand Down Expand Up @@ -135,6 +143,14 @@ public String getHashicorpVaultSecretVersion() {
return hashicorpVaultSecretVersion;
}

public String getAwsSecretsManagerPublicKeyId() {
return awsSecretsManagerPublicKeyId;
}

public String getAwsSecretsManagerPrivateKeyId() {
return awsSecretsManagerPrivateKeyId;
}

public void setConfig(KeyDataConfig config) {
this.config = config;
}
Expand Down Expand Up @@ -191,4 +207,11 @@ public void setHashicorpVaultSecretVersion(String hashicorpVaultSecretVersion) {
this.hashicorpVaultSecretVersion = hashicorpVaultSecretVersion;
}

public void setAwsSecretsManagerPublicKeyId(String awsSecretsManagerPublicKeyId) {
this.awsSecretsManagerPublicKeyId = awsSecretsManagerPublicKeyId;
}

public void setAwsSecretsManagerPrivateKeyId(String awsSecretsManagerPrivateKeyId) {
this.awsSecretsManagerPrivateKeyId = awsSecretsManagerPrivateKeyId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,22 @@ static DefaultKeyVaultConfig convert(AzureKeyVaultConfig azureKeyVaultConfig) {
static DefaultKeyVaultConfig convert(HashicorpKeyVaultConfig hashicorpKeyVaultConfig) {
DefaultKeyVaultConfig config = new DefaultKeyVaultConfig();
config.setKeyVaultType(hashicorpKeyVaultConfig.getKeyVaultType());
config.setProperty("url",hashicorpKeyVaultConfig.getUrl());
config.setProperty("approlePath",hashicorpKeyVaultConfig.getApprolePath());
config.setProperty("url", hashicorpKeyVaultConfig.getUrl());
config.setProperty("approlePath", hashicorpKeyVaultConfig.getApprolePath());

Optional.ofNullable(hashicorpKeyVaultConfig.getTlsKeyStorePath())
.map(Objects::toString)
.ifPresent(v -> {
config.setProperty("tlsKeyStorePath",v);
});
.map(Objects::toString)
.ifPresent(
v -> {
config.setProperty("tlsKeyStorePath", v);
});

Optional.ofNullable(hashicorpKeyVaultConfig.getTlsTrustStorePath())
.map(Objects::toString)
.ifPresent(v -> {
config.setProperty("tlsTrustStorePath",v);
});
.map(Objects::toString)
.ifPresent(
v -> {
config.setProperty("tlsTrustStorePath", v);
});

return config;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.quorum.tessera.config;

public enum KeyVaultType {
AZURE, HASHICORP
AZURE, HASHICORP, AWS
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@ public ConfigKeyPair unmarshal(final KeyData keyData) {
keyData.getHashicorpVaultSecretVersion());
}

// case 5, the keys are provided inside a file
// case 5, the AWS Secrets Manager data is provided
if (keyData.getAwsSecretsManagerPublicKeyId() != null && keyData.getAwsSecretsManagerPrivateKeyId() != null) {
return new AWSKeyPair(
keyData.getAwsSecretsManagerPublicKeyId(), keyData.getAwsSecretsManagerPrivateKeyId());
}

// case 6, the keys are provided inside a file
if (keyData.getPublicKeyPath() != null && keyData.getPrivateKeyPath() != null) {
final KeyEncryptor keyEncryptor = keyEncryptorHolder.getKeyEncryptor().get();
return new FilesystemKeyPair(keyData.getPublicKeyPath(), keyData.getPrivateKeyPath(), keyEncryptor);
}

// case 6, the key config specified is invalid
// case 7, the key config specified is invalid
return new UnsupportedKeyPair(
keyData.getConfig(),
keyData.getPrivateKey(),
Expand All @@ -80,7 +86,9 @@ public ConfigKeyPair unmarshal(final KeyData keyData) {
keyData.getHashicorpVaultPrivateKeyId(),
keyData.getHashicorpVaultSecretEngineName(),
keyData.getHashicorpVaultSecretName(),
keyData.getHashicorpVaultSecretVersion());
keyData.getHashicorpVaultSecretVersion(),
keyData.getAwsSecretsManagerPublicKeyId(),
keyData.getAwsSecretsManagerPrivateKeyId());
}

@Override
Expand Down Expand Up @@ -128,6 +136,14 @@ public KeyData marshal(final ConfigKeyPair keyPair) {
return keyData;
}

if (keyPair instanceof AWSKeyPair) {
AWSKeyPair kp = (AWSKeyPair) keyPair;

keyData.setAwsSecretsManagerPublicKeyId(kp.getPublicKeyId());
keyData.setAwsSecretsManagerPrivateKeyId(kp.getPrivateKeyId());
return keyData;
}

if (keyPair instanceof FilesystemKeyPair) {
FilesystemKeyPair kp = (FilesystemKeyPair) keyPair;

Expand All @@ -152,7 +168,9 @@ public KeyData marshal(final ConfigKeyPair keyPair) {
kp.getHashicorpVaultPublicKeyId(),
kp.getHashicorpVaultSecretEngineName(),
kp.getHashicorpVaultSecretName(),
kp.getHashicorpVaultSecretVersion());
kp.getHashicorpVaultSecretVersion(),
kp.getAwsSecretsManagerPublicKeyId(),
kp.getAwsSecretsManagerPrivateKeyId());
}

throw new UnsupportedOperationException("The keypair type " + keyPair.getClass() + " is not allowed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ public boolean isValid(
}
}

if (keyVaultType == KeyVaultType.AWS) {

if (keyVaultConfig.getProperties().containsKey("endpoint")) {
if (!keyVaultConfig.getProperties().get("endpoint").matches("^https?://.+$")) {
constraintValidatorContext.disableDefaultConstraintViolation();
constraintValidatorContext
.buildConstraintViolationWithTemplate("must be a valid AWS service endpoint URL with scheme")
.addConstraintViolation();
outcomes.add(Boolean.FALSE);
}
}
}

return outcomes.stream().allMatch(Boolean::booleanValue);
}
}
Loading