Search code examples
logginglogbacklogstash-logback-encoder

Encrypt a field in json log when using logstash logback encoder


In our scala apis we are using logstash-logback-encoder to create json logs. We want to encrypt a particular field. Masking is not enough as the value needs to be decrypted if need be from logs. What would be the best way to achieve this? One way could be to create an implementation of JsonGeneratorDecorator like there is one for masking. But given that we know which field to mask, is there an easier way to achieve this?


UPDATE : Solution

I used the suggestion provided by @Phil Clay in the answer. However, there was a small catch. The intention behind ValueMasker is to decide what/when to mask on the basis of values. For ex, using a RegEx to match against all values (net.logstash.logback.mask.RegexValueMasker). Because of their very generic usecase, ValueMaskers are less efficient/performant. However, my use case is more specific as there is a specific field to be masked. By definition FieldMasker should be used but it doesn't provide a handle to the value to allow encryption. In the end, I created a custom ValueMasker but used the logic from net.logstash.logback.mask.FieldNameBasedFieldMasker as below:

class FieldNameBasedEncryptionMasker extends ValueMasker {

  private var salt: String = ""
  private var fieldNames: Set[String] = Set.empty

  def addFieldName(fieldName: String): Unit =
    fieldNames = fieldNames + fieldName

  def setSalt(saltValue: String): Unit =
    salt = saltValue

  override def mask(context: JsonStreamContext, value: Any): AnyRef = {
    if (context.hasCurrentName && fieldNames.contains(context.getCurrentName) && value
          .isInstanceOf[CharSequence]) {
        // encrypt value  
    } else null
  }
}

This can be configured in `logback.xml as:

<appender name="stash" class="net.logstash.logback.appender.LogstashSocketAppender">
    <host>$host</host>
    <port>$port</port>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <customFields>{"app_name":"${LOG_APP_NAME:-undefined}", "app_env":"${LOG_ENV:-local}"}</customFields>
        <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
            <valueMasker class="FieldNameBasedEncryptionMasker">
                <salt>$logging-encryption-salt</salt>
                <fieldName>fieldNameToEncrypt1</fieldName>
                <fieldName>fieldNameToEncrypt2</fieldName>
            </valueMasker>
        </jsonGeneratorDecorator>
    </encoder>
</appender>

Solution

  • You could create a custom implementation of net.logstash.logback.mask.ValueMasker that encrypts specific fields, instead of returning a standard masked value such as ****.

    Then configure the encoder to use your custom masker like this:

    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
            <valueMasker class="your.custom.EncryptingMasker"/>
        </jsonGeneratorDecorator>
    </encoder>