Skip to content

Commit 8ceab26

Browse files
committed
Add property to disable adding invalid prefix when decryption fails
Fixes #2632 Signed-off-by: Ryan Baxter <[email protected]>
1 parent 84fafcd commit 8ceab26

File tree

7 files changed

+184
-6
lines changed

7 files changed

+184
-6
lines changed

docs/modules/ROOT/pages/server/encryption-and-decryption.adoc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,76 @@ AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
7777

7878
NOTE: The `--key` argument is mandatory (despite having a `--` prefix).
7979

80+
== Decryption Errors
81+
82+
When the config server fails to decrypt a value it will create an `invalid` property in the HTTP response.
83+
84+
For example
85+
86+
[source,json]
87+
----
88+
{
89+
"label": null,
90+
"name": "application",
91+
"profiles": [
92+
"prd"
93+
],
94+
"propertySources": [
95+
{
96+
"name": "file:/demo/configserver/application-prd.yaml",
97+
"source": {
98+
"invalid.SharedPassword": "<n/a>"
99+
}
100+
},
101+
{
102+
"name": "file:/demo/configserver/application.yaml",
103+
"source": {
104+
"SharedPassword": "Fill_me_in"
105+
}
106+
}
107+
],
108+
"state": null,
109+
"version": null
110+
}
111+
112+
----
113+
114+
In the example above the config server could not decrypt the value of `SharedPassword` in `application-prd.yaml`
115+
so the config server prefixed the property name with `invalid`.
116+
117+
If this response was received by the Config Client and then added to the app's `Environment` and the client
118+
requested the value of `SharedPassword` it would get `Fill_me_in`.
119+
120+
If you do not want the config server to prefix properties it can't decrypt wit `invalid` then you can set
121+
`spring.cloud.config.server.encrypt.prefix-invalid-properties` to `false`. If you do this then the same response from
122+
the config server would look like this:
123+
124+
[source,json]
125+
----
126+
"label": null,
127+
"name": "application",
128+
"profiles": [
129+
"prd"
130+
],
131+
"propertySources": [
132+
{
133+
"name": "file:/demo/configserver/application-prd.yaml",
134+
"source": {
135+
"SharedPassword": "AYBKlpcZpaR36OcRDQjNIQl6fmnddAQhetMw/uyTpnn5fDj+unJ9QOEbqiPc9fX0N+CC8i+EJiN6nlH9Xqu6sH1tX/P6zg1CIy+ct/1RWGNbmQ256jc6vQaXhiN8sA8Mr6QiqYnMoBd+Jni/Miir5G3a7G9MmjbEUASKJOhUlIFKqL1IqB81RBT/cv0bg9kAiy5VBF1WppxP/PwtjECzbeUi2Y1jbpYb98rnc/qmRO3ZJam9fDNcPpW09qGFhGgJIujca257F7G4guS2w/7haVzNoyRiwHzZ14oL8AIxHLMBSJJF19ULlsMAkROj9o9TnwhL9r4rX9sAWk28c5eq77+iVpmlT3yoRdZqvMqffzKiibDlzz95Gmms7V7mctxrhNVOOWTwMSJvk94Y9ZPenljKgPJIV3Z1cqqx+W8JxFFeelOuYvMEe4bOVBh1TepGzzdWVdYbylgXJy35uRTZ2drybUe5+jc0hiAuujHz0zdY1FwOHfwzSsSidlYn4syPeuytnxTzn7fbWXeXetTTtDlmLRf8MBSzXzDFWNH0cNGOCQ=="
136+
}
137+
},
138+
{
139+
"name": "file:/demo/configserver/application.yaml",
140+
"source": {
141+
"SharedPassword": "Fill_me_in"
142+
}
143+
}
144+
],
145+
"state": null,
146+
"version": null
147+
}
148+
----
149+
150+
In this case if the config client were to receive the above response and requested that value
151+
of `SharedPassword` from the `Environment` it would get the encrypted value back instead of
152+
`Fill_me_in`.

spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EncryptionAutoConfiguration.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.cloud.config.server.config;
1818

1919
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.beans.factory.annotation.Value;
2021
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2122
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -44,6 +45,9 @@
4445
@AutoConfigureAfter(DefaultTextEncryptionAutoConfiguration.class)
4546
public class EncryptionAutoConfiguration {
4647

48+
@Value("${spring.cloud.config.server.encrypt.prefixInvalidProperties:${spring.cloud.config.server.encrypt.prefix-invalid-properties:true}}")
49+
private boolean prefixInvalidProperties;
50+
4751
@Bean
4852
@ConditionalOnBean(TextEncryptor.class)
4953
@ConditionalOnMissingBean(TextEncryptorLocator.class)
@@ -60,7 +64,9 @@ public EnvironmentEncryptor environmentEncryptor(@Autowired(required = false) Te
6064
if (locator == null) {
6165
locator = new SingleTextEncryptorLocator(encryptor);
6266
}
63-
return new CipherEnvironmentEncryptor(locator);
67+
CipherEnvironmentEncryptor environmentEncryptor = new CipherEnvironmentEncryptor(locator);
68+
environmentEncryptor.setPrefixInvalidProperties(prefixInvalidProperties);
69+
return environmentEncryptor;
6470
}
6571

6672
}

spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/VaultEncryptionAutoConfiguration.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.cloud.config.server.config;
1818

19+
import org.springframework.beans.factory.annotation.Value;
1920
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2021
import org.springframework.cloud.config.server.encryption.vault.VaultEnvironmentEncryptor;
2122
import org.springframework.cloud.config.server.environment.vault.SpringVaultEnvironmentRepository;
@@ -34,10 +35,16 @@
3435
@Profile("vault")
3536
public class VaultEncryptionAutoConfiguration {
3637

38+
@Value("${spring.cloud.config.server.encrypt.prefixInvalidProperties:${spring.cloud.config.server.encrypt.prefix-invalid-properties:true}}")
39+
private boolean prefixInvalidProperties;
40+
3741
@Bean
3842
public VaultEnvironmentEncryptor vaultEnvironmentEncryptor(
3943
SpringVaultEnvironmentRepository vaultEnvironmentRepository) {
40-
return new VaultEnvironmentEncryptor(vaultEnvironmentRepository.getKeyValueTemplate());
44+
VaultEnvironmentEncryptor vaultEnvironmentEncryptor = new VaultEnvironmentEncryptor(
45+
vaultEnvironmentRepository.getKeyValueTemplate());
46+
vaultEnvironmentEncryptor.setPrefixInvalidProperties(this.prefixInvalidProperties);
47+
return vaultEnvironmentEncryptor;
4148
}
4249

4350
}

spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/encryption/CipherEnvironmentEncryptor.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class CipherEnvironmentEncryptor implements EnvironmentEncryptor {
4444

4545
private final TextEncryptorLocator encryptor;
4646

47+
private boolean prefixInvalidProperties = true;
48+
4749
private EnvironmentPrefixHelper helper = new EnvironmentPrefixHelper();
4850

4951
@Autowired
@@ -74,8 +76,10 @@ private Environment decrypt(Environment environment, TextEncryptorLocator encryp
7476
.decrypt(this.helper.stripPrefix(value));
7577
}
7678
catch (Exception e) {
77-
value = "<n/a>";
78-
name = "invalid." + name;
79+
if (this.prefixInvalidProperties) {
80+
value = "<n/a>";
81+
name = "invalid." + name;
82+
}
7983
String message = "Cannot decrypt key: " + key + " (" + e.getClass() + ": " + e.getMessage()
8084
+ ")";
8185
if (logger.isDebugEnabled()) {
@@ -93,4 +97,8 @@ else if (logger.isWarnEnabled()) {
9397
return result;
9498
}
9599

100+
public void setPrefixInvalidProperties(boolean prefixInvalidProperties) {
101+
this.prefixInvalidProperties = prefixInvalidProperties;
102+
}
103+
96104
}

spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/encryption/vault/VaultEnvironmentEncryptor.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class VaultEnvironmentEncryptor implements EnvironmentEncryptor {
4444

4545
private final VaultKeyValueOperations keyValueTemplate;
4646

47+
private boolean prefixInvalidProperties = true;
48+
4749
public VaultEnvironmentEncryptor(VaultKeyValueOperations keyValueTemplate) {
4850
this.keyValueTemplate = keyValueTemplate;
4951
}
@@ -102,8 +104,10 @@ public Environment decrypt(Environment environment) {
102104
}
103105
}
104106
catch (Exception e) {
105-
value = "<n/a>";
106-
name = "invalid." + name;
107+
if (this.prefixInvalidProperties) {
108+
value = "<n/a>";
109+
name = "invalid." + name;
110+
}
107111
String message = "Cannot resolve key: " + key + " (" + e.getClass() + ": " + e.getMessage()
108112
+ ")";
109113
if (logger.isDebugEnabled()) {
@@ -121,4 +125,8 @@ else if (logger.isWarnEnabled()) {
121125
return result;
122126
}
123127

128+
public void setPrefixInvalidProperties(boolean prefixInvalidProperties) {
129+
this.prefixInvalidProperties = prefixInvalidProperties;
130+
}
131+
124132
}

spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/encryption/CipherEnvironmentEncryptorTests.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,55 @@ public void shouldDecryptEnvironmentIncludeOrigin(String salt, String key) {
119119
.isEqualTo(secret);
120120
}
121121

122+
@ParameterizedTest
123+
@MethodSource("params")
124+
public void shouldDecryptFailed(String salt, String key) {
125+
TextEncryptor textEncryptor = new EncryptorFactory(salt).create(key);
126+
CipherEnvironmentEncryptor encryptor = new CipherEnvironmentEncryptor(keys -> textEncryptor);
127+
encryptor.setPrefixInvalidProperties(true);
128+
// given
129+
String secret = randomUUID().toString();
130+
131+
// when
132+
Environment environment = new Environment("name", "profile", "label");
133+
String encrypted = "{cipher}" + new EncryptorFactory(salt).create("dummykey").encrypt(secret);
134+
environment.add(new PropertySource("a", Collections.<Object, Object>singletonMap(environment.getName(),
135+
new PropertyValueDescriptor(encrypted, "encrypted value"))));
136+
137+
// then
138+
assertThat(encryptor.decrypt(environment)
139+
.getPropertySources()
140+
.get(0)
141+
.getSource()
142+
.get("invalid." + environment.getName())).isEqualTo("<n/a>");
143+
assertThat(encryptor.decrypt(environment).getPropertySources().get(0).getSource().get(environment.getName()))
144+
.isNull();
145+
}
146+
147+
@ParameterizedTest
148+
@MethodSource("params")
149+
public void decryptFailedWithoutInvalidPrefix(String salt, String key) {
150+
TextEncryptor textEncryptor = new EncryptorFactory(salt).create(key);
151+
CipherEnvironmentEncryptor encryptor = new CipherEnvironmentEncryptor(keys -> textEncryptor);
152+
encryptor.setPrefixInvalidProperties(false);
153+
// given
154+
String secret = randomUUID().toString();
155+
156+
// when
157+
Environment environment = new Environment("name", "profile", "label");
158+
String encryptedSecret = new EncryptorFactory(salt).create("dummykey").encrypt(secret);
159+
String encrypted = "{cipher}" + encryptedSecret;
160+
environment.add(new PropertySource("a", Collections.<Object, Object>singletonMap(environment.getName(),
161+
new PropertyValueDescriptor(encrypted, "encrypted value"))));
162+
163+
// then
164+
assertThat(encryptor.decrypt(environment)
165+
.getPropertySources()
166+
.get(0)
167+
.getSource()
168+
.get("invalid." + environment.getName())).isNull();
169+
assertThat(encryptor.decrypt(environment).getPropertySources().get(0).getSource().get(environment.getName()))
170+
.isEqualTo(encryptedSecret);
171+
}
172+
122173
}

spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/encryption/vault/VaultEnvironmentEncryptorTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,31 @@ public void shouldMarkAsInvalidPropertyWithNoKeyValue() {
117117
.isEqualTo("<n/a>");
118118
}
119119

120+
@Test
121+
public void shouldNotPrefixInvalidPropertyWithNoKeyValue() {
122+
// given
123+
String accounts = "accounts/mypay";
124+
String value = "{vault}:" + accounts;
125+
126+
VaultKeyValueOperations keyValueTemplate = mock(VaultKeyValueOperations.class);
127+
128+
VaultEnvironmentEncryptor encryptor = new VaultEnvironmentEncryptor(keyValueTemplate);
129+
encryptor.setPrefixInvalidProperties(false);
130+
131+
// when
132+
Environment environment = new Environment("name", "profile", "label");
133+
environment
134+
.add(new PropertySource("a", Collections.<Object, Object>singletonMap(environment.getName(), value)));
135+
136+
// then
137+
Environment processedEnvironment = encryptor.decrypt(environment);
138+
139+
assertThat(processedEnvironment.getPropertySources().get(0).getSource().get("invalid." + environment.getName()))
140+
.isNull();
141+
assertThat(processedEnvironment.getPropertySources().get(0).getSource().get(environment.getName()))
142+
.isEqualTo(accounts);
143+
}
144+
120145
@Test
121146
public void shouldMarkAsInvalidPropertyWithNoEmptyValue() {
122147
// given

0 commit comments

Comments
 (0)