Add property to disable adding invalid prefix when decryption fails (#2708)
Fixes #2632 Signed-off-by: Ryan Baxter <ryan.baxter@broadcom.com>
This commit is contained in:
@@ -77,3 +77,76 @@ AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
|
||||
|
||||
NOTE: The `--key` argument is mandatory (despite having a `--` prefix).
|
||||
|
||||
== Decryption Errors
|
||||
|
||||
When the config server fails to decrypt a value it will create an `invalid` property in the HTTP response.
|
||||
|
||||
For example
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"label": null,
|
||||
"name": "application",
|
||||
"profiles": [
|
||||
"prd"
|
||||
],
|
||||
"propertySources": [
|
||||
{
|
||||
"name": "file:/demo/configserver/application-prd.yaml",
|
||||
"source": {
|
||||
"invalid.SharedPassword": "<n/a>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "file:/demo/configserver/application.yaml",
|
||||
"source": {
|
||||
"SharedPassword": "Fill_me_in"
|
||||
}
|
||||
}
|
||||
],
|
||||
"state": null,
|
||||
"version": null
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
In the example above the config server could not decrypt the value of `SharedPassword` in `application-prd.yaml`
|
||||
so the config server prefixed the property name with `invalid`.
|
||||
|
||||
If this response was received by the Config Client and then added to the app's `Environment` and the client
|
||||
requested the value of `SharedPassword` it would get `Fill_me_in`.
|
||||
|
||||
If you do not want the config server to prefix properties it can't decrypt wit `invalid` then you can set
|
||||
`spring.cloud.config.server.encrypt.prefix-invalid-properties` to `false`. If you do this then the same response from
|
||||
the config server would look like this:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
"label": null,
|
||||
"name": "application",
|
||||
"profiles": [
|
||||
"prd"
|
||||
],
|
||||
"propertySources": [
|
||||
{
|
||||
"name": "file:/demo/configserver/application-prd.yaml",
|
||||
"source": {
|
||||
"SharedPassword": "AYBKlpcZpaR36OcRDQjNIQl6fmnddAQhetMw/uyTpnn5fDj+unJ9QOEbqiPc9fX0N+CC8i+EJiN6nlH9Xqu6sH1tX/P6zg1CIy+ct/1RWGNbmQ256jc6vQaXhiN8sA8Mr6QiqYnMoBd+Jni/Miir5G3a7G9MmjbEUASKJOhUlIFKqL1IqB81RBT/cv0bg9kAiy5VBF1WppxP/PwtjECzbeUi2Y1jbpYb98rnc/qmRO3ZJam9fDNcPpW09qGFhGgJIujca257F7G4guS2w/7haVzNoyRiwHzZ14oL8AIxHLMBSJJF19ULlsMAkROj9o9TnwhL9r4rX9sAWk28c5eq77+iVpmlT3yoRdZqvMqffzKiibDlzz95Gmms7V7mctxrhNVOOWTwMSJvk94Y9ZPenljKgPJIV3Z1cqqx+W8JxFFeelOuYvMEe4bOVBh1TepGzzdWVdYbylgXJy35uRTZ2drybUe5+jc0hiAuujHz0zdY1FwOHfwzSsSidlYn4syPeuytnxTzn7fbWXeXetTTtDlmLRf8MBSzXzDFWNH0cNGOCQ=="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "file:/demo/configserver/application.yaml",
|
||||
"source": {
|
||||
"SharedPassword": "Fill_me_in"
|
||||
}
|
||||
}
|
||||
],
|
||||
"state": null,
|
||||
"version": null
|
||||
}
|
||||
----
|
||||
|
||||
In this case if the config client were to receive the above response and requested that value
|
||||
of `SharedPassword` from the `Environment` it would get the encrypted value back instead of
|
||||
`Fill_me_in`.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.config.server.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -44,6 +45,9 @@ import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
@AutoConfigureAfter(DefaultTextEncryptionAutoConfiguration.class)
|
||||
public class EncryptionAutoConfiguration {
|
||||
|
||||
@Value("${spring.cloud.config.server.encrypt.prefixInvalidProperties:${spring.cloud.config.server.encrypt.prefix-invalid-properties:true}}")
|
||||
private boolean prefixInvalidProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(TextEncryptor.class)
|
||||
@ConditionalOnMissingBean(TextEncryptorLocator.class)
|
||||
@@ -60,7 +64,9 @@ public class EncryptionAutoConfiguration {
|
||||
if (locator == null) {
|
||||
locator = new SingleTextEncryptorLocator(encryptor);
|
||||
}
|
||||
return new CipherEnvironmentEncryptor(locator);
|
||||
CipherEnvironmentEncryptor environmentEncryptor = new CipherEnvironmentEncryptor(locator);
|
||||
environmentEncryptor.setPrefixInvalidProperties(prefixInvalidProperties);
|
||||
return environmentEncryptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.config.server.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.cloud.config.server.encryption.vault.VaultEnvironmentEncryptor;
|
||||
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepository;
|
||||
@@ -34,10 +35,16 @@ import org.springframework.vault.core.VaultTemplate;
|
||||
@Profile("vault")
|
||||
public class VaultEncryptionAutoConfiguration {
|
||||
|
||||
@Value("${spring.cloud.config.server.encrypt.prefixInvalidProperties:${spring.cloud.config.server.encrypt.prefix-invalid-properties:true}}")
|
||||
private boolean prefixInvalidProperties;
|
||||
|
||||
@Bean
|
||||
public VaultEnvironmentEncryptor vaultEnvironmentEncryptor(
|
||||
SpringVaultEnvironmentRepository vaultEnvironmentRepository) {
|
||||
return new VaultEnvironmentEncryptor(vaultEnvironmentRepository.getKeyValueTemplate());
|
||||
VaultEnvironmentEncryptor vaultEnvironmentEncryptor = new VaultEnvironmentEncryptor(
|
||||
vaultEnvironmentRepository.getKeyValueTemplate());
|
||||
vaultEnvironmentEncryptor.setPrefixInvalidProperties(this.prefixInvalidProperties);
|
||||
return vaultEnvironmentEncryptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ public class CipherEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
|
||||
private final TextEncryptorLocator encryptor;
|
||||
|
||||
private boolean prefixInvalidProperties = true;
|
||||
|
||||
private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();
|
||||
|
||||
@Autowired
|
||||
@@ -74,8 +76,10 @@ public class CipherEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
.decrypt(this.helper.stripPrefix(value));
|
||||
}
|
||||
catch (Exception e) {
|
||||
value = "<n/a>";
|
||||
name = "invalid." + name;
|
||||
if (this.prefixInvalidProperties) {
|
||||
value = "<n/a>";
|
||||
name = "invalid." + name;
|
||||
}
|
||||
String message = "Cannot decrypt key: " + key + " (" + e.getClass() + ": " + e.getMessage()
|
||||
+ ")";
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -93,4 +97,8 @@ public class CipherEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPrefixInvalidProperties(boolean prefixInvalidProperties) {
|
||||
this.prefixInvalidProperties = prefixInvalidProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ public class VaultEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
|
||||
private final VaultKeyValueOperations keyValueTemplate;
|
||||
|
||||
private boolean prefixInvalidProperties = true;
|
||||
|
||||
public VaultEnvironmentEncryptor(VaultKeyValueOperations keyValueTemplate) {
|
||||
this.keyValueTemplate = keyValueTemplate;
|
||||
}
|
||||
@@ -102,8 +104,10 @@ public class VaultEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
value = "<n/a>";
|
||||
name = "invalid." + name;
|
||||
if (this.prefixInvalidProperties) {
|
||||
value = "<n/a>";
|
||||
name = "invalid." + name;
|
||||
}
|
||||
String message = "Cannot resolve key: " + key + " (" + e.getClass() + ": " + e.getMessage()
|
||||
+ ")";
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -121,4 +125,8 @@ public class VaultEnvironmentEncryptor implements EnvironmentEncryptor {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setPrefixInvalidProperties(boolean prefixInvalidProperties) {
|
||||
this.prefixInvalidProperties = prefixInvalidProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,4 +119,55 @@ public class CipherEnvironmentEncryptorTests {
|
||||
.isEqualTo(secret);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("params")
|
||||
public void shouldDecryptFailed(String salt, String key) {
|
||||
TextEncryptor textEncryptor = new EncryptorFactory(salt).create(key);
|
||||
CipherEnvironmentEncryptor encryptor = new CipherEnvironmentEncryptor(keys -> textEncryptor);
|
||||
encryptor.setPrefixInvalidProperties(true);
|
||||
// given
|
||||
String secret = randomUUID().toString();
|
||||
|
||||
// when
|
||||
Environment environment = new Environment("name", "profile", "label");
|
||||
String encrypted = "{cipher}" + new EncryptorFactory(salt).create("dummykey").encrypt(secret);
|
||||
environment.add(new PropertySource("a", Collections.<Object, Object>singletonMap(environment.getName(),
|
||||
new PropertyValueDescriptor(encrypted, "encrypted value"))));
|
||||
|
||||
// then
|
||||
assertThat(encryptor.decrypt(environment)
|
||||
.getPropertySources()
|
||||
.get(0)
|
||||
.getSource()
|
||||
.get("invalid." + environment.getName())).isEqualTo("<n/a>");
|
||||
assertThat(encryptor.decrypt(environment).getPropertySources().get(0).getSource().get(environment.getName()))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("params")
|
||||
public void decryptFailedWithoutInvalidPrefix(String salt, String key) {
|
||||
TextEncryptor textEncryptor = new EncryptorFactory(salt).create(key);
|
||||
CipherEnvironmentEncryptor encryptor = new CipherEnvironmentEncryptor(keys -> textEncryptor);
|
||||
encryptor.setPrefixInvalidProperties(false);
|
||||
// given
|
||||
String secret = randomUUID().toString();
|
||||
|
||||
// when
|
||||
Environment environment = new Environment("name", "profile", "label");
|
||||
String encryptedSecret = new EncryptorFactory(salt).create("dummykey").encrypt(secret);
|
||||
String encrypted = "{cipher}" + encryptedSecret;
|
||||
environment.add(new PropertySource("a", Collections.<Object, Object>singletonMap(environment.getName(),
|
||||
new PropertyValueDescriptor(encrypted, "encrypted value"))));
|
||||
|
||||
// then
|
||||
assertThat(encryptor.decrypt(environment)
|
||||
.getPropertySources()
|
||||
.get(0)
|
||||
.getSource()
|
||||
.get("invalid." + environment.getName())).isNull();
|
||||
assertThat(encryptor.decrypt(environment).getPropertySources().get(0).getSource().get(environment.getName()))
|
||||
.isEqualTo(encryptedSecret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -117,6 +117,31 @@ public class VaultEnvironmentEncryptorTests {
|
||||
.isEqualTo("<n/a>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotPrefixInvalidPropertyWithNoKeyValue() {
|
||||
// given
|
||||
String accounts = "accounts/mypay";
|
||||
String value = "{vault}:" + accounts;
|
||||
|
||||
VaultKeyValueOperations keyValueTemplate = mock(VaultKeyValueOperations.class);
|
||||
|
||||
VaultEnvironmentEncryptor encryptor = new VaultEnvironmentEncryptor(keyValueTemplate);
|
||||
encryptor.setPrefixInvalidProperties(false);
|
||||
|
||||
// when
|
||||
Environment environment = new Environment("name", "profile", "label");
|
||||
environment
|
||||
.add(new PropertySource("a", Collections.<Object, Object>singletonMap(environment.getName(), value)));
|
||||
|
||||
// then
|
||||
Environment processedEnvironment = encryptor.decrypt(environment);
|
||||
|
||||
assertThat(processedEnvironment.getPropertySources().get(0).getSource().get("invalid." + environment.getName()))
|
||||
.isNull();
|
||||
assertThat(processedEnvironment.getPropertySources().get(0).getSource().get(environment.getName()))
|
||||
.isEqualTo(accounts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMarkAsInvalidPropertyWithNoEmptyValue() {
|
||||
// given
|
||||
|
||||
Reference in New Issue
Block a user