Jenkins: прячем пароли в console output

Столкнулись с проблемой, что Jenkins не прячет пароли, токены  в output если это обычный string, прячет только при использовании secret text, password or credential parameters. Таким образом очень часто в output попадают конфиденциальные данные в  незашифрованном виде (что не есть безопасным). Если для примера взять Helm, то у нас всегда перед деплоем происходит полный вывод того что будет устанавливаться, туда как раз и попадаю все данные. Нужно исправить данную проблему, cпрятать все пароли, секреты, токены, сертификаты и упоминания об них в Jenkins Console output.

Данную проблему возможно решить установив специальный плагин Log File Filter или Mask Password. Оба плагина работают используя регулярные выражения, позволяют настроить поиск слов, строк, единственное различие так это то что Mask Password заменяет найденное слово, строку на ****, а Log File Filter позволяет в настройках заменить найденное слово или строку значением которое мы укажем. Детальнее рассмотрим в примерах ниже.

Для начала установим плагины и создадим две дженкинс пайпплайн задания для тестирования.

 

Установка плагинов

Переходим Manage Jenkins > Manage Pluggin > Available, в поле поиска прописываем названия плагинов, выбираем и устанавливаем их:

 

плагины установлены, перейдем к созданиям заданий.

Создание Job для тестирования

Всего для тестирования создам 2 задания, буду использовать groovy sctipt, ниже их названия:

– pipeline-mask-password-plugin-test – для тестирования плагина Mask Password;
– pipeline-log-file-filter-plugin-test – для тестирования плагина Log File Filted;

По умолчанию два задания будут использовать одинаковый groovy sctipt который ми будем модифицировать при тестировании плагинов:

node('master'){
       
       stage('simple string text'){
            sh "echo 'DB_PASSWORD: admin' "
            sh "echo 'APP_TOKEN: 309jdndd'"
       }

       stage('helm output for test'){
            sh 'echo "\n-name: EXAMPLE_TOKEN\n \
                value: someTOKEN\n \
                -name: MERCHANT_ID\n \
                value: someID\n"'
    }
}

запустим и проверим что нам покажет Console output:

Started by user admin
Replayed #26
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/HidePassword/pipeline-log-file-filter-plugin-test
[Pipeline] {
[Pipeline] stage
[Pipeline] { (simple string text)
[Pipeline] sh
+ echo 'DB_PASSWORD: admin'
DB_PASSWORD: admin
[Pipeline] sh
+ echo 'APP_TOKEN: 309jdndd'
APP_TOKEN: 309jdndd
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (helm output for test)
[Pipeline] sh
+ echo '
-name: EXAMPLE_TOKEN
                 value: someTOKEN
                 -name: MERCHANT_ID
                 value: someID
'

-name: EXAMPLE_TOKEN
                 value: someTOKEN
                 -name: MERCHANT_ID
                 value: someID

[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

хорошо, видим значения паролей, теперь переходим к тестированию плагинов.

Log File Filter

Для начала в наш скрипт добавим блок logFileFilter {}, а после перейдем к настройке фильтрации.

Модифицируем наш скрипт:

node('master'){
    
    logFileFilter {
       
       stage('simple string text'){
            sh "echo 'DB_PASSWORD: admin' "
            sh "echo 'APP_TOKEN: 309jdndd'"
       }

       stage('helm output for test'){
            sh 'echo "\n-name: EXAMPLE_TOKEN\n \
                value: someTOKEN\n \
                -name: MERCHANT_ID\n \
                value: someID\n"'
        }
    }
}

далее переходим в Manage Jenkins > Configure System ищем пункт Log File Filter Global config:

здесь жмем ADD, после чего появиться два поля Regexp и Replacement:

где:

– Regex – поле в котором указываем какое слово(строку) нужно найти
– Replacement – указываем на какое слово нужно заменить

Приступим к сортировке.
Нам нужно найти строку, которая начинается со слова  APP_TOKEN: 309jdndd и заменить ее на APP_TOKEN: **********. Так нужно проделать со всеми словами(строками) в которых нужно спрятать данные (пароли, секреты, токены). Для фильтрации и поиска будем использовать регулярные выражения. Создадим три правила:

Плагин ищет строку, которая начинается со слов (в колонке Regexp), и .* все что после них до конца строки, затем берет значения с второй колонки (Replacement) и подставляет на место старых значений. Для лучшего понимания я использую идентичные слова, но добавляю к ним **********. Сохраняем, запускаем задание, после чего проверим сработала ли подмена или нет.

Запускаем задание pipeline-log-file-filter-plugin-test и жмем Build Now:

 

смотрим Console Output:

Started by user admin
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/HidePassword/pipeline-log-file-filter-plugin-test
[Pipeline] {
[Pipeline] logFileFilter
[Pipeline] {
[Pipeline] stage
[Pipeline] { (simple string text)
[Pipeline] sh
+ echo 'DB_PASSWORD: *****
DB_PASSWORD: *****
[Pipeline] sh
+ echo 'APP_TOKEN: *****
APP_TOKEN: *****
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (helm output for test)
[Pipeline] sh
+ echo ' -name: EXAMPLE_TOKEN
                 value: *****
                 -name: MERCHANT_ID
                 value: *****
'
 -name: EXAMPLE_TOKEN
                 value: *****
                 -name: MERCHANT_ID
                 value: *****

[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // logFileFilter
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

как видим плагин сработал, он подставил значение которые ми указывали в настройках фильтрации.  Перейдем к следующему плагину.

 

Mask Password

Работает аналогично как LogFileFilter но не умеет заменять значение, то есть, ми можем указать слово или строку которую нужно спрятать и плагин заменит ее на ******. Настраивать фильтрацию можно через Manage Jenkins > Configure System > Mask Password  или  в самом скрипте. Так же плагин может быть в роли secret parameters и в него можем записывать ключ – значение и он будет заменять их если увидит идентичные строки в output, но не вижу смысла дублировать данные(подробное можно ознакомиться на странице плагина). Перейдем к самому тестированию.

Настраивать параметры фильтрации можем как в настройках дженкинса так и описывать в скрипте:

и пример в скрипте:

# для испопльзования с Regex
maskPasswords(varMaskRegexes: [[regex: '']]){

  // some block

}

# для использования с ключ - значение
maskPasswords(varPasswordPairs: [[password: '123', var: 'password']]) {
    
  // some block

}

внесем изменения в наш скрипт:

node('master'){
    
    maskPasswords(varMaskRegexes: [[regex: 'DB_PASSWORD:.*'], [regex: 'APP_TOKEN:.*'], [regex: 'value:.*']]) {
       
       stage('simple string text'){
            sh "echo 'DB_PASSWORD: someAdmin'"
            sh "echo 'APP_TOKEN: someTOKEN'"
       }

       stage('helm output for test'){
            sh 'echo " -name: EXAMPLE_TOKEN\n \
                value: someTOKEN\n \
                -name: MERCHANT_ID\n \
                value: someID\n"'
        }
    }
}

где в поле [regex: ‘DB_PASSWORD:.*’] мы описываем фильтрацию, если нужно несколько фильтров разделяем их запетой. В фильтрах описано искать ключевые слова и все что после них до конца строки, после чего плагин если найдет такую строку и заменит ее на *********.

Запустим задание pipeline-mask-password-plugin-test > Build Now:

 

проверяем Console Output:

Started by user admin
Replayed #48
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/HidePassword/pipeline-mask-password-plugin-test
[Pipeline] {
[Pipeline] maskPasswords
[Pipeline] {
[Pipeline] stage
[Pipeline] { (simple string text)
[Pipeline] sh
+ echo '********
********
[Pipeline] sh
+ echo '********
********
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (helm output for test)
[Pipeline] sh
+ echo ' -name: EXAMPLE_TOKEN
                 ********
                 -name: MERCHANT_ID
                 ********
'
 -name: EXAMPLE_TOKEN
                 ********
                 -name: MERCHANT_ID
                 ********

[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // maskPasswords
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

как видим плагин спрятал нужные нам значения, свою задачу выполнил.

Какой лучше плагин использовать – тут нужно отталкиваться от ваших нужд и целей, главное, что оба плагина справляются со своим заданием.

Полезные ссылки

https://regex101.com/ – ресурс, который поможет проверять работу регулярных выражений.
https://plugins.jenkins.io/log-file-filter/ – плагин LogFileFilter.
https://plugins.jenkins.io/mask-passwords/ – плагин MaskPassword.

Click to rate this post!
[Total: 1 Average: 5]