spring-boot-actuator

Spring actuator SanitizingFunction bean is not used


Spring boot version 2.7.3

I need to mask all the fields containing 'uri' string in actuator/env response.

According to documentation:

To take control over the sanitization, define a SanitizingFunction bean. The SanitizableData with which the function is called provides access to the key and value as well as the PropertySource from which they came. This allows you to, for example, sanitize every value that comes from a particular property source. Each SanitizingFunction is called in order until a function changes the value of the sanitizable data.

So i implemented class:

import org.springframework.boot.actuate.endpoint.SanitizingFunction;

@Component
public class ActuatorSanitizer implements SanitizingFunction {
...

during application startup debugging i can see my bean is initialized and even autowired to org.springframework.boot.actuate.endpoint.Sanitizer class

    public Sanitizer(Iterable<SanitizingFunction> sanitizingFunctions, String... keysToSanitize) {
        this.sanitizingFunctions = new ArrayList();
        List var10001 = this.sanitizingFunctions;
        sanitizingFunctions.forEach(var10001::add);
        this.sanitizingFunctions.add(this.getDefaultSanitizingFunction());
        this.setKeysToSanitize(keysToSanitize);
    }

but then i see that Sanitizer constructor is invoked once again without my custom sanitizing function implementation injected and i see no effect in /actuator/env call response.

Is there a way to get more sanitizing control programatically or via config?

UPD: custom SanitizingFunction implementation works for /actuator/configprops request. This is why Sanitizer constructor is invoked twice.


Solution

  • Update for Spring Boot 2.7.3

    For configuring sanitizing of env-endpoint in application.yml; replace keys-to-sanitize with actual keys:

    management:
      endpoint:
        env:
          keys-to-sanitize:
            - USERNAME
            - HOMEBREW_REPOSITORY 
    

    For customizing env-endpoint programatically, add a custom EnvironmentEndpoint bean. SanitizingFunction is the same as in the original answer.

    import org.springframework.boot.actuate.env.EnvironmentEndpoint;
    import org.springframework.boot.actuate.endpoint.SanitizingFunction;
    import org.springframework.core.env.Environment;
    
    
        @Bean
        public EnvironmentEndpoint customEnvironmentEndpoint(
                Environment environment,
                SanitizingFunction sanitizingFunction) {
            return new EnvironmentEndpoint(
                    environment,
                    Collections.singletonList(sanitizingFunction));
        }
    

    Update #2

    OP experienced this error during app start:

    nested exception is java.lang.IllegalStateException: Found two endpoints with the id 'env': 'writableEnvironmentEndpoint' and 'customEnvironmentEndpoint'
    

    OP later reported that using these suggestions from me fixed the issue:

    management.endpoint.env.enabled=false
    

    and

    @SpringBootApplication(exclude = EnvironmentEndpointAutoConfiguration.class)
    

    For Spring Boot 3.4

    Step 1: Configure actuator

    Add this to .authorizeHttpRequests(authorize -> authorize in SecurityFilterChain

    // for testing only, enable each actuator endpoint separately
    .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
    

    Add this to application.yml

    management:
      endpoints:
        web:
          exposure:
            include:
              - env
              - configprops
      endpoint:
        env:
          show-values: always
        configprops:
          show-values: always
    

    Verify that both http://localhost:8080/actuator/env and http://localhost:8080/actuator/configprops works.

    Step 2: Add SanitizingFunction

    Put this bean into any @Configuration-annotated class

        @Bean
        public SanitizingFunction customSanitizingFunction() {
            return data -> data.getKey().contains("uri")
                    ? data.withValue("TESTING") // replace with e.g. ****
                    : data;
        }
    

    Verify that "TESTING" appears in both endpoints.