Build project codecov pre-commit

Masking logs

Подключение

Maven

<dependency>
  <groupId>ru.romanow.logging</groupId>
  <artifactId>log-masking-lib</artifactId>
  <version>${log-masking-lib.version}</version>
</dependency>

Gradle

testImplementation "ru.romanow.logging:log-masking-lib:$logMaskingVersion"

Реализация

Для маскирования логов используется плагин MaskingConverter.

Для его подключения используется annotation processor org.apache.logging.log4j:log4j-core, который собирает плагины в $buildDir/tmp/kapt3/classes/main/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat.

В шаблоне этот конвертер вызывается как %mask{ %msg }:


<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %mask{ %msg }%n"/>

Отправка в ELK выполняется в формате JSON через fluent-bit (по адресу localhost:5170), для сборки сообщения используется JsonTemplateLayout (шаблон layout.json, для маскирования используется pattern resolver как %mask{ %msg }.

Тестирование

$ docker compose up -d

$ ./gradlew bootRun --args='--spring.profiles.active=docker'

$ curl http://localhost:8080/api/v1/send \
  -H 'Content-Type: application/json' \
  -d '{"lastName": "Alex"}'

$ docker compose logs -f fluent-bit

Вывод fluent-bit:

fluent-bit  | {"localTime"=>"2024-05-14T20:47:40.589Z", "level"=>"INFO", "text"=>"Initializing Servlet 'dispatcherServlet'", "levelInt"=>6, "loggerName"=>"org.springframework.web.servlet.DispatcherServlet", "threadName"=>"http-nio-8080-exec-1", "appName"=>"log-masking", "appType"=>"JAVA"}]
fluent-bit  | {"localTime"=>"2024-05-14T20:47:40.590Z", "level"=>"INFO", "text"=>"Completed initialization in 1 ms", "levelInt"=>6, "loggerName"=>"org.springframework.web.servlet.DispatcherServlet", "threadName"=>"http-nio-8080-exec-1", "appName"=>"log-masking", "appType"=>"JAVA"}]
fluent-bit  | {"localTime"=>"2024-05-14T20:47:40.620Z", "level"=>"INFO", "text"=>"{"lastName":"<masked>"}", "levelInt"=>6, "loggerName"=>"ru.romanow.logging.web.LogSender", "threadName"=>"http-nio-8080-exec-1", "appName"=>"log-masking", "appType"=>"JAVA"}]

Правила маскирования

Тип поля:

  • email –> romanowalex@mail.ru -> r**********@mail.ru
  • firstName –> не маскируется
  • lastName, middleName –> if (name.length > 7) Ab*****c else A*****
  • text –> в зависимости от длины:
    • 1 -> *
    • 1..4 -> 1234 -> 1***
    • 5..9 -> 123456789 -> 1******89 (length * 60%)
    • 10..15 -> 12345678901 -> 12*******01 (length * 60%)
    • 16 + -> 12345678901234567 -> 123***********567 (length * 60%)

Конфигурация:

application:
    masking:
        -   field: fullName
            type: FULL_NAME
        -   field: lastName
            type: LAST_NAME
        -   field: email
            type: EMAIL
        -   field: JWT
            type: TEXT
        -   regex: Authorization\s*:\s*(\S+)
            type: TEXT