Skip to content

TextEncryptor not resolved from bootstrapContext in DecryptEnvironmentPostProcessor #897

@ninj

Description

@ninj

Version Information

spring-cloud-context: 3.0.1

Details

Pre-Ilford, it was possible to inject a custom TextEncryptor into the framework to override the default implementation of decrypting a secret. (In my case, I am using GCP KMS to decrypt the secrets.)

The TextEncryptorBindHandler introduced in #872 allows for a custom TextEncryptor implementations by resolving from the bootstrap context: TextEncryptorConfigBootstrapper:L70

registry.registerIfAbsent(BindHandler.class, context -> {
    TextEncryptor textEncryptor = context.get(TextEncryptor.class);
    if (textEncryptor != null) {
        KeyProperties keyProperties = context.get(KeyProperties.class);
        return new TextEncryptorBindHandler(textEncryptor, keyProperties);
    }
    return null;
});

However, DecryptEnvironmentPostProcessor prefers to create it's own TextEncryptor: DecryptEnvironmentPostProcessor:L78

protected TextEncryptor getTextEncryptor(ConfigurableEnvironment environment) {
    Binder binder = Binder.get(environment);
    KeyProperties keyProperties = binder.bind(KeyProperties.PREFIX, KeyProperties.class)
            .orElseGet(KeyProperties::new);
    if (keysConfigured(keyProperties)) {
        setFailOnError(keyProperties.isFailOnError());
        if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null)) {
            RsaProperties rsaProperties = binder.bind(RsaProperties.PREFIX, RsaProperties.class)
                    .orElseGet(RsaProperties::new);
            return EncryptionBootstrapConfiguration.createTextEncryptor(keyProperties, rsaProperties);
        }
        return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
    }
    // no keys configured
    return new FailsafeTextEncryptor();
}

Workaround

To enable custom decryption of properties in the spring environment:

  • Subclass DecryptEnvironmentPostProcessor (with higher priority) to:
    • decrypt properties using custom TextEncryptor by overriding getTextEncryptor to create a new instance of the custom TextEncryptor.
    • inject a custom PropertySource to enable (for example) spring.config.use-legacy-processing to prevent DecryptEnvironmentPostProcessor from processing encrypted properties (*).
  • Because the standard DecryptEnvironmentPostProcessor executes at lowest priority, use a high-priority ApplicationContextInitializer to remove the custom PropertySource before any other ApplicationContextInitializer implementations might execute and use the temporary value.

In addition to the above, to support decrypting properties with TextEncryptorBindHandler:

  • Add a custom EnvironmentPostProcessor to register a TextEncryptor supplier that creates a new instance of the custom TextEncryptor.

(*) If DecryptEnvironmentPostProcessor executes (because neither spring cloud bootstrap nor legacy processing is enabled), this results in a FailsafeTextEncryptor being created (**). An exception gets thrown when DecryptEnvironmentPostProcessor also tries to decrypt any values that have been encrypted.

(**) Because the properties to configure a normal TextEncryptor are not defined, as we are intending to use a custom implementation instead.

Questions

  • Should people be allowed to customise TextEncryptor during bootstrap to decrypt ConfigData values?
  • If not, should people simply use a different prefix for encrypted values and clone the code for DecryptEnvironmentPostProcessor and AbstractEnvironmentDecrypt?
  • Or alternatively, people could implement a ConfigData version of what is described in Spring Boot Configuration with Jasypt. However, this means moving away from using the {cipher} prefix and so away from how spring-cloud denotes encrypted values.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions