Skip to content

Commit

Permalink
Add property to disable adding invalid prefix when decryption fails (#…
Browse files Browse the repository at this point in the history
…2708)

Fixes #2632

Signed-off-by: Ryan Baxter <ryan.baxter@broadcom.com>
  • Loading branch information
ryanjbaxter authored Jan 15, 2025
1 parent 84fafcd commit 2d88fa0
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 6 deletions.
73 changes: 73 additions & 0 deletions docs/modules/ROOT/pages/server/encryption-and-decryption.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -44,6 +45,9 @@
@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)
Expand All @@ -60,7 +64,9 @@ public EnvironmentEncryptor environmentEncryptor(@Autowired(required = false) Te
if (locator == null) {
locator = new SingleTextEncryptorLocator(encryptor);
}
return new CipherEnvironmentEncryptor(locator);
CipherEnvironmentEncryptor environmentEncryptor = new CipherEnvironmentEncryptor(locator);
environmentEncryptor.setPrefixInvalidProperties(prefixInvalidProperties);
return environmentEncryptor;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,10 +35,16 @@
@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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class CipherEnvironmentEncryptor implements EnvironmentEncryptor {

private final TextEncryptorLocator encryptor;

private boolean prefixInvalidProperties = true;

private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();

@Autowired
Expand Down Expand Up @@ -74,8 +76,10 @@ private Environment decrypt(Environment environment, TextEncryptorLocator encryp
.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()) {
Expand All @@ -93,4 +97,8 @@ else if (logger.isWarnEnabled()) {
return result;
}

public void setPrefixInvalidProperties(boolean prefixInvalidProperties) {
this.prefixInvalidProperties = prefixInvalidProperties;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class VaultEnvironmentEncryptor implements EnvironmentEncryptor {

private final VaultKeyValueOperations keyValueTemplate;

private boolean prefixInvalidProperties = true;

public VaultEnvironmentEncryptor(VaultKeyValueOperations keyValueTemplate) {
this.keyValueTemplate = keyValueTemplate;
}
Expand Down Expand Up @@ -102,8 +104,10 @@ public Environment decrypt(Environment environment) {
}
}
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()) {
Expand All @@ -121,4 +125,8 @@ else if (logger.isWarnEnabled()) {
return result;
}

public void setPrefixInvalidProperties(boolean prefixInvalidProperties) {
this.prefixInvalidProperties = prefixInvalidProperties;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,55 @@ public void shouldDecryptEnvironmentIncludeOrigin(String salt, String key) {
.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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,31 @@ public void shouldMarkAsInvalidPropertyWithNoKeyValue() {
.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
Expand Down

0 comments on commit 2d88fa0

Please sign in to comment.