АйТи Фреш
Главная / Статьи / Серверы и хостинг
Серверы и хостинг

Proxmox VE 9 выкинул VM.Monitor: чем заменить права на Guest Agent, чтобы скрипты снова видели IP

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~23 мин чтения
Proxmox VE 9 выкинул VM.Monitor: чем заменить права на Guest Agent, чтобы скрипты снова видели IP
Иллюстрация к статье «Proxmox VE 9 выкинул VM.Monitor: чем заменить права на Guest Agent, чтобы скрипты снова видели IP».

Обновили кластер до Proxmox VE 9, и сервисный аккаунт мониторинга перестал отдавать IP-адреса виртуалок. В веб-морде на месте адресов надпись про недостающие права, в скрипте — 403 Permission check failed. Причина одна: привилегия VM.Monitor в девятке удалена, а вместо неё появилось пять раздельных VM.GuestAgent.* плюс Sys.Audit для HMP-монитора. Ниже — точная карта замены, где именно ломается автоматизация (спойлер: дело часто не в правах, а в том, какой эндпоинт дёргает ваш скрипт), готовые команды pveum и разбор небольшого кластера ландшафтного бюро «Зелёный квадрат», который я вытаскивал из этой ямы.

Что именно произошло при апгрейде и почему это тихая поломка

Proxmox VE 8 обходился одной привилегией VM.Monitor. Она закрывала сразу две очень разные вещи: доступ к QEMU-монитору (HMP) и доступ к QEMU Guest Agent. Одна привилегия — и на «покажи мне IP-адреса гостя», и на «выполни произвольную команду в мониторе гипервизора». Название было ровно настолько же неоднозначным, насколько ими были последствия. В девятке это разделили: HMP переехал на Sys.Audit/Sys.Modify, а гостевой агент получил собственное семейство VM.GuestAgent.*.

Ломается это тихо, и вот почему. Роли в Proxmox хранятся в /etc/pve/user.cfg плоскими строками вида role:ITF-Inventory:VM.Audit,VM.Monitor:. При старте демона конфиг разбирается, и на неизвестную привилегию парсер не падает, а пишет предупреждение в лог и просто выбрасывает её из роли. В исходниках PVE::AccessControl это буквально одна строка: warn "user config - ignore invalid privilege '$priv'\n". То есть роль остаётся, пользователь остаётся, ACL остаётся — а прав внутри роли стало на одну меньше. Никакого баннера в интерфейсе, никакого письма.

Сразу успокою по одному пункту, вокруг которого в форумах много паники: сам апгрейд от этого не падает и кластер не встаёт. Виртуалки продолжают работать, бэкапы запускаются, веб-интерфейс под root@pam выглядит абсолютно нормально. Проблема локализована ровно в сервисных учётках и кастомных ролях — а это, к сожалению, именно тот слой, который никто не проверяет глазами после обновления, потому что «оно же само работало».

Дальше по классике. Скрипт инвентаризации отвечает 403, Zabbix теряет часть метрик, Terraform-провайдер начинает ругаться на отсутствующую привилегию, а в CMDB у половины машин остаются вчерашние адреса. Причём отдельная подлость: с локальной консоли под root@pam всё работает, потому что root@pam ACL не проверяет вообще. Админ заходит по SSH, делает qm agent 101 network-get-interfaces, видит красивый JSON и делает вывод «агент живой, значит проблема в мониторинге». И уходит копать не туда.

Если после обновления вы увидели в UI сообщение про VM.Monitor — сначала обновите кэш браузера и переустановите пакет: apt install --reinstall pve-manager. В ранних 9.0.x в UI оставался старый JS, который проверял уже несуществующую привилегию, и сообщение появлялось даже у тех, кому прав хватало.

Карта замены: что на что меняется один в один

Скрипт предупреждения в pve8to9 формулирует замену максимально точно, и я приведу её как есть, потому что это первоисточник: «Proxmox VE 9 replaced the ambiguously named 'VM.Monitor' privilege with 'Sys.Audit' for QEMU HMP monitor access and new dedicated 'VM.GuestAgent.*' privileges for access to a VM's guest agent». Дальше перечисляются подпривилегии: Audit для всех информационных команд, FileRead и FileWrite для чтения и записи файлов, FileSystemMgmt для freeze/thaw/trim, Unrestricted — для всего, включая выполнение команд.

На практике это выглядит так. VM.GuestAgent.Audit — информационные команды: network-get-interfaces, get-osinfo, get-host-name, get-time, get-timezone, get-users, get-fsinfo, get-vcpus, ping. Это ровно то, что нужно инвентаризации и мониторингу. VM.GuestAgent.FileRead и VM.GuestAgent.FileWrite — чтение и запись файлов внутри гостя через агент (у file-read стоит потолок 16 МиБ на ответ). VM.GuestAgent.FileSystemMgmt — fsfreeze-freeze, fsfreeze-thaw, fstrim; это то, что нужно системе резервного копирования для консистентного снапшота. VM.GuestAgent.Unrestricted — произвольные команды агента, в том числе guest-exec, guest-exec-status и set-user-password.

И есть отдельная приятная деталь, которую многие пропускают: команды агента, меняющие состояние ВМ — shutdown, suspend-ram, suspend-disk, suspend-hybrid — требуют не GuestAgent-права, а VM.PowerMgmt (либо Unrestricted, который перекрывает всё). Это логично: если у вас уже есть право гасить машину штатно, глупо запрещать делать то же самое через агент.

Ещё один момент, который стоит проговорить: разделение на FileRead и FileWrite — это не бюрократия ради бюрократии. Раньше право «прочитать /etc/shadow из гостя» и право «положить туда свой файл» были неразличимы. Теперь CI-пайплайн, который доставляет конфиг в виртуалку, получает только FileWrite и физически не может выкачать оттуда данные, а система аудита получает только FileRead и не может ничего испортить. Для компаний, где есть хоть какие-то требования по разделению доступа, это аргумент за апгрейд сам по себе.

Важная механика проверки: каждый именной эндпоинт агента зарегистрирован как «нужная привилегия ИЛИ VM.GuestAgent.Unrestricted, any => 1». То есть Unrestricted — это надмножество всего остального, и выдавать его «в довесок» к Audit бессмысленно и вредно. У fsfreeze-status проверка мягче: подойдёт любая из Audit, FileSystemMgmt, Unrestricted.

Встроенные роли уже обновлены: PVEAuditor получил VM.GuestAgent.Audit, PVEVMUser — FileRead, FileWrite и FileSystemMgmt, PVEVMAdmin и PVEAdmin — Unrestricted. Если вам просто нужна инвентаризация IP, отдельную роль можно не городить: хватит PVEAuditor на /vms. Но помните обратную сторону — стандартный PVEVMUser в девятке умеет писать файлы внутрь гостевой ОС.
Proxmox VE 9 выкинул VM.Monitor: чем заменить права на Guest Agent, чтобы скрипты снова видели IP — схема
Схема к статье. Открыть схему в полном размере

Главная ловушка: старый эндпоинт /agent требует Unrestricted

Вот здесь ломается больше всего автоматизации, и почти никто не догадывается. В API есть два разных способа спросить у агента одно и то же. Новый — именной эндпоинт: GET /nodes/{node}/qemu/{vmid}/agent/network-get-interfaces. Ему достаточно VM.GuestAgent.Audit. Старый, оставленный для совместимости — POST /nodes/{node}/qemu/{vmid}/agent с параметром command=network-get-interfaces. Этот старый эндпоинт зарегистрирован ровно одной строкой: register_command('', 'POST', 'VM.GuestAgent.Unrestricted'). То есть любой запрос через него требует полных прав на агент, независимо от того, насколько безобидную команду вы просите.

Отсюда и берётся типовой сценарий, из-за которого админы выдают Unrestricted и потом живут с этим годами. Человек добавляет роли VM.GuestAgent.Audit — не работает. Добавляет FileRead — не работает. В сердцах выдаёт Unrestricted — заработало. Вывод делается неверный: «в девятке для IP нужен Unrestricted». А на самом деле нужно было поменять один HTTP-метод и один URL в скрипте.

Проверяется это за минуту. Под сервисным токеном с одной только VM.GuestAgent.Audit выполните два запроса:

# нужен только VM.GuestAgent.Audit — вернёт список интерфейсов
curl -sS -H "Authorization: PVEAPIToken=svc-inv@pve!zbx=SECRET" \
  https://pve1.local:8006/api2/json/nodes/pve1/qemu/101/agent/network-get-interfaces

# требует VM.GuestAgent.Unrestricted — тот самый legacy-эндпоинт
curl -sS -H "Authorization: PVEAPIToken=svc-inv@pve!zbx=SECRET" \
  -d command=network-get-interfaces \
  https://pve1.local:8006/api2/json/nodes/pve1/qemu/101/agent

Первый вернёт JSON с интерфейсами. Второй — 403. Если ваш скрипт, Ansible-роль или самописный экспортёр ходит вторым способом, права тут ни при чём: правьте клиента. Кстати, локальная обёртка qm agent <vmid> <command> — это алиас на qm guest cmd, и внутри она вызывает как раз обработчик старого совместимого эндпоинта. Но qm и pvesh на ноде работают от root@pam, поэтому из консоли разницу вы не увидите никогда — воспроизводить проблему нужно только через HTTPS API тем же токеном, которым ходит скрипт.

Есть и третья причина 403, которая маскируется под ту же самую проблему, — API-токены. По умолчанию токен создаётся с privsep=1, то есть его права задаются отдельным ACL и НЕ наследуются от пользователя автоматически. Вы аккуратно выдали роль пользователю svc-inv@pve, проверили через веб-интерфейс под ним — работает. А скрипт ходит токеном svc-inv@pve!zbx, у которого своего ACL нет, и получает отказ. Правило простое: выдавайте роль двумя строками — и на пользователя, и на токен.

Не выдавайте VM.GuestAgent.Unrestricted «чтобы заработало», пока не проверили, каким эндпоинтом ходит клиент. Unrestricted открывает guest-exec — выполнение команд внутри гостевой ОС от root/SYSTEM на всех ВМ под этим ACL. Если после добавления Audit всё ещё 403 — сначала смотрите метод и URL запроса, затем ACL самого токена, и только потом права роли.
Памятка: Главная ловушка: старый эндпоинт /agent требует Unrestricted — схема
Памятка: Главная ловушка: старый эндпоинт /agent требует Unrestricted. Открыть схему в полном размере

Как это выглядело у клиента: две ноды, 14 ВМ и сутки устаревших адресов

Ландшафтное бюро «Зелёный квадрат», 19 рабочих мест: архитекторы и дендрологи в AutoCAD и ArchiCAD, сметчик, бухгалтерия. Весь «серверный парк» — две ноды Proxmox в шкафу в офисе, 14 виртуалок: файловый сервер с проектами, 1С, терминальный сервер для удалённых сотрудников, сервер сетевых лицензий, контроллер домена и несколько служебных машин. Qemu-guest-agent стоит на 11 из них. Инвентаризация — небольшой сборщик на Python, ходит по API раз в 10 минут и складывает связку «VMID → hostname → IP → MAC» в базу, из неё живут витрина для админа и правила фаервола для подключений к серверу лицензий. Сервисный токен, отдельная кастомная роль на /vms с VM.Audit и VM.Monitor. Всё это спокойно работало с восьмёрки.

Апгрейд 8.4 → 9 прошли по официальному гайду, pve8to9 отработали, но результат читали глазами и строку про custom role пропустили — проверка отдаёт по этому пункту FAIL, а не WARN, и это стоило разбирательства. Заметили только на следующий день, когда после смены адреса у терминального сервера правило фаервола к серверу лицензий осталось старым и у двух проектировщиков перестал запускаться ArchiCAD. По факту сборщик молча падал в 403 и писал в лог свою же строчку «agent unavailable» — обработчик ошибок не различал «агента нет» и «прав нет». Реальный возраст данных на момент разбора — почти сутки по всем 11 машинам с агентом.

Разбор занял минут сорок. Сначала journalctl -u pvedaemon дал ключ: ignore invalid privilege 'VM.Monitor'. Дальше pveum role list показал, что в роли осталась одна VM.Audit. Добавили VM.GuestAgent.Audit — сборщик всё равно 403. Вот тут и всплыл второй слой: клиент ходил старым POST /agent, потому что так было написано ещё в 2022 году по примеру из чужого форума, и никто этот код с тех пор не открывал. Переписали на именной GET — заработало без единой дополнительной привилегии.

Итог: роль ITF-Inventory — VM.Audit и VM.GuestAgent.Audit, ничего больше. Токен пересоздали с privsep, ACL повешен на /vms отдельно для пользователя и для токена. Отдельно поправили обработчик ошибок в сборщике: теперь 403 и «агент не отвечает» — два разных состояния, и 403 уходит алертом в Telegram, а не в тишину. Заодно всплыло, что у резервного копирования была отдельная учётка со старым VM.Monitor; ей выдали VM.GuestAgent.FileSystemMgmt, и консистентные снапшоты с fsfreeze вернулись на место — про них, кстати, никто не сообщал, бэкапы просто тихо стали менее консистентными.

Проверьте свою систему резервного копирования отдельно. Потеря VM.GuestAgent.FileSystemMgmt не роняет бэкап — он продолжает идти, просто без fsfreeze, то есть без гарантии консистентности файловой системы внутри гостя. Это худший тип поломки: тихий и всплывающий только при восстановлении.

Рецепт: выдать ровно столько прав, сколько нужно

Я исхожу из простого правила: сервисному аккаунту мониторинга нельзя иметь возможность выполнять код внутри гостевых ОС. В восьмёрке это правило было невыполнимо технически — VM.Monitor давал всё сразу. В девятке оно наконец выполнимо, и это, на мой взгляд, лучшее, что случилось с моделью прав Proxmox за последние несколько лет. Не поленитесь воспользоваться.

Ниже готовая последовательность. Роль для инвентаризации и мониторинга: только чтение информационных команд агента. Отдельная роль для бэкапа: freeze/thaw/trim без чтения файлов. Токен обязательно с privsep, чтобы права токена задавались собственным ACL, а не наследовались от пользователя целиком.

# 1. роль только на чтение данных агента
pveum role add ITF-Inventory --privs "VM.Audit,VM.GuestAgent.Audit"

# 2. роль для системы резервного копирования
pveum role add ITF-Backup --privs "VM.Audit,VM.Backup,VM.GuestAgent.FileSystemMgmt"

# 3. сервисный пользователь и токен с разделением привилегий
pveum user add svc-inv@pve
pveum user token add svc-inv@pve zbx --privsep 1

# 4. ACL на /vms — и пользователю, и токену отдельно
pveum acl modify /vms --users svc-inv@pve --roles ITF-Inventory
pveum acl modify /vms --tokens 'svc-inv@pve!zbx' --roles ITF-Inventory

# 5. добавить привилегию в существующую роль, НЕ затерев остальные
pveum role modify ITF-Inventory --privs "VM.GuestAgent.Audit" --append 1

Про уровень ACL. Вешать на / — лень, которая потом стреляет: Sys.Modify на корне даёт заметно больше, чем кажется. Для гостевого агента достаточно /vms, а если у вас есть логическое деление — /pool/{pool} или точечно /vms/{vmid}. Пропагация по умолчанию включена; для /vms это нормально, для / — подумайте дважды.

На что можно забить — сразу скажу, чтобы не тратили время. Не нужно перелопачивать встроенные роли: Proxmox пересобирает их сам из внутренних групп привилегий, и в девятке они уже содержат нужные VM.GuestAgent.*. Не нужно вычищать VM.Monitor из /etc/pve/user.cfg руками — это кластерный файл на pmxcfs, править его текстовым редактором я не рекомендую, а вреда от лишней игнорируемой строки нет. Не нужно и заново раздавать права обычным пользователям, которые ходят только в веб-интерфейс: если у них PVEVMUser или PVEAuditor, всё уже на месте.

И про миграцию существующих ролей. pveum role modify без --append 1 заменяет список привилегий целиком — это самый частый способ случайно снести роли половину прав. Если правите живую роль, сначала снимите слепок: pveum role list --output-format json > /root/roles-before.json. Займёт секунду, а откатываться потом гораздо приятнее.

Секрет API-токена pveum показывает ровно один раз — при создании. Сразу положите его в менеджер паролей, а не в скрипт открытым текстом. И помните: `pveum role modify` без `--append 1` заменяет список привилегий целиком, так что пятая команда без этого флага оставит в роли одну VM.GuestAgent.Audit.
Памятка: Рецепт: выдать ровно столько прав, сколько нужно — схема
Памятка: Рецепт: выдать ровно столько прав, сколько нужно. Открыть схему в полном размере

HMP-монитор: Sys.Audit — это не «то же самое, что было»

Вторая половина бывшего VM.Monitor — доступ к QEMU HMP через POST /nodes/{node}/qemu/{vmid}/monitor. Сам эндпоинт теперь требует Sys.Audit или Sys.Modify на /vms/{vmid}. Но одной проверкой на входе дело не заканчивается: внутри есть отдельная таблица прав по каждой команде монитора, и это принципиально новая вещь, которой в восьмёрке не было.

Команды разложены на три корзины. Первая — «без дополнительных прав»: help, ?, info. То есть info block, info network, info status и прочие info-* доступны любому, кто прошёл проверку Sys.Audit на входе. Вторая — требующие Sys.Modify, причём проверка идёт по пути / (корень), а не по конкретной ВМ: сюда попали stop, cont, savevm, loadvm, delvm, sendkey, set_link, system_reset, system_powerdown, qom-get, qom-list, print, x, xp и десятки других. Третья — root-only, доступные исключительно root@pam: migrate, drive_mirror, drive_backup, device_add, chardev-add, nbd_server_start, dump-guest-memory, gdbserver, memsave, logfile и всё, что позволяет писать в произвольный файл на хосте или дотянуться до сети хоста.

Отдельно отмечу поведение по умолчанию для незнакомых команд: если команды нет в таблице прав, она автоматически считается root-only, и обычный пользователь получит отказ с внятным текстом. Мне такой подход нравится — при обновлении QEMU новая недокументированная команда не окажется случайно доступной сервисному аккаунту.

Практический вывод: если ваш скрипт использовал VM.Monitor ради info-команд HMP — вам нужен Sys.Audit на /vms/{vmid}, и этого хватит. Если он делал что-то меняющее — вам нужен Sys.Modify на корне, а это очень много прав, и я бы такой скрипт вообще переписал на нормальные API-вызовы Proxmox вместо HMP. Если он делал migrate или drive_mirror руками через монитор — под сервисным аккаунтом это больше не заведётся никогда, только root@pam.

Не пытайтесь «восстановить как было», выдав сервисному аккаунту Sys.Modify на /. Это право на изменение настроек узлов кластера, а не только на пару HMP-команд. Если очень нужно — Sys.Audit на /vms/{vmid} и переписанный клиент.

Чек-лист: до апгрейда, после апгрейда, и что мониторить дальше

До обновления всё решается одной командой. pve8to9 --full прогоняется на каждой ноде и, помимо прочего, проверяет кастомные роли на наличие VM.Monitor. Важно: по этому пункту он отдаёт FAIL, а не предупреждение, так что в общем ворохе вывода его несложно принять за критику по другому поводу. Читайте текст, а не только цвет. Специальные (встроенные) роли проверка пропускает — их Proxmox обновит сам.

После обновления первым делом смотрю логи демонов на предмет выброшенных привилегий и снимаю актуальный список ролей. Строка про ignore invalid privilege появляется при каждой перечитке user.cfg, так что она никуда не денется, пока вы не отредактируете роль. Заодно это удобный способ найти роли, до которых руки не дошли: пока правка не сделана, VM.Monitor физически остаётся в /etc/pve/user.cfg и просто игнорируется на разборе.

И третий пункт, который я считаю обязательным независимо от Proxmox: научите мониторинг отличать 403 от «сервис недоступен». В истории с «Зелёным квадратом» сутки устаревших адресов и два проектировщика без ArchiCAD случились ровно потому, что обработчик ошибок сваливал два разных состояния в одно сообщение. Права ломаются редко, но ломаются молча, и единственная защита — явный алерт на код ответа.

Про сроки. Девятка вышла 5 августа 2025 года на базе Debian 13 «Trixie»; актуальная на сегодня ветка — 9.2, выпущенная 21 мая 2026 года (Debian 13.5, ядро 7.0, QEMU 11.0). Так что если вы всё ещё на восьмёрке — планируйте переход, и заложите в план полчаса на аудит ролей. Это не тот случай, когда можно «разобраться по ходу»: сломается не апгрейд, а автоматизация, и заметите вы это не сразу.

Спорный момент, скажу честно: единого мнения о том, где вешать ACL для сервисных аккаунтов, в сообществе нет. Кто-то держит всё на /vms с пропагацией, кто-то расписывает по пулам. Я за /vms для read-only ролей и точечные пути для всего, что умеет писать. Главное — не /.
Порядок действий: Чек-лист: до апгрейда, после апгрейда, и что мониторить дальше — схема
Порядок действий: Чек-лист: до апгрейда, после апгрейда, и что мониторить дальше. Открыть схему в полном размере

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

Какую привилегию выдать, чтобы скрипт снова получал IP-адреса виртуалок?

VM.GuestAgent.Audit на нужном пути ACL (обычно /vms). Этого достаточно для команды network-get-interfaces, а также для get-osinfo, get-host-name, get-fsinfo и остальных информационных команд агента. Если у вас нет собственной роли, подойдёт встроенная PVEAuditor — в PVE 9 в неё уже включён VM.GuestAgent.Audit.

Выдал VM.GuestAgent.Audit, но всё равно 403. Почему?

Почти наверняка ваш клиент ходит на legacy-эндпоинт POST /nodes/{node}/qemu/{vmid}/agent с параметром command=... Он зарегистрирован с требованием VM.GuestAgent.Unrestricted и никаких послаблений не имеет. Переведите клиента на именной эндпоинт GET /nodes/{node}/qemu/{vmid}/agent/network-get-interfaces — прав Audit хватит. Вторая по частоте причина — API-токен с privsep, которому не выдали собственный ACL: права пользователя на токен автоматически не распространяются.

Чем VM.GuestAgent.Unrestricted опасен на самом деле?

Он открывает guest-exec, guest-exec-status и set-user-password. Это выполнение произвольных команд внутри гостевой ОС от имени пользователя, под которым работает qemu-guest-agent — как правило, root или SYSTEM. Фактически это удалённое выполнение кода на всех ВМ, попавших в ACL, в обход сетевых ограничений, фаервола и SSH-ключей. Ради получения IP-адресов такое право выдавать точно не стоит.

Что произойдёт с ролью, в которой остался VM.Monitor, после апгрейда?

Роль не исчезнет и кластер не сломается. При разборе /etc/pve/user.cfg неизвестная привилегия просто игнорируется с предупреждением в лог (ignore invalid privilege), а роль продолжает работать с оставшимися правами. Строка VM.Monitor физически останется в файле, пока вы не отредактируете роль через pveum или веб-интерфейс.

Как найти все проблемные роли заранее, до обновления?

Запустите pve8to9 --full — он проверяет кастомные (не встроенные) роли на наличие VM.Monitor и по этому пункту отдаёт FAIL с пояснением, чем заменить. Дополнительно полезно посмотреть сырой конфиг: grep '^role:' /etc/pve/user.cfg. Встроенные роли проверять не нужно, Proxmox обновляет их сам.

Мой скрипт использовал VM.Monitor для HMP-команд, а не для агента. Что теперь?

Эндпоинт /vms/{vmid}/monitor требует Sys.Audit или Sys.Modify на /vms/{vmid}. Если вы дёргали только info-команды — Sys.Audit достаточно. Команды, меняющие состояние (stop, cont, savevm, sendkey и т.п.), требуют Sys.Modify на корневом пути '/', а migrate, drive_mirror, device_add и подобные стали доступны исключительно root@pam. Практический совет: такие сценарии лучше переписать на штатные API-вызовы Proxmox вместо HMP.

Влияет ли это на резервное копирование?

Да, и это самая тихая часть проблемы. За fsfreeze-freeze, fsfreeze-thaw и fstrim отвечает VM.GuestAgent.FileSystemMgmt. Если учётка бэкапа потеряла VM.Monitor и новую привилегию ей не выдали, копирование продолжит идти, но без заморозки файловых систем внутри гостя — то есть без гарантии консистентности. Обнаружится это обычно только при восстановлении.

Столкнулись с похожей задачей? Обращайтесь — решим

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

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи