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.conf | AllowKey=system.run[systemctl restart nginx,nowait] | Именно тот ключ, который присылает action (обязательная строка) |
zabbix_agent2.conf | AllowKey=system.run[systemctl restart nginx,wait] | Тот же скрипт вручную/через zabbix_get для диагностики |
zabbix_agent2.conf | DenyKey=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-агент
Ниже — минимальная последовательность, которую мы прогоняем на каждой новой площадке, когда клиент просит именно автоматический рестарт службы через триггер, а не просто уведомление.
- Определить точную команду восстановления и зафиксировать её текстом без вариаций (например,
systemctl restart nginx, а не обёртку со скриптом с параметрами по умолчанию — маски усложняют аудит). - В
zabbix_agent2.confцелевого хоста добавить пару строк AllowKey — под nowait (для action) и под wait (для собственной диагностики), и финальныйDenyKey=system.run[*]. - Перечитать конфигурацию:
systemctl restart zabbix-agent2, проверитьsystemctl status zabbix-agent2на отсутствие ошибок парсинга конфига. - Убедиться, что пользователь, под которым запущен агент (обычно
zabbix), технически способен выполнить саму команду — права на unit в systemd, либо правило в/etc/sudoers.d/zabbixбез запроса пароля именно под эту команду. - В веб-интерфейсе Zabbix создать действие (Data collection → Actions → Trigger actions), условие — конкретный триггер отказа службы, операция — «Удалённая команда», тип «Пользовательский скрипt», цель — «Текущий хост», «Выполнить на: Zabbix-агент», текст команды — сама команда без обёртки в
system.run[](это делает сервер автоматически). - Протестировать не через кнопку теста действия, а вызвав реальный триггер (например, остановив службу вручную) — только так вы увидите настоящий путь: сервер → action → агент с ключом nowait.
- Добавить второе, контролирующее условие или отдельный триггер, который проверяет статус службы после паузы (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После включения и перезапуска агента повторите срабатывание триггера и смотрите:
/var/log/zabbix/zabbix_agent2.log(илиzabbix_agentd.log) — здесь появится строка с точным ключом и вердиктом «разрешено» или «запрещено правилом»;/var/log/zabbix/zabbix_server.log— здесь фиксируется сам факт постановки операции в очередь и, при DebugLevel не ниже 4, обмен с агентом;- раздел Reports → Action log в веб-интерфейсе — там видно статус выполнения операции на уровне action (отправлено/ошибка), но не текст ошибки ключа — за деталями всё равно нужно идти в лог агента.
По итогам диагностики мы обычно фиксируем находку в одной фразе для отчёта руководству: «агент получал ключ 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 |
| 6 | LogRemoteCommands=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 просто по другой причине.