Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing sensitive data in logs in a Quarkus application

I'm trying to implement a regex-based replacement of sensitive log data, using the default Quarkus logging solution.

For example, if in the logged information appears <password>secret</, would like it to be saved into the logfile as <password>***</. I had this working in other apps using Logback and defining in the logback.xml a conversionRule and a pattern:

  <conversionRule conversionWord="replaceConverter"
                  converterClass="org.something.logger.CustomFieldCompositeConverter" />
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>logs/replaced.log</file>
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      <providers>
        <timestamp/>
        <logLevel/>
        <logLevelValue/>
        <loggerName/>
        <threadName/>
        <pattern>
            <pattern>
            {
                "message":"%replaceConverter(%message){'$1***$2', '(:password&gt;)(?:.*)(&lt;/.*:password&gt;)'}",
                }
            </pattern>
          </pattern>

I would like to know if something similar can be done if I use the Quarkus default JBoss logger-based logging solution, or using the Logback extension is the only option. So far I have found nothing related to log message replacing/processing in the Quarkus logging guide. Do you know if this can be done?

I'm also using Slf4j, if that is relevant.

like image 395
peterhuba Avatar asked Oct 22 '25 03:10

peterhuba


1 Answers

I was facing a similar issue. I've been able to solve it without logback. I'm using the default JBoss LoggerManager with SLF4J. For this, I've added the following dependencies

    <dependency>
            <groupId>org.jboss.slf4j</groupId>
            <artifactId>slf4j-jboss-logmanager</artifactId>
    </dependency>

And for test, I had to add a system property in my pom.xml to the surefire plugin like this

    <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <configuration>
            <systemPropertyVariables>
                <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                <maven.home>${maven.home}</maven.home>
            </systemPropertyVariables>
            <argLine>-Dfile.encoding=UTF-8</argLine>
        </configuration>
    </plugin>

After that, I created a custom implementation for org.jboss.logmanager.handlers.ConsoleHandler interface

import org.jboss.logmanager.handlers.ConsoleHandler;
import java.util.logging.LogRecord;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CustomConsoleHandler extends ConsoleHandler {

    @Override
    public void publish(LogRecord record) {
        record.setMessage(maskPassword(record.getMessage()));
        super.publish(record);
    }
    
    private String maskPassword(String body) {
        Matcher matcher = Pattern.compile("(((pwd|pw|pass|password|pwrd)([=: ])+\\s*['\"]?)([^*]+?))(,|\\s|'|\")", Pattern.CASE_INSENSITIVE).matcher(body);
        while (matcher.find()) {
            body = new StringBuilder(body).replace(matcher.start(5), matcher.end(5), "******").toString();
            matcher.reset(body);
        }
        return body;
    }
}

To use this handler with the default handlers that quarkus generates I had to add them to the delayed handlers in io.quarkus.bootstrap.logging.InitialConfigurator

!!! The order of the handlers is important, they are chained after each other, so the second handler will work with the output of the first handler, so we have to put our CustomConsoleHandler in the first place in the array!!!

I don't need to mask liquibase and other logs at initialization time (before bean creations) so I've added my handler in a post construct method of a bean annotated with @Startup annotation.

import io.quarkus.runtime.Startup;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import java.util.logging.Handler;

import static io.quarkus.bootstrap.logging.InitialConfigurator.DELAYED_HANDLER;

@ApplicationScoped
@Slf4j
@Startup(1)
public class LoggerConfig {
    @PostConstruct
    public void initLogger() {
        Handler[] oldHandlers = DELAYED_HANDLER.clearHandlers();
        DELAYED_HANDLER.addHandler(new CustomConsoleHandler());
        for (Handler oldHandler : oldHandlers) {
            DELAYED_HANDLER.addHandler(oldHandler);
        }
    }
}

It is important to use DELAYED_HANDLER's thread-safe public methods to clear and add handlers.

To try this you can add a request body logger like this

import lombok.extern.slf4j.Slf4j;

import javax.enterprise.context.RequestScoped;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
import java.io.IOException;

@Slf4j
@Provider
@RequestScoped
public class logServletFilter implements ReaderInterceptor {

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext context)
            throws IOException, WebApplicationException {
        log.info("Reading request with type: " + context.getGenericType());
        Object o = context.proceed();
        log.info("Request body " + o.toString());
        return o;
    }
}

In application.yaml I have the following configuration for logs

quarkus:
  console:
    color: true
  log:
    console:
      format: "Level={%-5p} Elapsed time={%r} Source class={%C} Date={%d{yyyy-MM-dd HH:mm:ss}} Thread={%t} ThreadId={%t{id}} Host={%h} MDC={%X} Nested={%x} %nMessage={%m} Ex={%e}%n"
      level: DEBUG
like image 161
Viktor Korai Avatar answered Oct 26 '25 06:10

Viktor Korai