Почему скрытое Custom Field в NetBox всё равно видно через API и можно ли хранить там код доступа
Hidden у Custom Field в NetBox убирает поле из карточки объекта и формы редактирования — и только. На REST и GraphQL API это не действует: любой с правом View на объект получит значение целиком. Разбираю, как устроена видимость custom fields с версии 3.7 и можно ли хранить в NetBox код доступа.
Hidden в NetBox — это настройка интерфейса, а не разграничение доступа
За последние два года я трижды видел одну и ту же ошибку на внедрениях NetBox: администратор заводит Custom Field под что-то чувствительное — код от двери, PIN сигнализации, временный пароль от свитча — ставит видимость Hidden и на этом успокаивается. Логика понятна: поле пропадает из карточки объекта, из формы редактирования, из списка колонок. Выглядит как «спрятал». На деле Hidden — это фильтр рендеринга веб-интерфейса Django, он выполняется на уровне шаблона страницы, а не на уровне выдачи данных. Когда мы делаем аудит информационной безопасности для клиента с NetBox или другой CMDB, разбор custom fields — обязательный пункт: почти всегда там находится что-то, что не должно быть доступно всем пользователям с правом чтения.
До версии 3.7 (вышла 29 декабря 2023 года) в NetBox был один параметр custom field — ui_visibility, который одновременно управлял и просмотром, и редактированием. В 3.7 его разделили на два независимых поля: ui_visible (видимость при просмотре объекта) и ui_editable (доступность при редактировании). Разработчики прямо объяснили зачем: раньше нельзя было сделать поле видимым, но нередактируемым (например, значение, которое заполняет только интеграция) — теперь для этого есть ui_visible: Always + ui_editable: No. Разделение сохранилось и в текущих версиях: на сентябрь 2026 года актуальная ветка — NetBox 4.7 (4.7.0 вышел 2 сентября 2026-го, 4.7.1 — 15 сентября), схема ui_visible/ui_editable в нём та же, что и в 3.7.
Официальная документация не оставляет простора для интерпретаций. В разделе про custom fields прямо написано: «Note that this setting has no impact on the REST or GraphQL APIs: Custom field data will always be available via either API» — «эта настройка не влияет на REST или GraphQL API: данные custom field всегда доступны через любой из API». Это не побочный эффект и не недосмотр — это задокументированное поведение по дизайну. Hidden в ui_visible и Yes/No/Hidden в ui_editable — про то, что видит и может менять человек, кликающий мышкой по веб-интерфейсу. Про то, что видит скрипт, интеграция или любопытный сотрудник с токеном API, эти настройки не говорят вообще ничего.
Как custom_fields выглядит в ответе REST API
В REST API NetBox все значения дополнительных полей объекта — вне зависимости от ui_visible и ui_editable — лежат внутри одного ключа custom_fields в JSON-ответе. Это касается любой модели, у которой в принципе бывают custom fields: устройства, сайты, IP-адреса, префиксы, VLAN, контракты и так далее. Структура одинаковая что для API-справочника (/api/schema/swagger-ui/), что для реального объекта: custom_fields — это вложенный словарь, где ключ — machine name поля, значение — то, что туда занесли, в исходном, не отфильтрованном виде.
Проверить это можно без интеграций, одним curl-запросом с токеном API — токен создаётся в разделе учётной записи или через /api/users/tokens/, у него могут быть свои разрешения, но по умолчанию он наследует права привязанного пользователя:
Если у поля выставлено ui_visible: Hidden, в ответе REST API оно всё равно присутствует в custom_fields — просто в UI карточки объекта эта строка не отрисовывается. Отдельно стоит помнить про фильтрацию: NetBox поддерживает поиск по custom field через query-параметр с префиксом cf_ — например, GET /api/dcim/sites/?cf_room_key=4821 вернёт все сайты, где значение поля room_key равно 4821. То есть скрытое поле можно не только прочитать поштучно, но и использовать как критерий фильтра — переберите площадки одним запросом, если знаете шаблон значения.
- curl -H "Authorization: Token $NB_TOKEN" https://netbox.example.com/api/dcim/sites/14/
- curl -H "Authorization: Token $NB_TOKEN" "https://netbox.example.com/api/dcim/sites/?cf_room_key=4821"
- curl -H "Authorization: Token $NB_TOKEN" https://netbox.example.com/api/dcim/sites/14/ | python3 -c "import json,sys;print(json.load(sys.stdin)['custom_fields'])"
GraphQL: тот же custom_fields, но с оговоркой по нагрузке
В GraphQL API NetBox (эндпоинт /graphql/) у каждого объектного типа тоже есть поле custom_fields, и оно так же не фильтруется настройками ui_visible/ui_editable — те применяются исключительно к серверному рендерингу шаблонов веб-интерфейса, GraphQL-резолверы про них не знают. Запрос списка сайтов с их дополнительными полями выглядит так:
Здесь стоит сделать техническую оговорку, которая пригодится, если вы уже используете GraphQL для интеграций: до NetBox 4.6.7 запрос поля custom_fields на list-запросах GraphQL генерировал по одному дополнительному SQL-запросу на каждый объект в выдаче — классический N+1 (issue #22813, исправлено в 4.6.7 от 30 июля 2026 года). Для десятка сайтов это незаметно, для выгрузки полутора тысяч устройств одним запросом на старой версии — ощутимая просадка. К вопросу видимости это отношения не имеет, но если вы строите мониторинг или экспорт через custom_fields на версии старше 4.6.7, ограничивайте выборку фильтрами GraphQL, а не выгружайте всё разом.
Практический вывод из обеих секций один: неважно, каким клиентом идёт запрос — curl, Python-скрипт на requests, Zabbix-интеграция или GraphQL-клиент во внутреннем дашборде. Если у учётной записи, от имени которой выполняется запрос, есть право View на объект, она получит все его custom fields целиком, независимо от того, что настроено в ui_visible.
- query { site_list(filters: {status: STATUS_ACTIVE}) { name status custom_fields } }
Это не баг: сообщество NetBox уже просит отдельный тип поля для секретов
29 мая 2026 года в основном репозитории netbox-community/netbox на GitHub пользователь damsitt открыл Discussion #22334 с ровно этой проблемой. Компания завела около 200 площадок (Sites) и хранила код от двери серверной в custom field с говорящим названием Room Key. Field был выставлен ui_visible: Hidden, чтобы не мозолить глаза в карточке сайта рядовым пользователям — но любой, у кого была роль с правом просмотра Site (а таких пользователей, как обычно бывает в инвентарных системах, оказалось намного больше, чем предполагал администратор), мог получить значение через API одним запросом.
Автор обсуждения предложил не костыль, а нормальную фичу: отдельный тип поля Secret. Значение маскируется в интерфейсе точками, рядом — иконка «глаз» для раскрытия по клику, доступ к раскрытию завязан на отдельное разрешение (extras.view_sensitive_customfield в предложенном виде), и опционально — запись факта просмотра в журнал аудита. На сентябрь 2026 года обсуждение всё ещё открыто в категории Ideas, статус — предложение, не принятое решение и тем более не выпущенная функциональность. В релизах 4.6 (май 2026) и 4.7 (сентябрь 2026) такого типа поля нет — я проверял release notes обоих релизов отдельно, ничего похожего там не анонсировано. На момент моей проверки у обсуждения не было ни одного ответа мейнтейнеров.
Это важный сигнал для тех, кто уже держит в NetBox что-то чувствительное «на Hidden»: рассчитывать, что маскирование появится само собой в следующем минорном релизе, не стоит. Функция даже не в roadmap, а на стадии обсуждения сообществом — сроков нет, и решение может не пройти вовсе (в Discussions у NetBox регулярно закрываются идеи, которые команда считает избыточными для ядра продукта в пользу плагинов).
Что реально скрывает Hidden, а что нет — сравнение
Чтобы не гадать каждый раз, что именно контролируют ui_visible и ui_editable, я свёл поведение в одну таблицу — она у меня висит в заметках на каждом внедрении NetBox, где заказчик просит завести чувствительные поля.
| Место | ui_visible: Hidden | ui_editable: No / Hidden | Скрывает? |
|---|---|---|---|
| Карточка объекта в веб-UI | Поле не отображается в блоке Custom Fields | — | Да |
| Форма редактирования объекта в веб-UI | — | Поле не выводится или выводится read-only | Да |
| Список объектов и CSV-экспорт таблицы в веб-UI | Колонки для Hidden-поля нет, в CSV из таблицы оно не попадает | — | Да |
REST API, GET .../{id}/ | Значение в custom_fields присутствует полностью | Значение доступно для чтения | Нет |
REST API, фильтр ?cf_<name>= | Поле участвует в фильтрации | — | Нет |
REST API, PATCH/PUT (если ui_editable: No) | — | Значение меняется через API без ограничений | Нет |
GraphQL, запрос поля custom_fields | Значение возвращается в ответе | Значение доступно | Нет |
Шаблон экспорта (ExportTemplate, obj.cf) | Значение доступно в Jinja2-шаблоне | — | Нет |
| Legacy Django admin | С 4.0 отключён по умолчанию (DJANGO_ADMIN_ENABLED) | — | Не применимо |
Обратите внимание на строку про ui_editable: No. Многие считают, что раз поле нельзя поменять в форме, то его нельзя поменять и вообще. Это не так: документация прямо оговаривает, что настройки видимости и редактируемости не влияют на REST и GraphQL API, и в исходниках NetBox сериализатор custom_fields действительно не смотрит ни на ui_visible, ни на ui_editable. PATCH-запрос от пользователя с правом change на объект перезапишет значение «нередактируемого» поля без ошибки. То есть через ui_editable нельзя защитить ни чтение, ни даже запись — это чисто интерфейсная настройка, как и Hidden.
Кейс: код от серверной в custom field у «Репутация Плюс»
PR-агентство «Репутация Плюс» (34 рабочих места) развернуло NetBox в начале 2026 года — вело в нём учёт сетевого оборудования и точек подключения в офисе и на арендованной серверной стойке в дата-центре. Администратор компании завёл на объекте Site (стойка в ЦОД) custom field «Код от двери серверной» — четырёхзначный PIN электронного замка, чтобы инженеры не звонили каждый раз на ресепшен ЦОД. Поле выставили ui_visible: Hidden, потому что не хотели, чтобы код мелькал в карточке сайта у всех, кто заходит в NetBox посмотреть список оборудования — а таких в компании было немало: и IT-подрядчик, и штатный сисадмин, и офис-менеджер, который вносил инвентарные номера.
В сентябре 2026 года, в рамках планового аудита ИБ (мы делали его для «Репутация Плюс» по отдельному договору), я выгрузил список ролей и разрешений NetBox и обнаружил, что группа readonly-staff — 12 учётных записей, изначально заведённых, чтобы сотрудники могли смотреть, какое оборудование за ними закреплено, — имела право dcim.view_site без всяких ограничений через constraints. Один запрос GET /api/dcim/sites/3/ от имени любой из этих 12 учёток отдавал custom_fields.kod_ot_dveri открытым текстом. Поле было создано в феврале 2026-го — то есть код был доступен через API семь месяцев, и я не могу утверждать, что им никто не воспользовался: журнал доступа в ЦОД у клиента не сверялся с журналом обращений к API NetBox, потому что второго журнала попросту не было — API-логирование в базовой поставке NetBox не пишет тело ответа.
Решение было простым и заняло один день: код от двери убрали из custom field вообще, физический замок перепрограммировали на новый PIN, а сам PIN и подобные ему значения (коды доступа, временные пароли от коммутаторов на время пусконаладки) вынесли в корпоративный менеджер паролей с ограниченным доступом по ролям. В NetBox для стойки в ЦОД оставили только ссылку-заметку «код доступа — см. хранилище секретов, обратиться к дежурному инженеру» — то есть сам факт, что код существует и где искать, остался, а значение — нет. Заодно я сократил группу readonly-staff до constraint по конкретному tenant — та же логика, что я разбирал при аудите неактивных учёток Active Directory: права раздаются на вырост «чтобы работало», а потом никто не возвращается их сузить.
Что делать вместо Hidden: где на самом деле граница доступа в NetBox
Разграничение доступа в NetBox — это система permissions: тип объекта, действие (View/Add/Change/Delete), группа или пользователь и constraints — JSON-фильтр в синтаксисе Django QuerySet ({"status": "active"}, {"region__name": "Europe"}), который сужает permission до подмножества объектов. Это не список доступа уровня сети вроде ACL, где можно разрешить конкретный протокол на конкретный порт, — constraint в NetBox сужает набор объектов целиком, а не набор полей внутри объекта. Если у роли есть dcim.view_site хоть с каким-то constraint, который пропускает конкретный сайт, — пользователь получит этот сайт целиком, включая все custom fields, скрытые или нет. Ограничить доступ к одному-единственному полю объекта, оставив остальные видимыми той же роли, штатными permissions NetBox нельзя — это не Postgres row-level security с политиками на колонку, а модель уровня объекта.
Прямой ответ на вопрос, можно ли хранить в custom field код доступа, пароль или любой другой секрет: нет, нельзя — ни при каком значении ui_visible. Любое поле объекта, включая custom fields, доступно через REST и GraphQL API каждому, у кого есть право View на этот объект, а такое право в реальных компаниях почти всегда шире, чем кажется администратору при заведении поля. Для секретов нужен отдельный инструмент с моделью доступа на уровне значения — корпоративный менеджер паролей (Bitwarden, Vaultwarden, HashiCorp Vault и подобные) с раздельными правами на конкретную запись, не на весь список. В NetBox в этом случае имеет смысл хранить только неконфиденциальную ссылку на запись — «где искать», а не «что искать».
Если задача шире одной статьи и вы только разворачиваете NetBox с нуля для учёта инфраструктуры — сразу закладывайте отдельный контур для чувствительных данных, а не добавляйте custom fields по ходу дела: переносить уже заполненные секреты из NetBox в secret-менеджер и одновременно ревизировать права доступа задним числом — работа на порядок дольше, чем спроектировать это с нуля с самого внедрения.
Частые вопросы
Ui_visibility ещё существует в NetBox или его убрали совсем?
Поле `ui_visibility` заменили в версии 3.7 (29 декабря 2023 года) на два отдельных параметра — `ui_visible` и `ui_editable`. В текущей версии (4.7, сентябрь 2026) `ui_visibility` в модели custom field уже нет, значения при апгрейде с более старых версий переносятся автоматически.
Можно ли скрыть custom field конкретно от GraphQL, оставив его в REST API?
Нет, штатными средствами NetBox — нельзя. И REST, и GraphQL API читают одни и те же данные модели, настроек видимости отдельно для каждого протокола не существует. Ограничить можно только на уровне того, кому вообще разрешён View объекта.
Fields plugin или кастомный код резолвера может это исправить?
Теоретически да — можно написать собственный сериализатор или GraphQL-резолвер, который вручную вырезает конкретные ключи из `custom_fields` по условию. Но это выход за пределы штатного NetBox, требует поддержки при каждом обновлении платформы и не защищает REST-эндпоинт, если резолвер правили только для GraphQL (и наоборот).
А что с экспортом объектов и bulk-операциями — там тоже видно скрытые поля?
Зависит от способа. CSV-экспорт таблицы из веб-интерфейса строится по колонкам, а колонки для Hidden-поля нет, поэтому туда оно не попадает. Но REST API (включая list-запросы с limit=0), GraphQL и шаблоны экспорта (ExportTemplate, обращение obj.cf) отдают значение полностью.
Если поставить ui_editable: No, значение точно нельзя будет изменить через API?
Нет. ui_editable управляет только формой редактирования в веб-интерфейсе. Документация прямо говорит, что эти настройки не влияют на REST и GraphQL API, и PATCH/PUT от пользователя с правом change на объект изменит значение без ошибки. Защищать запись нужно правами (permissions и constraints), а не ui_editable.
Появится ли в NetBox официальный тип поля для секретов?
На сентябрь 2026 года — нет, это только предложение в Discussion #22334 (открыто 29 мая 2026), статус Ideas без решения и без сроков. Планировать миграцию секретов из custom fields в расчёте на будущий релиз не стоит — переносите их в отдельный секрет-менеджер уже сейчас.
Источники
- NetBox Labs Docs — Custom Fields (v4.4) — Параметры ui_visible (Always/If Set/Hidden) и ui_editable (Yes/No/Hidden), дословная цитата «this setting has no impact on the REST or GraphQL APIs»: https://netboxlabs.com/docs/netbox/v4.4/customization/custom-fields/ — сверено также по неверсионированной current-копии https://netboxlabs.com/docs/netbox/customization/custom-fields/
- NetBox Labs — NetBox v3.7 Released — Дата релиза 29 декабря 2023 года и замена единого поля ui_visibility на ui_visible/ui_editable: https://netboxlabs.com/blog/netbox-v370-released/
- GitHub Discussion #22334, netbox-community/netbox — Автор damsitt, открыто 29 мая 2026: ~200 площадок, custom field Room Key видно через API пользователям с правом View на Site, предложение типа поля Secret с маскированием и отдельным правом extras.view_sensitive_customfield, статус Open/Ideas: https://github.com/netbox-community/netbox/discussions/22334
- NetBox Labs Docs — REST API (v4.4) — Структура ответа объекта с ключом custom_fields, синтаксис фильтрации по кастомным полям через префикс cf_: https://netboxlabs.com/docs/netbox/v4.4/integrations/rest-api/ и https://netboxlabs.com/docs/netbox/reference/filtering/
- GitHub Issue #22813, netbox-community/netbox — GraphQL custom_fields на list-запросах давал по дополнительному SQL-запросу на объект (N+1), исправлено в NetBox 4.6.7 (PR #22824): https://github.com/netbox-community/netbox/issues/22813
- NetBox Labs Docs — Release Notes и Permissions — Дата и состав релизов 4.6 (май 2026) и 4.7 (4.7.0 — 2 сентября 2026, 4.7.1 — 15 сентября 2026); в 4.0 legacy Django admin отключён по умолчанию; модель object-based permissions и constraints как Django QuerySet-фильтр, без ограничений на уровне отдельного поля: https://netboxlabs.com/docs/netbox/release-notes/ и https://netboxlabs.com/docs/netbox/administration/permissions/


