Короткий ответ

Подрядчик — отдельный identity: свой ключ WireGuard / свой сертификат OpenVPN / своя AD-учётка без админских групп, свой [Peer] с AllowedIPs = 10.8.0.20/32 на сервере. На клиенте подрядчика в туннель кладут только то, что нужно: часто /32 бастиона 10.0.10.10, не всю 10.0.10.0/24. Срок в календарь + отзыв как у уволенного. Не давайте общий office.conf из чата сотрудников и не открывайте 3389 в WAN «на время проекта».

Фиксируйте в заявке: ФИО, компания, цель (RDP к pc01), префиксы, дата окончания, кто утвердил.

Симптомы и как отличить

Плохой доступ уже выдан, если:

  • в wg show один peer на «ООО Интегратор» с AllowedIPs 10.0.0.0/8;
  • подрядчик логинится учётки ivanov сотрудника;
  • нет MFA, нет журнала.

Нужна другая статья, если сотрудник штатный — шаблон сотрудника со split на LAN. Если увольнение — отзыв.

Возможные причины

  1. Shared key = нельзя отозвать одного человека.
  2. Широкий маршрут = сканирование DC, шары, принтеры.
  3. Домен-админ «чтобы побыстрее поставили 1С».
  4. RDP с интернета параллельно VPN.
  5. Нет логов, кто коннектился в инцидент.

Диагностика

wg show
wg show wg0 allowed-ips

OpenVPN CCD: уникален ли client-config-dir / сертификат CN. RRAS: уникальная учётка в NPS.

Get-VpnConnection
Get-ADUser -Identity 'vendor.petrov' -Properties MemberOf, AccountExpirationDate, Enabled

Составьте: какие 10.0.10.0/24 хосты ему реально нужны (лучше один jump).

Решение

1. Identity

  • AD: vendor.<фамилия>, OU подрядчиков, AccountExpirationDate, группа VPN-Vendors не Domain Admins.
  • OpenVPN: отдельный client cert, CN=vendor.petrov, CRL готов.
  • WireGuard: wg genkey только для него, public на сервере.

2. Адресация туннеля

Выдайте свободный 10.8.0.20/32. На сервере WG:

[Peer]
PublicKey = <vendor>
AllowedIPs = 10.8.0.20/32

Не давайте ему 10.8.0.0/24 на сервере (это cryptokey routing чужих пиров).

3. Маршруты клиента подрядчика

AllowedIPs = 10.8.0.1/32, 10.0.10.10/32

Только шлюз (если нужен) и бастион. Не 10.0.10.0/24.

Windows RAS:

Add-VpnConnectionRoute -ConnectionName 'Vendor' -DestinationPrefix '10.0.10.10/32'

4. Firewall на шлюзе

FORWARD: 10.8.0.2010.0.10.10 порты 3389 (или 22), drop остальное. Даже если ошибутся AllowedIPs, ACL спасёт.

iptables -A FORWARD -s 10.8.0.20/32 -d 10.0.10.10 -p tcp --dport 3389 -j ACCEPT
iptables -A FORWARD -s 10.8.0.20/32 -d 10.0.10.0/24 -j DROP

Не ufw disable.

5. На бастионе

Локальная или dedicated учётка, не domain admin. Журнал RDP. NLA. См. RDP-статьи.

6. MFA и срок

Где возможно — MFA на VPN (MFA). Календарь отзыва. После проекта — чеклист отзыва.

7. Журнал

Включите учёт подключений (журналирование).

Стенд: три проверки ACL

Подрядческий пир 10.8.0.20. С его конфига:

ping -c 1 10.0.10.10
ping -c 1 10.0.10.11
nc -vz 10.0.10.10 3389
nc -vz 10.0.10.11 3389
nc -vz 10.0.10.10 445

Ожидание: жив только согласованный хост и порт. Если 445 открылся «сам» — дыра FORWARD или слишком широкий AllowedIPs на клиенте (он поставил 10.0.10.0/24). Серверный DROP на 10.8.0.20 → 10.0.10.0/24 кроме 3389 к бастиону должен спасать даже кривой клиентский конфиг.

Второй человек подрядчика с тем же файлом .conf: wg show на сервере увидит прыгающий endpoint. Заведите второй ключ, первый не шарьте. CMDB: pubkey, ФИО, компания, 10.8.0.20, дата конца.

AD:

Get-ADUser vendor.petrov -Properties AccountExpirationDate, MemberOf, Enabled

Expiration в прошлом при живом WG-пире = дыра. Снимайте оба.

Не выдавайте домен-админа «на день установки». Локальный админ на бастионе + журналы RDP. После проекта — чеклист отзыва, не «оставим, вдруг доделка».

Отдельный endpoint не обязателен, но отдельный [Peer] — да. Не кладите подрядчика в тот же AllowedIPs-групповой список, что сотрудники. На nftables заведите набор vendor_peers с 10.8.0.20, правила читаемее, чем десяток однострочников iptables. Журналируйте wg show dump без private key в SIEM, чтобы видеть handshake после даты окончания договора.

wg show wg0 dump | awk '$4=="10.8.0.20/32" {print}'

Если подрядчику нужен Git по SSH на 10.0.10.40, это новый /32 и порт 22 в ACL, новая строка в заявке, не «ну раз VPN уже есть». Каждый ресурс — изменение политики. Windows-клиент подрядчика: Get-VpnConnection не заменяет WG; выдайте инструкцию того стека, который реально стоит, иначе они включат RAS L2TP и откроют 1701 «сами».

Срок в календаре без remove peer бесполезен так же, как disable AD без WG. Поставьте напоминание за 3 дня и в день X. После X wg show не должен содержать его pubkey. Договор: запрет пересылки .conf в личный мессенджер; при нарушении — ротация ключа как инцидент, не «перевыпустим когда-нибудь».

Как проверить, что проблема устранена

С подрядческого пира:

  • ping 10.0.10.10 ок, 10.0.10.11 fail;
  • RDP только на согласованный хост;
  • ip route get 10.0.10.11 не в туннель (или ACL drop);
  • второй ноутбук с тем же ключом не должен существовать.

С сервера: wg show handshake только с ожидаемого endpoint. AD expiration в будущем, MemberOf минимален.

ping -c 1 10.0.10.10
ping -c 1 10.0.10.11

Если не помогло

  • Нужен «весь VLAN на неделю установки» — временный широкий маршрут с датой в тикете и напоминанием, не бессрочно.
  • Их корпоративный VPN конфликтует overlapping — NAT/перенумерация.
  • Требуют домен-админа — эскалируйте, не соглашайтесь в чате.

Профилактика

  • Шаблон vendor-peer.conf.j2 в ansible.
  • Ежеквартальный review пиров без handshake 30+ дней.
  • Запрет шаринга конфигов в договоре.
  • 3389 с WAN закрыт.

FAQ

Можно ли одну учётку на компанию подрядчика?

Юридически иногда да, технически плохо: нет персональной ответственности. Лучше персона + их MFA.

Подрядчик на Mac, у нас только RAS L2TP

Выдайте WG или OpenVPN 2.6, не открывайте L2TP/1701 в WAN. L2TP legacy.

Нужен SMB на файловую шару

/32 файлового сервера + share permissions + отдельная учётка, не «вся LAN».

Они просят AnyDesk параллельно

Либо VPN, либо согласованный tool с журналом. Два дырявых канала — хуже.

Как дать доступ на день

Expiration AD + WG peer с календарём. Не «потом удалим».