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

Decrypt properties earlier in the SB lifecycle #1219

Merged
merged 1 commit into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -14,6 +14,13 @@
"sourceType": "org.apache.camel.component.jasypt.springboot.JasyptEncryptedPropertiesConfiguration",
"defaultValue": "PBEWithMD5AndDES"
},
{
"name": "camel.component.jasypt.early-decryption-enabled",
"type": "java.lang.Boolean",
"description": "Enable the early properties decryption during Spring Start Up",
"sourceType": "org.apache.camel.component.jasypt.springboot.JasyptEncryptedPropertiesConfiguration",
"defaultValue": false
},
{
"name": "camel.component.jasypt.enabled",
"type": "java.lang.Boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public class JasyptEncryptedPropertiesConfiguration {
*/
private boolean enabled;

/**
* Enable the early properties decryption during Spring Start Up.
* Enabling this feature, encrypted properties can be decrypted before the Spring Boot AutoConfiguration
* kicks in, for example, server.port=ENC(oBpQDDUvFY0c4WNAG0o4LIS5bWqmlxYlUUDTW2iXJIAZFYvM+3vOredaMcVfL4xW)
* will be decrypted to 8082, and the application will start using that port.
*/
private boolean earlyDecryptionEnabled;

/**
* The algorithm to be used for decryption. Default: PBEWithMD5AndDES
*/
Expand Down Expand Up @@ -73,6 +81,14 @@ public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public boolean isEarlyDecryptionEnabled() {
return earlyDecryptionEnabled;
}

public void setEarlyDecryptionEnabled(boolean earlyDecryptionEnabled) {
this.earlyDecryptionEnabled = earlyDecryptionEnabled;
}

public String getAlgorithm() {
return algorithm;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.apache.camel.component.jasypt.springboot;

import org.apache.camel.component.jasypt.JasyptPropertiesParser;
import org.apache.camel.component.properties.PropertiesParser;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;

import java.util.Properties;

public class SpringBootJasyptPropertiesParser implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
private static final Logger LOG = LoggerFactory.getLogger(SpringBootJasyptPropertiesParser.class);

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();

// Manual Autoconfigure jasypt component
JasyptEncryptedPropertiesAutoconfiguration jasyptEncryptedPropertiesAutoconfiguration = new JasyptEncryptedPropertiesAutoconfiguration();
JasyptEncryptedPropertiesConfiguration jasyptEncryptedPropertiesConfiguration =
jasyptEncryptedPropertiesAutoconfiguration.JasyptEncryptedPropertiesAutoconfiguration(event.getEnvironment());

if (jasyptEncryptedPropertiesConfiguration != null && jasyptEncryptedPropertiesConfiguration.isEarlyDecryptionEnabled()) {
// Too early in the lifecycle, the property has to be resolved manually
String password = jasyptEncryptedPropertiesConfiguration.getPassword();
if (password.startsWith("sysenv:")) {
password = System.getenv(StringHelper.after(password, "sysenv:"));
}
if (ObjectHelper.isNotEmpty(password) && password.startsWith("sys:")) {
password = System.getProperty(StringHelper.after(password, "sys:"));
}
jasyptEncryptedPropertiesConfiguration.setPassword(password);

EnvironmentStringPBEConfig environmentStringPBEConfig =
jasyptEncryptedPropertiesAutoconfiguration.environmentVariablesConfiguration(jasyptEncryptedPropertiesConfiguration);
StringEncryptor stringEncryptor =
jasyptEncryptedPropertiesAutoconfiguration.stringEncryptor(environmentStringPBEConfig);
EncryptablePropertySourcesPlaceholderConfigurer encryptablePropertySourcesPlaceholderConfigurer =
jasyptEncryptedPropertiesAutoconfiguration.propertyConfigurer(stringEncryptor);
PropertiesParser propertiesParser = jasyptEncryptedPropertiesAutoconfiguration.encryptedPropertiesParser(environment,
stringEncryptor,
environment);

final Properties props = new Properties();
for (PropertySource mutablePropertySources : event.getEnvironment().getPropertySources()) {
if (mutablePropertySources instanceof MapPropertySource mapPropertySource) {
mapPropertySource.getSource().forEach((key, value) -> {
if (value instanceof OriginTrackedValue originTrackedValue &&
originTrackedValue.getValue() instanceof String stringValue &&
stringValue.startsWith(JasyptPropertiesParser.JASYPT_PREFIX_TOKEN) &&
stringValue.endsWith(JasyptPropertiesParser.JASYPT_SUFFIX_TOKEN)) {

LOG.debug("decrypting and overriding property {}", key);
try {
props.put(key, propertiesParser.parseProperty(key.toString(), stringValue, null));
} catch (Exception e) {
// Log and do nothing
LOG.debug("failed to parse property {}", key, e);
}
}
});
}
}

environment.getPropertySources().addFirst(new PropertiesPropertySource("overridden-camel-jasypt-properties", props));
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.context.ApplicationListener=\
org.apache.camel.component.jasypt.springboot.SpringBootJasyptPropertiesParser
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.apache.camel.component.jasypt.springboot;

import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;

@CamelSpringBootTest
@DirtiesContext
@SpringBootApplication
@SpringBootTest(
classes = { EncryptedPropertiesTest.TestConfiguration.class },
properties = { "camel.component.jasypt.early-decryption-enabled=true" })
public class EarlyEncryptedPropertiesTest extends EncryptedPropertiesTest {
}
Loading