It is not possible to provide they encryption key properties in later stage of application startup (e.g via EnvironmentPostProcessor).
Describe the bug
TextEncryptorConfigBootstrapper.java#L67-L85 promotes KeyProperties, RsaProperties and TextEncryptor beans to ApplicationContext. When key properties are not available during bootstrap, default instances of KeyProperties, RsaProperties, and TextEncryptor (FailsafeTextEncryptor) are promoted.
In later stage of startup, if key properties are supplied (in my case via EnvironmentPostProcessor), they will be ignored by AutoConfiguration classes, because the relevant bean (TextEncryptor) is present in the ApplicationContext.
Sample
A simple spring-boot app like the following, with org.springframework.cloud:spring-cloud-config-server as dependency, shows the issue:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
class Processor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
final MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addFirst(new MapPropertySource("test", Map.of("encrypt.key", "my-secret-key")));
}
}
@Component
class Test {
@Autowired
private TextEncryptor encryptor;
@PostConstruct
void test() {
System.out.println("### Encryptor:: " + encryptor.getClass().getSimpleName());
}
}
sample output:
### Encryptor:: FailsafeTextEncryptor
The output shows that FailsafeTextEncryptor is registered which cannot encrypt/decrypt any secret.
Workaround
Currently I workaround the issue by enabling the legacy bootstrap (e.g adding org.springframework.cloud:spring-cloud-starter-bootstrap as dependency). See the if condition in TextEncryptorConfigBootstrapper.java#L69-L71 which cancels the promotion, in case legacy bootstrap is enabled.
Can you elaborate more on why you want this to work?
Here is the use-case:
A config-server is tasked to fetch encrypted configuration properties from a git server. Encryption key is provided by platform during server startup. An EnvironmentPostProcessor is needed to read the platform provided key and add it to the property sources with the expected name (encrypt.key). At the moment, this is only possible by enabling the legacy bootstrap which seems odd to me.
Looking at EncryptionBootstrapConfiguration.java, I see that It can create the same beans ( KeyProperties, RsaProperties, and TextEncryptor) if they are missing. So I am wondering why TextEncryptorConfigBootstrapper.java#L67-L85 is needed to promote them?
So I am wondering why TextEncryptorConfigBootstrapper.java#L67-L85 is needed to promote them?
Honestly I am not sure.
If we don't promote them with bootstrap enabled then, yes it looks like EncryptionBootstrapConfiguration.java should kick in.
If we don't promote and bootstrap is not enabled AND spring-cloud-config-server is on the classpath then it looks like DefaultTextEncryptionAutoConfiguration will get invoked and create the beans.
If we don't promote them and bootstrap is not enabled AND spring-cloud-config-server IS NOT on the classpath then there would be no TextEncryptor bean created, and that would be a breaking change IMO.
yes, you are right. the third case would be a breaking change! unless something like DefaultTextEncryptionAutoConfiguration is added to spring-cloud-context module.
The other thing I was thinking about was adding a property so we disable promoting of the beans (it would be disabled by default) allowing your use case to work without having to use bootstrap.
The other thing I was thinking about was adding a property so we disable promoting of the beans (it would be disabled by default) allowing your use case to work without having to use bootstrap.
I don't think that's needed. A property just to resolve this corner case seems too much. My use case is working fine with the workaround at the moment. But I'll think about a solution without breaking changes.