Привет, HABR!

Все те кто пользуются системой мониторинга Zabbix, с большой вероятностью, уже использовали UserParameter. Это, безусловно, очень полезный инструмент позволяющий значительно расширить базовые возможности Zabbix и поставить на мониторинг что-то такое эдакое. Но, к сожалению, у данного подхода есть свои минусы:

  1. Необходимость распространение файлов конфигураций UserParameter

  2. Необходимость перезапуска zabbix-agent после добавления UserParameter

  3. Не нулевой шанс, что zabbix-agent не запустится после добавления некорректного файла конфигурации UserParameter

Проблему под пунктом 1 затрагивать не будем и перейдем к оставшимся двум, т.к. решив проблему 2 и 3 пунктов, та что под №1 станет чуть менее проблемной потому-что мы не будем бояться сломать запуск zabbix-agent при распространении файлов UserParameter. 

Не так давно Zabbix в своем Release Notes for Zabbix 6.0.0 сообщил о том, что внедрил runtime команду для перечитки пользовательских параметров, без необходимости перезагрузки zabbix-agent. Но, по моему скромному мнению, что перезагрузка агента, что выполнение runtime команды (zabbix_agentd -R userparameter_reload) - являются дополнительным шагом при распространении UserParameter. А так как проблема с возможной поломкой запуска zabbix-agent не решена, то по прежнему распространение UserParameter остается не совсем безопасным этапом.

Запуск zabbix-agent достаточно просто сломать, не аккуратно работая с файлами пользовательских параметров. Добавьте агенту zabbix некорректный файл пользовательских параметров (дублирующие ключи, опечатки, лишние непечатаемые символы) и выполнив его перезагрузку, он пожалуется на конфиг и не запуститься. В случае если "плохой" UserParameter уже распространили на несколько тысяч или десятков тысяч серверов, то это очень неприятно исправлять. При большом парке серверов, большого разнообразия продуктов, нескольких десятков инженеров, разрабатывающих мониторинг, и нескольких сотен различных UserParameter, данная ситуация нет-нет, да и случится. Конечно же надо тестировать UserParameter перед распространением, но человеческий фактор никто не отменял.

Именно возможность сломать zabbix-agent, я считаю, основой опасностью UserParameter, и, поэтому, в первую очередь надо решать именно эту проблему.

Как же не стать параноиком и при этом не сломать zabbix-agent?

Не хочется мириться с потенциальным риском сломать zabbix-agent на большом количестве серверов практически одномоментно, но при этом хочется продолжать пользоваться пользовательскими параметрами, расширяя возможности мониторинга Zabbix.

Решение, которое можно предложить - это использовать один стандартный UserParameter (в связке со скриптом), который нам добавит возможность использования безопасно и динамически(как бонус) загружаемых пользовательских параметров. 

Взаимодействие zabbix-agent с динамическими пользовательскими параметрами будет выглядеть следующим образом

Основные тезисы данного подхода:

  1. Мы используем классические файлы UserParameter, т.е. переписывать ничего не придется.

  2. Если UserParameter параметр не корректный, то сломается сбор данных только для данного UserParameter и только для ключа/ключей, где закралась ошибка

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

Пример настройки на Linux

1. Создадим в /etc/zabbix/zabbix_agentd.d/ директорию dyn_up для динамических UserParameter:

2. Создадим в директории /etc/zabbix/zabbix_agentd.d/ скрипт dyn_up.sh со следующим содержимым:

#!/bin/bash
#Укажем расположение директории динамических UserParameter
dyn_up_dir="/etc/zabbix/zabbix_agentd.d/dyn_up"

#Получим ключ команды и имя UserParameter
read -r up_key up_name <<< $(echo "$1" | tr '@' ' ')

#Получи команду из ключе в динамическом UserParameter
up_cmd=`perl -ne "print if s/^UserParameter\s?=\s?\S+,\s?(.*)/\1/" "$dyn_up_dir/$up_name"`

#Пройдемся по всем полученным скриптом параметрам и заменим соответствующие ссылки на позиции
for ((i=1; i<=${#*}; i++))
do
    up_cmd=$(echo "$up_cmd" | perl -pe 's/(?<!\$)\${1}'$(( i - 1 ))'/'"${!i}"'/g')
done

#$$ меняем на $
up_cmd=$(echo "$up_cmd" | perl -pe 's/\$\$/\$/g')

#Выполняем команду
/bin/bash -c "$up_cmd"

3. Создадим в директории /etc/zabbix/zabbix_agentd.d/ наш единственный обычный UserParameter userparameter_dyn_up.conf со следующим содержимым:

UserParameter=dyn.up[*], ./dyn_up.sh "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"

4. Запуск проверки будет выглядеть следующим образом:

dyn.up["item_key@userparameter","param1","param2","param3","param4","param5","param6"]

P.S. Вашему вниманию представлен концепт, который опробован только на zabbix-агентах под Linux.

Скрипт для Windows может выглядеть следующим образом
$dir_path = 'D:\zabbix\zabbix_agentd.d\dyn_up'

$up_key, $up_name = $args[0].split('@')
$up_name = Join-Path -Path $dir_path -ChildPath $up_name

$up_cmd = (Select-String -Path $up_name -Pattern "^UserParameter\s?=\s?$up_key(\[\*\])?,").Line.Split(',') | Select-Object -Skip 1

for($i=1; $i -lt $args.Count; $i++){
   $str = '(?<!\$)\${1}'+$i 
   $up_cmd = $up_cmd -replace $str, $args[$i]
}

$up_cmd = $up_cmd -replace '\${2}', '$'

Invoke-Expression "$up_cmd"

Спасибо за внимание!

Комментарии (5)


  1. kvazimoda24
    11.06.2022 00:55

    А почему тогда не воспользоваться system.run?


    1. AlexGluck
      11.06.2022 01:03
      +2

      Если мне не изменяет память, раньше он был ограничен 255 символами. А сейчас описано, что ограничен возврат данных в 512кб. Плюс его надо отдельно включать и тогда можно будет любую команду на сервере выполнить, а не только то что запланировано. Свободное выполнение команд, это точно к вопросам о безопасности.


    1. azk Автор
      11.06.2022 01:36
      +1

      AlexGluck написал основные моменты с использованием system.run, немного дополню. Ключ элемента данных ограничен в последних версиях 2048 символами. Так же сталкивались с тем, что не любую команду можно запихнуть в system.ru(возможно что-то недосмотрел, утверждать не буду). С безопасниками можно договориться на использовании system.run с ведением списка AllowKey, но это доставляет примерно туже головную боль, что и распространение UserParameter, а ввиду вышеописанных ограничений system run, то выбрали для себя использование UserParameter


  1. bpof
    11.06.2022 13:56

    А как же агент2 и плагины на Go?


    1. azk Автор
      11.06.2022 14:09
      +1

      Можно, конечно, сделать и плагин, но постоянно пересобирать агент, что бы добавить свой плагин как-то не очень хочется. Так же в нашем случае из-за отсутствии рута проще использовать скрипт, т.к. скрипт можно разлить своими руками, а обновлять агентов, надо напрягать админов, и этап распространения растянется надолго.

      Если бы можно было написав плагин отдать его Zabbix, то это был бы интересный вариант, но тут нужно что бы сообщество поддержало соответствующий ZBX-NEXT.