Дисклеймер. Все, что описано ниже, мысленный эксперимент и собранный на коленке стенд «а что, если». Никакая реальная компания, команда или процесс найма здесь не описаны, любые совпадения случайны. IP‑адреса, домены и пароли в примерах вымышлены. Если решите повторить что‑то подобное на живых людях, сначала поговорите с юристом и предупредите кандидатов.
Типичное собеседование в 2026 году: кандидат расшарил экран, бодро рассуждает о сагах и шардировании, проходит live‑кодинг сессии без единой паузы. А если еще и хороший актер, то можно не сразу заподозрить его в читинге и использовании разного рода помощников. А там и до волчизма недалеко.
Инструменты вроде Ghost GPT и его многочисленные аналоги умеют слушать звонок, распознавать вопрос и показывать готовый ответ в окне, которое не попадает в захват экрана. Классические меры против них почти бессильны:
-
Демонстрация экрана — оверлей помощника исключен из захвата на уровне ОС.
-
Покажите, что у вас открыто — помощник может работать на втором мониторе, телефоне или в отдельном процессе без окна.
-
Прокторинг‑софт — тяжело, дорого, требует установки агента и сразу отпугивает сильных кандидатов.
-
Наблюдение за глазами и паузами — субъективно и легко объясняется волнением.
Но у всех этих помощников есть общая слабость: им нужна связь. Распознать речь, сходить в LLM и получить ответ, это все сетевые запросы.
А что если…
.на время интервью кандидат подключается к корпоративному VPN, то и весь его трафик проходит через сервера компании. Тогда интервьюер сможет видеть журнал запросов в реальном времени и может сопоставить их с тем, что происходит в разговоре.
Захватывать можно весь трафик целиком, но анализировать ip‑адреса в real‑time такое себе удовольствие. Поэтому логичнее анализировать DNS‑запросы, которые проще анализировать через web‑интерфейс, понятный HR и интервьюеру. На роль такого анализатора может подойти AdGuard (или любая иная альтернатива). Возможность «горячей» блокировки трафика прилагается.
Чтобы у VPN был естественный смысл, тестовые задания живут внутри него: доска для system design, live‑кодинга, и.т.д. Без VPN они просто не открываются. Побочный плюс — задания не утекают в паблик и не индексируются.
Дополнительные требования для этого решения:
-
Никакого стороннего софта у кандидата, только встроенный в ОС клиент. Соответственно ограничиваемся IKEv2/L2TP.
-
Отдельная учетная запись на каждого кандидата, чтобы события в журнале однозначно относились к конкретному интервью. После встречи доступ удаляется.
Архитектура
Кандидат и интервьюер подключаются к одному IKEv2-шлюзу. DNS кандидата уходит в AdGuard, HTTP к code‑server в nginx на loopback‑адресе, все остальное в интернет через NAT.
Шаг 1. IKEv2-сервер на strongSwan
Далее в примерах: 203.0.113.10 — IP‑адрес сервера, vpn.example.com — домен VPN, 10.10.10.0/24 — пул адресов клиентов. A‑запись vpn.example.com → 203.0.113.10 заведена заранее.
Выпуск сертификатов
В качестве примера используем Let’s Encrypt из коробки:
apt update && apt upgrade -yapt install -y strongswan strongswan-pki libcharon-extra-plugins \ iptables-persistent certbotcertbot certonly --standalone --key-type rsa --rsa-key-size 4096 \ -d vpn.example.comcp /etc/letsencrypt/live/vpn.example.com/fullchain.pem /etc/ipsec.d/certs/vpn-cert.pemcp /etc/letsencrypt/live/vpn.example.com/privkey.pem /etc/ipsec.d/private/vpn-key.pemchmod 600 /etc/ipsec.d/private/vpn-key.pem
Не забудьте deploy‑hook в certbot, который копирует обновленный сертификат и делает ipsec reload, иначе через 3 месяца все тихо сломается.
Конфиг /etc/ipsec.conf
config setup uniqueids=noconn ikev2-vpn auto=add keyexchange=ikev2 type=tunnel fragmentation=yes forceencaps=yes dpdaction=clear dpddelay=300s rekey=no left=%any leftid=@vpn.example.com leftcert=vpn-cert.pem leftsendcert=always leftsubnet=0.0.0.0/0 # весь трафик клиента в туннель right=%any rightid=%any rightsourceip=10.10.10.0/24 rightdns=203.0.113.10 # DNS = AdGuard eap_identity=%identity leftauth=pubkey rightauth=eap-mschapv2 ike=aes256-sha256-modp2048,aes128-sha1-modp1024 esp=aes256-sha256,aes128-sha1
Ключевые строки для нашей задачи leftsubnet=0.0.0.0/0 (full tunnel, а не split) и rightdns. Первая заставляет клиента гнать весь трафик через сервер, вторая выдает ему наш DNS‑сервер.
StrongSwan выдает клиентам адреса из пула начиная с .1. Поэтому в rightdns проще всего указать публичный IP сервера — запросы к нему все равно идут внутри туннеля.
Учетные записи кандидатов
/etc/ipsec.secrets — по учетке на каждого кандидата:
: RSA vpn-key.pemlogin : EAP "<passwd>"
Маршрутизация, NAT и firewall
Схема «плоская»: трафик клиентов просто маскарадится и выходит в интернет с IP самого сервера.
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-vpn.confsysctl --system# eth0 внешний интерфейс, см. `ip route | grep default`iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADEnetfilter-persistent saveufw allow 22/tcpufw allow 80/tcp # certbot и заглушка nginxufw allow 500/udpufw allow 4500/udpufw enablesystemctl restart strongswan-starteripsec statusall
Настройка клиента
|
Server address |
|
|
User authentication |
Username |
|
Username / Password |
выданные кандидату |
Альтернатива для mac — собрать .mobileconfig‑профиль на каждого кандидата: двойной клик, Install, готово.
Шаг 2. AdGuard: DNS, журнал и блокировки
AdGuard здесь делает три вещи: резолвит запросы клиентов, пишет их в Query Log и подменяет ответы для наших внутренних доменов (DNS rewrite, понадобится в шаге 3).
Установка
cd /optcurl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
Порт 53 обычно занят systemd-resolved. Отключаем его и прописываем серверу обычные резолверы:
systemctl disable --now systemd-resolvedrm -f /etc/resolv.confprintf 'nameserver 1.1.1.1\nnameserver 8.8.8.8\n' > /etc/resolv.conf
В мастере настройки (:3000) DNS‑сервер вешаем на 0.0.0.0:53, веб‑интерфейс на 0.0.0.0:3000. Дальше после расшифровки IPsec источником запроса будет адрес из пула, так что открываем 53 только для него:
ufw allow from 10.10.10.0/24 to any port 53 proto udpufw allow from 10.10.10.0/24 to any port 53 proto tcpufw allow from 10.10.10.0/24 to any port 3000 proto tcp # админка тоже только из VPN
Админку для интервьюеров тоже лучше держать за VPN или ходить через ssh‑туннель.
Проверка
Переподключаем VPN на клиенте и смотрим, какой сервер реально отвечает:
dig google.com | grep SERVER# ;; SERVER: 203.0.113.10#53
Запросы должны появиться в Query Log с клиентом 10.10.10.x.
Upstream и фильтры
В Settings → DNS settings ставим upstream по DoH, например https://dns.cloudflare.com/dns-query и https://dns.google/dns-query. Рекламные фильтр‑листы можно не включать, нам нужна не чистка рекламы, а чистый журнал и точечные блокировки по ситуации. А вот срок хранения Query Log стоит урезать до суток: держать чужую историю запросов дольше, чем нужно, незачем.
Шаг 3. Live‑coding в code‑server, доступный только из VPN
На примере code‑server разберем задачу self‑hosted решения для live‑coding.
Похожую схему можно использовать и для других песочниц для проверки разных навыков (system design, db, etc).
DNS rewrite и loopback‑адрес
Чтобы *.lab.example.com открывался только из VPN, вешаем на lo адрес из пула, который strongSwan никому не выдаст, 10.10.10.254:
cat >/etc/systemd/system/lab-loopback-ip.service <<'EOF'[Unit]Description=Loopback IP for lab servicesAfter=network.target[Service]Type=oneshotExecStart=/bin/sh -c 'ip addr add 10.10.10.254/32 dev lo 2>/dev/null || true'ExecStop=/bin/sh -c 'ip addr del 10.10.10.254/32 dev lo 2>/dev/null || true'RemainAfterExit=yes[Install]WantedBy=multi-user.targetEOFsystemctl daemon-reload && systemctl enable --now lab-loopback-ip
Затем в AdGuard (Filters → DNS rewrites) добавляем *.lab.example.com → 10.10.10.254. Клиент в VPN получает внутренний адрес и добирается до него по туннелю. Для всех остальных учебный домен либо не резолвится, либо ведет на публичный IP, где nginx отдает 403.
code‑server в Docker
code‑server это VS Code (сборка OSS) в браузере. Кандидат получает привычную IDE с терминалом, а интервьюер может открыть тот же адрес и следить за кодом. Встроенных AI‑помощников в нем нет.
Нужные языки и расширения ставим заранее, на этапе сборки образа:
# /opt/lab/DockerfileFROM codercom/code-server:latestUSER rootRUN apt-get update \ && apt-get install -y --no-install-recommends python3 python3-pip \ && rm -rf /var/lib/apt/lists/*USER coderRUN code-server --install-extension ms-python.python
И compose:
# /opt/lab/docker-compose.ymlservices: code-server: build: . restart: unless-stopped environment: - PASSWORD=${CODE_SERVER_PASSWORD} # из .env, новый на каждое интервью command: - --auth=password - --disable-telemetry - --disable-update-check - --disable-workspace-trust volumes: - ./workspace:/home/coder/project ports: ["127.0.0.1:8443:8080"] dns: [10.10.10.254] # DNS контейнера тоже через AdGuard extra_hosts: # маркетплейс расширений в никуда - "open-vsx.org:0.0.0.0" - "marketplace.visualstudio.com:0.0.0.0"
Зачем dns и extra_hosts. Расширения и терминал code‑server работают на сервере, а не на машине кандидата. Если кандидат поставит AI‑расширение или сделает curl к API из терминала, эти запросы пойдут с сервера и в журнал клиента 10.10.10.x не попадут. Поэтому DNS контейнера тоже заводим в AdGuard (в Query Log он будет виден как клиент 172.x.x.x), а маркетплейс расширений отрезаем. ufw должен пускать к AdGuard и Docker‑сети:
ufw allow from 172.16.0.0/12 to any port 53 proto udpufw allow from 172.16.0.0/12 to any port 53 proto tcp
Если задаче не нужен интернет (pip install и тому подобное), новые исходящие соединения из контейнеров можно закрыть совсем:
iptables -I DOCKER-USER -s 172.16.0.0/12 -m conntrack --ctstate NEW -j DROP
nginx
Общие заголовки прокси вынесем в сниппет /etc/nginx/snippets/lab-proxy.conf. Строки с Upgradeобязательны: code‑server живет на WebSocket.
proxy_http_version 1.1;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_read_timeout 3600s;
И сам конфиг:
# /etc/nginx/sites-available/labserver { listen 10.10.10.254:80; server_name code.lab.example.com; client_max_body_size 50m; location / { proxy_pass http://127.0.0.1:8443; include snippets/lab-proxy.conf; }}# Все, что пришло на публичный IP, 403server { listen 203.0.113.10:80 default_server; server_name _; return 403;}
Кандидат открывает http://code.lab.example.com/?folder=/home/coder/project, а пароль получает в начале интервью.
Чистый стенд перед каждым интервью
Заготовка задачи лежит в /opt/lab/template. Перед встречей скрипт восстанавливает рабочую папку и выдает новый пароль, чтобы следующий кандидат не увидел чужое решение:
#!/bin/sh# /opt/lab/reset.shset -ecd /opt/labPASS=$(openssl rand -base64 12)echo "CODE_SERVER_PASSWORD=$PASS" > .envdocker compose stop code-serverrm -rf workspace && cp -a template workspace && chown -R 1000:1000 workspacedocker compose up -d code-serverecho "Пароль для кандидата: $PASS"
Часть функций VS Code (буфер обмена, webview, preview Markdown) требует secure context, и code‑server предупреждает об этом. Домен резолвится в приватный адрес, поэтому HTTP-01 не сработает, выпускайте wildcard *.lab.example.com через DNS-01.
Как проводить интервью
Инфраструктура это половина дела. Вторая половина — процесс для HR и интервьюера.
До встречи HR заранее предупреждает кандидата: задания живут во внутренней среде, на время интервью нужно подключение к корпоративному VPN, а действия кандидата в это время могут логироваться.
Во время встречи HR или интервьюер держит открытым Query Log с фильтром по клиенту 10.10.10.x и обращает внимание на домены:
-
AI‑чатов и интервью‑копилотов, в том числе их API и сервисов распознавания речи;
-
remote desktop и screen sharing, кроме самого созвона;
-
мессенджеров и pastebin‑подобных сервисов;
-
поисковиков, если всплески совпадают с моментами, когда звучит новый вопрос.
Блокировка на лету
Если подозрение серьезное, домен можно заблокировать прямо из Query Log (⋮ → Block), через Filters → Blocked services (там есть готовые переключатели для популярных AI‑сервисов и мессенджеров)
или списком в Custom filtering rules:
||chatgpt.com^||openai.com^||claude.ai^||gemini.google.com^||perplexity.ai^||*ghost*gpt*^||telegram.org^||t.me^
Любая блокировка сработает не мгновенно, а когда истечет DNS‑кэш в ОС и браузере кандидата, а это может занять несколько минут.
Если после блокировки «беглость» кандидата заметно пропадает, это наблюдение, а не доказательство. Ковровые блокировки целых категорий «на всякий случай» делать не стоит. Честный кандидат может просто подсмотреть синтаксис в документации.
Нюансы использования
Предложенное решение это не детектор волков, а еще один сигнал для интервьюера. И его тоже можно обойти.
|
Способ обхода |
Видно ли в журнале |
Что можно сделать |
|
Помощник на втором устройстве (телефон, ноутбук) |
Нет |
Ничего сетевого; только формат вопросов |
|
DNS‑over‑HTTPS в браузере или приложении |
Виден только запрос к DoH‑провайдеру |
Замечать сам факт; при желании блокировать известные DoH‑домены и |
|
iCloud Private Relay, сторонний VPN поверх нашего |
Видны домены самого сервиса |
Это тоже сигнал |
|
Захардкоженные IP и собственный резолвер у помощника |
Нет |
Смотреть уже не DNS, а conntrack/NetFlow на сервере |
|
Разделение туннеля (split tunnel) на клиенте |
Частично |
Понять и простить |
Технические ограничения стенда:
-
Весь трафик кандидата идет через сервер, включая сам видеозвонок. Далекий или слабый сервер даст задержку и будет шакалить звук.
-
Трафик из code‑server виден хуже. Запросы из терминала и расширений code‑server идут с сервера и в Query Log видны одним общим клиентом Docker‑сети. Надежнее всего закрыть контейнеру исходящий интернет целиком.
Этика и закон
Речь идет о сборе данных о сетевой активности человека, так что тут нет простора для действий втихую.
-
Кандидат заранее и прямо уведомляется о том, что его активность может логироваться, и дает согласие. Альтернатива для несогласных это очная встреча или другой формат.
-
Просите установить и включить VPN только на время интервью: все, что до и после, вас не касается.
-
Храните журнал минимальное время, доступ к админке только у тех, кто проводит интервью.
-
Не принимайте решение по одному DNS‑запросу. Системные службы и фоновые приложения сами ходят куда угодно, в том числе к AI‑доменам.
-
Перед реальным применением проверьте с юристом требования к обработке персональных данных в вашей юрисдикции (152-ФЗ, GDPR и.т.п.).
Итоги
Один стенд, четыре open‑source компонента и вечер настройки дают интервьюеру то, чего не дает демонстрация экрана: журнал сетевых обращений кандидата в реальном времени.
Это не панацея, но еще один инструмент, с котором есть возможность повысить качество принимаемых решений по найму. Всегда нужно помнить, что второй телефон на столе может свести все на нет.
А как вы боретесь с AI‑помощниками?
ссылка на оригинал статьи https://habr.com/ru/articles/1100212/