· 16 мин чтения

Zabbix 7.4: system.run проходит ручной тест, а action пишет Unsupported item key — разбираем AllowKey и nowait

Классика support-кейса на Zabbix 7.4: инженер заходит на сервер, руками гоняет <code>zabbix_agentd -t "system.run[systemctl restart nginx]"</code> или <code>zabbix_get -k</code> — агент честно возвращает <strong>1</strong>, команда отрабатывает. А триггер срабатывает, action запускает операцию «Удаленная команда» — и в истории элемента данных летит <strong>Unsupported item key</strong>. Права на файл скрипта проверены, пользователь zabbix в sudoers прописан, служба существует. Разбираю, почему ручной тест и автоматическое восстановление службы — это в буквальном смысле два разных ключа мониторинга, и какую строку добавить в конфиг агента, чтобы автовосстановление наконец заработало.

Почему ручной тест и action — это не одна и та же проверка

Когда вы вызываете zabbix_agentd -t "system.run[command]" или отправляете запрос через zabbix_get без явного указания второго параметра, агент выполняет команду в режиме wait — это режим по умолчанию для ключа system.run[command,<mode>]. Агент ждёт завершения процесса, забирает stdout/stderr и код возврата, формирует значение элемента данных. Тест зелёный, вы делаете вывод: «агент настроен верно, ключ разрешён» — и переходите к action.

Но операция «Удалённая команда» в действии (action) устроена иначе. Когда вы указываете в поле команды systemctl restart nginx и выбираете «Выполнить на: Zabbix-агент», сервер сам формирует внутренний запрос к агенту и всегда подставляет второй параметр nowait — то есть реальный ключ, который улетает на агент, это system.run[systemctl restart nginx,nowait], а не тот, что вы проверяли руками. Это задокументированное поведение remote command operations, и оно не зависит от версии — актуально и для 6.0, и для 7.0/7.4.

Отсюда и весь эффект «скрипт и права проверены, а восстановление не срабатывает»: правило AllowKey, которое вы добавили и проверили тестом, покрывает ключ с параметром wait (или вообще без параметра — это синоним wait), а агент получает от action ключ с параметром nowait. Для механизма ограничения проверок агента это два разных ключа, и совпадение по названию команды роли не играет — сверяется строка целиком, включая режим.

На практике мы фиксируем этот сценарий почти на каждой второй площадке, где клиент настраивал Zabbix самостоятельно по обрывочным инструкциям из интернета: администратор честно проверяет команду, видит зелёный тест, переходит к настройке действия — и останавливается на первой же реальной аварии, когда служба легла ночью, а рестарт не выполнился. К этому моменту часы уже потрачены на проверку прав на файл скрипта, состава группы sudo и содержимого самого скрипта — хотя причина всё это время была в одном параметре ключа мониторинга, который вообще не имеет отношения к операционной системе.

Как Zabbix 7.4 на самом деле блокирует system.run по умолчанию

Второй момент, который у нас на практике встречается ещё чаще первого: администратор смотрит в zabbix_agentd.conf или zabbix_agent2.conf, видит пустые строки AllowKey= и DenyKey= (или их полное отсутствие) и делает вывод — раз ничего не запрещено, значит разрешено всё. Для системных проверок это верно, но не для system.run.

По официальной документации Zabbix, все элементы данных с ключом system.run отключены по умолчанию, даже если DenyKey пуст — агент ведёт себя так, будто в самом конце списка правил неявно стоит DenyKey=system.run[*]. Это специальное исключение только для system.run, никакой другой ключ так себя не ведёт. Поэтому «агент настроен по умолчанию, ничего специально не блокировали» и «удалённые команды работают» — утверждения из разных вселенных: без явного разрешающего правила system.run не выполнится никогда, вне зависимости от прав на файл, sudoers и членства пользователя zabbix в группах.

Именно поэтому ручной тест, который часто выполняют вообще без ограничения по ключам (например, временно с AllowKey=system.run[*], а после проверки правило забывают сузить), проходит, а боевая конфигурация с точечным AllowKey под конкретный скрипт — нет: правило точечное, а ключ от action чуть отличается по параметрам.

Логика такого поведения понятна с точки зрения безопасности: ключ system.run — единственный из встроенных ключей агента, который умеет исполнять произвольный shell на хосте, поэтому разработчики Zabbix сознательно сделали его «закрытым по умолчанию», в отличие от остальных элементов данных (vfs.file.size, proc.num и подобных), которые работают без единой строки в AllowKey. Это стоит держать в голове при подготовке любого нового шаблона мониторинга: если шаблон использует system.run хотя бы в одном элементе или операции действия, конфигурация агента на целевом хосте требует правки в любом случае — ни один готовый пакет агента «из коробки» этот ключ не откроет.

Синтаксис AllowKey/DenyKey: маски, порядок и точное совпадение параметров

Правила AllowKey и DenyKey обрабатываются агентом последовательно, сверху вниз, и как только ключ элемента данных совпал с маской правила — проверка останавливается и применяется вердикт этого правила (разрешить или запретить). Правил можно задавать сколько угодно, каждое — отдельной строкой конфигурационного файла. Маска поддерживает символ * как для имени команды, так и для параметров ключа.

Для нашей задачи — автоматический перезапуск службы через action — рабочий набор правил выглядит так:

ФайлСтрока конфигурацииЧто разрешает
zabbix_agent2.confAllowKey=system.run[systemctl restart nginx,nowait]Именно тот ключ, который присылает action (обязательная строка)
zabbix_agent2.confAllowKey=system.run[systemctl restart nginx,wait]Тот же скрипт вручную/через zabbix_get для диагностики
zabbix_agent2.confDenyKey=system.run[*]Финальный запрещающий барьер — всё остальное system.run закрыто явно

Обратите внимание: строка DenyKey=system.run[*] в конце избыточна с точки зрения задокументированного поведения «system.run закрыт по умолчанию», но мы всё равно оставляем её явно в шаблоне — так конфигурация читается однозначно при аудите другим инженером и не зависит от версии агента, в которой нюансы поведения по умолчанию могут измениться.

Если вы обслуживаете несколько служб через один шаблон действия, не ставьте широкую маску вроде AllowKey=system.run[systemctl restart *,nowait] «для удобства» — это фактически даёт агенту право перезапустить любую unit-службу хоста по команде с сервера Zabbix, включая sshd и сам агент. В нашей практике мы перечисляем разрешённые команды поимённо и держим этот список в системе конфигурационного управления вместе с шаблоном действия.

Ещё один нюанс масок, который мы регулярно ловим при аудите чужих конфигураций: маска system.run[systemctl restart nginx*,nowait] с звёздочкой в конце имени службы кажется безобидной, но фактически разрешает и systemctl restart nginx-debug, и любую другую unit-службу, чьё имя начинается на nginx — если на хосте когда-либо появится тестовый юнит с похожим именем, правило откроет доступ и к нему. Мы используем маски только там, где вариативность параметров действительно нужна (например, номер порта в команде проверки), и указываем точное имя команды без хвостовой звёздочки везде, где это возможно.

EnableRemoteCommands: почему параметр не спасёт и чем он опасен

В старых конфигурациях агента (сохранившихся ещё с ветки 2.x-3.x) встречается параметр EnableRemoteCommands=1. Официальная документация прямо отмечает: этот параметр считается устаревшим, вместо него нужно использовать AllowKey/DenyKey. На Zabbix agent (classic, демон zabbix_agentd) параметр ещё формально распознаётся ради обратной совместимости, но на Zabbix Agent 2 в текущих версиях (включая 7.4) он попросту игнорируется — что подтверждается и обсуждениями в официальном форуме поддержки Zabbix. Если ваш агент — Agent 2 (а это конфигурация по умолчанию для новых установок, начиная примерно с 5.0), правка EnableRemoteCommands ничего не изменит, даже если вы поставите 1 и перезапустите службу.

Второй нюанс — EnableRemoteCommands=1 в тех версиях, где он ещё работает, снимает ограничение вообще для всех удалённых команд разом, без возможности выбрать конкретные команды. Это прямой путь к тому, что любой пользователь Zabbix с правом редактировать действия получает возможность выполнить произвольный shell на хосте. Мы в проектах ITfresh такую конфигурацию считаем находкой аудита безопасности и переводим клиента на точечные AllowKey при первой возможности, даже если старый параметр формально ещё «работает».

ПараметрСтатус в 7.4Что делать
EnableRemoteCommandsУстарел, на Agent 2 не действуетУдалить из конфига, заменить на AllowKey под конкретные команды
AllowKeyАктуален, рекомендован документациейПрописать точный ключ команды и режим (wait/nowait)
DenyKeyАктуаленЯвно закрыть system.run[*] последней строкой
UnsafeUserParametersАктуален, к system.run отношения не имеетНе путать с задачей восстановления служб — это для UserParameter

Zabbix agent (classic) и Agent 2: где искать конфиг и в чём разница поведения

Отдельно стоит развести, где в этой истории версия 7.4 действительно важна, а где поведение унаследовано из значительно более старых релизов — это экономит время, когда вы сверяетесь с документацией и находите статьи разных лет. Механика AllowKey/DenyKey и скрытое отключение system.run по умолчанию появились ещё в ветке 4.4-5.0 и с тех пор принципиально не менялись — это фундаментальная часть модели безопасности агента, а не фича конкретного релиза. То же касается и правила «remote command из action всегда nowait» — оно действует одинаково и в 6.0 LTS, и в 7.0, и в 7.4.

Что специфично для линейки 7.x — это Zabbix Agent 2 как агент по умолчанию для новых установок (в отличие от эпохи 4.x-5.x, где по умолчанию ставился classic-агент), плюс более строгая изоляция плагинов. На практике это означает: если вы разворачиваете хост с нуля на 7.4, вероятность того, что у вас установлен именно Agent 2, а не устаревший EnableRemoteCommands-совместимый classic-агент, значительно выше, чем на площадках, которые росли эволюционно с версии 3.x-4.x и просто обновлялись поверх старого конфига. Мы всегда сверяем это в первую очередь при разборе подобных обращений — иначе легко потратить время на правку не того файла или не того параметра.

На хостах наших клиентов встречаются оба агента, и перед тем как редактировать AllowKey, стоит убедиться, какой именно процесс слушает порт 10050. Проверяется просто: zabbix_agent2 -V или zabbix_agentd -V покажет версию и тип сборки, а в systemd — какой юнит реально запущен (zabbix-agent или zabbix-agent2).

Логика ограничения ключей у обоих агентов идентична — маски, порядок правил, скрытое DenyKey=system.run[*] по умолчанию действуют одинаково что на zabbix_agentd.conf, что на zabbix_agent2.conf. Разница — в дополнительных возможностях Agent 2: он поддерживает плагины и параметр Plugins.SystemRun.* для точной настройки логирования именно плагина system.run, тогда как classic-агент этого не умеет. Для нашей задачи — action, который перезапускает службу — набор строк AllowKey/DenyKey одинаков для обоих типов агента, менять нужно только имя файла конфигурации.

После правки конфигурации обязательно перечитайте службу — точечное добавление строки без перезапуска агента эффекта не даёт: systemctl restart zabbix-agent2 (или zabbix-agent для classic). Затем повторите тест уже с явным указанием режима nowait, а не в режиме по умолчанию — иначе вы снова проверите не тот ключ, который реально уходит из action.

zabbix_get -s 127.0.0.1 -k "system.run[systemctl restart nginx,nowait]"

Если это правило разрешено — команда в nowait-режиме вернёт единицу почти мгновенно, агент не ждёт завершения перезапуска и не проверяет код возврата. Это нормально и ожидаемо — так работает nowait по определению, подробнее в следующем разделе.

wait и nowait — не просто синтаксис, а два разных способа исполнения

Разница между режимами system.run[command,wait] и system.run[command,nowait] — не только в том, какое значение AllowKey нужно прописать. Это два принципиально разных сценария исполнения, и от того, какой вы выбираете в шаблоне действия, зависит, узнаете ли вы вообще, что команда восстановления упала.

ПараметрОжидание результатаЧто попадает в элемент данныхГде применимо
wait (по умолчанию)Агент ждёт завершения процессаstdout/stderr и признак успехаРучная диагностика, элементы данных с расписанием опроса
nowaitАгент не ждёт, отвечает сразу после запускаТолько подтверждение запуска, без результата выполненияВсе remote command из action с «Выполнить на: Zabbix-агент»

Отсюда практический вывод для отчётности перед руководством: если операция восстановления настроена через action на агенте, Zabbix физически не может показать вам в истории элемента данных, реально ли служба поднялась — nowait специально не проверяет результат исполнения, это описано и в официальной документации по выполнению команд. Мониторить сам факт восстановления нужно отдельным элементом данных (например, проверкой proc.num или net.tcp.service по расписанию после срабатывания триггера), а не полагаться на то, что operation «отработала» — «отработала» здесь означает «команда была запущена», а не «служба поднялась».

Если вам критично получить именно результат выполнения (код возврата, текст ошибки), альтернатива — выбрать в операции действия «Выполнить на: Zabbix сервер» (или Zabbix proxy). В этом случае команда выполняется локально на сервере/прокси через SSH или локальный shell, с таймаутом из параметра TrapperTimeout файла zabbix_server.conf (по документации — от 1 до 300 секунд, по умолчанию 300), и результат исполнения проверяется. Но тогда вопрос AllowKey агента снимается вовсе — команда никогда не уходит на хост через system.run, вместо этого нужен SSH-доступ сервера к целевой машине.

Пошаговое внедрение: action автовосстановления службы через Zabbix-агент

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

  1. Определить точную команду восстановления и зафиксировать её текстом без вариаций (например, systemctl restart nginx, а не обёртку со скриптом с параметрами по умолчанию — маски усложняют аудит).
  2. В zabbix_agent2.conf целевого хоста добавить пару строк AllowKey — под nowait (для action) и под wait (для собственной диагностики), и финальный DenyKey=system.run[*].
  3. Перечитать конфигурацию: systemctl restart zabbix-agent2, проверить systemctl status zabbix-agent2 на отсутствие ошибок парсинга конфига.
  4. Убедиться, что пользователь, под которым запущен агент (обычно zabbix), технически способен выполнить саму команду — права на unit в systemd, либо правило в /etc/sudoers.d/zabbix без запроса пароля именно под эту команду.
  5. В веб-интерфейсе Zabbix создать действие (Data collection → Actions → Trigger actions), условие — конкретный триггер отказа службы, операция — «Удалённая команда», тип «Пользовательский скрипt», цель — «Текущий хост», «Выполнить на: Zabbix-агент», текст команды — сама команда без обёртки в system.run[] (это делает сервер автоматически).
  6. Протестировать не через кнопку теста действия, а вызвав реальный триггер (например, остановив службу вручную) — только так вы увидите настоящий путь: сервер → action → агент с ключом nowait.
  7. Добавить второе, контролирующее условие или отдельный триггер, который проверяет статус службы после паузы (30-60 секунд по нашей практике достаточно для systemd-рестарта) — иначе вы не узнаете, помог ли рестарт.

Отдельно фиксируем в документации проекта: список хостов, где включён system.run, и точный список разрешённых команд. Это тот случай, когда экономия пяти минут на аудите оборачивается открытой дверью для произвольного исполнения команд с правами агента.

Для контроля версий мы храним итоговый фрагмент конфигурации агента рядом с описанием действия — так следующий инженер видит оба конца цепочки в одном месте, а не только настройку в веб-интерфейсе:

# /etc/zabbix/zabbix_agent2.conf
AllowKey=system.run[systemctl restart nginx,nowait]
AllowKey=system.run[systemctl restart nginx,wait]
DenyKey=system.run[*]
LogRemoteCommands=1

И отдельно — правило sudo без запроса пароля именно под эту команду, без расширения прав пользователя agent сверх необходимого:

# /etc/sudoers.d/zabbix
zabbix ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx

Логирование и диагностика: LogRemoteCommands и где смотреть, что реально ушло на агент

Когда правило вроде бы прописано, а action всё равно падает с Unsupported item key, быстрее всего включить логирование именно удалённых команд, не поднимая общий уровень отладки агента на весь хост. В конфиге агента (и classic, и Agent 2) есть параметр LogRemoteCommands — при значении 1 агент пишет в свой лог предупреждением (уровень warning) каждую команду, которая была выполнена удалённо, включая её точный текст и режим. Это позволяет свести к нулю гадание «какой именно ключ агент получил» — вы увидите его в логе буквально построчно.

LogRemoteCommands=1
DebugLevel=3

После включения и перезапуска агента повторите срабатывание триггера и смотрите:

По итогам диагностики мы обычно фиксируем находку в одной фразе для отчёта руководству: «агент получал ключ system.run[…,nowait], правило AllowKey покрывало только …,wait — добавили недостающую строку, повторный тест триггера прошёл». Это и есть тот самый facepalm-момент кейса — расхождение на одно слово в конце ключа.

Чек-лист: что проверить перед тем, как звать нас или писать в поддержку

Прежде чем эскалировать проблему «action не восстанавливает службу», пройдите по пунктам в этом порядке — в девяти случаях из десяти причина находится на первых трёх шагах.

ПроверкаОжидаемый результат
1Тип агента: classic или Agent 2Определяет имя конфигурационного файла и актуальность EnableRemoteCommands
2Наличие строки AllowKey именно с ,nowaitКлюч совпадает посимвольно с тем, что реально шлёт action
3Ручной тест с явным ,nowait, а не по умолчаниюzabbix_get с nowait возвращает 1, а не Unsupported item key
4Служба агента перечитана после правки конфигаsystemctl status без ошибок парсинга, аптайм процесса меньше времени правки
5Действие настроено на «Выполнить на: Zabbix-агент», а не сервер/проксиСовпадает с тем, где физически прописан AllowKey
6LogRemoteCommands=1 включён для контроляВ логе агента виден точный принятый или отклонённый ключ

Если все шесть пунктов пройдены, а ошибка сохраняется — почти всегда остаётся либо опечатка в самой команде (регистр, лишний пробел, кавычки), либо правило DenyKey выше по списку, которое перехватывает ключ раньше, чем до него доходит очередь AllowKey. Помните: правила читаются сверху вниз, и первое совпадение побеждает — порядок строк в конфиге имеет значение не меньшее, чем их содержимое.

Частые вопросы

Можно ли обойтись одним AllowKey без DenyKey, если система.run нужна только для одной команды?
Да, формально одного правила AllowKey=system.run[systemctl restart nginx,nowait] достаточно — по документации всё остальное system.run и так закрыто скрытым правилом по умолчанию. Мы всё же рекомендуем добавлять явный DenyKey=system.run[*] последней строкой — это делает поведение конфигурации однозначным при чтении файла другим инженером и не зависит от того, как поведение по умолчанию задокументировано в конкретной сборке агента.
Почему тест действия (кнопка Test в форме action) в интерфейсе Zabbix показывает успех, а реальное срабатывание триггера — нет?
Кнопка тестирования операции действия в веб-интерфейсе в большинстве версий проверяет условия и корректность настройки самого action (синтаксис, доступность цели), но не всегда выполняет реальный сетевой обмен с агентом с теми же параметрами, что боевое срабатывание. Достоверную проверку даёт только фактический триггер — остановите службу вручную и дождитесь реакции, либо тестируйте ключ system.run напрямую через zabbix_get с явным ,nowait.
EnableRemoteCommands=1 стоит в конфиге и, судя по всему, работает — можно ли его оставить как есть?
Технически на классическом Zabbix agent (демоне zabbix_agentd) он может ещё действовать ради обратной совместимости, но документация прямо называет его устаревшим и рекомендует AllowKey. На Zabbix Agent 2 параметр в текущих версиях игнорируется. Мы рекомендуем не полагаться на этот параметр в новых конфигурациях: он снимает ограничение сразу для всех удалённых команд, что с точки зрения безопасности эквивалентно открытому shell с сервера.
Как понять, сработала ли команда восстановления, если nowait не возвращает результат?
Через отдельный элемент данных, который проверяет фактическое состояние службы после паузы (классические варианты — proc.num[nginx], net.tcp.service[http] или системная проверка через systemd) и отдельный триггер/действие на случай, если служба так и не поднялась. Операция nowait подтверждает только запуск команды на агенте, а не её итог — это заложено в саму механику режима nowait, а не является багом настройки.
Нужно ли одинаково настраивать AllowKey и для wait, и для nowait, если действие точно использует только action?
Если единственный сценарий запуска — action с «Выполнить на: Zabbix-агент», достаточно правила под ,nowait — именно этот ключ реально уходит на агент. Правило под ,wait имеет смысл добавлять отдельно только если вы (или другой инженер) планируете диагностировать команду вручную через zabbix_get/zabbix_agentd -t — иначе тестовый прогон снова упрётся в Unsupported item key просто по другой причине.
📄
Скачайте подробный разбор в PDF Кейсы, статистика, типовые ошибки и чек-лист самопроверки — 12 страниц
Скачать PDF

Подпишитесь на разборы ITfresh

Раз в неделю — практичные материалы по ИТ для бизнеса: без спама, только польза.

Письмо придёт в течение минутыНе нашли его во «Входящих» — загляните в папку «Спам» или «Промоакции» и нажмите «Не спам». Так все следующие выпуски будут приходить прямо в основную почту.