Windows Server для самых маленьких. Часть 28: Настраиваем VPN для удалённого доступа
Все части серии «Windows Server для самых маленьких»Как устроен IPsec, что происходит в обмене IKEv2 и чем VPN отличается от прокси, разобрано в частях 19 и 20 сетевой серии. Там туннель соединял две сети. Здесь туннель соединяет сотрудника на домашнем диване с офисом, и у этого случая свои вопросы: как шлюз узнаёт, что подключается именно сотрудник, а не тот, кто подобрал его ноутбук, какой адрес выдать, куда пустить, и как всё это связано с каталогом, NPS из части 26 и сертификатами из части 25.
Роль Windows в этой схеме — проверять пользователей. Сам туннель обычно терминирует не Windows, а то, что и так стоит на границе сети: файрвол, роутер или Linux-машина со strongSwan. У Windows Server есть собственная роль для VPN, Routing and Remote Access, но в этой части её нет. Шлюз здесь — strongSwan: он бесплатный, стоит на любом Linux, и его конфигурация показывает механику без лишних слоёв. Как его ставить и настраивать в современном формате swanctl, я писал раньше: переход от ipsec.conf к swanctl и swanctl с EAP-MSCHAPv2. Там пользователи и пароли лежали в самом strongSwan; сегодня их проверяет домен. Ещё раньше был пост IKEv2 VPN на Ubuntu 20.04 — по нему настраивать шлюз не надо: strongSwan там описан в устаревшем формате ipsec.conf, а для клиентов Windows предлагается скачать вручную собранный плагин. Как настроить шлюз правильно, разобрано ниже, в этой части.
Схема
ноутбук дома интернет граница сети внутри
┌───────────┐ ┌────────────────┐ ┌──────────┐
│ клиент │ ── IKEv2: UDP 500, 4500 ────► │ vpn-gw │ ── RADIUS ──► │ nps1 │ ── AD ──► dc1, dc2
│ Windows │ │ strongSwan │ UDP 1812 │ NPS │
└───────────┘ └────────────────┘ └──────────┘
адрес 192.168.50.x из пула vpn-gw, 192.0.2.10 снаружи проверяет пароль,
DNS 192.168.1.10 и .11 192.168.1.1 внутри решает по политике
Имя vpn.example.com — в публичной зоне, указывает на внешний адрес vpn-gw 192.0.2.10. Шлюз принимает IKEv2 на UDP 500 и 4500 (4500 — IKE и ESP, завёрнутые в UDP для прохода через NAT, NAT-T из части 19), проверяет пользователя через nps1 по RADIUS, выдаёт клиенту адрес из пула 192.168.50.0/24 и адреса DNS-серверов — контроллеров домена. Внутренняя сеть должна знать, что 192.168.50.0/24 достижима через vpn-gw; если vpn-gw и есть роутер внутренней сети, как здесь, знать ничего не нужно.
Три роли, три протокола между ними:
| Между | Протокол | Что внутри |
|---|---|---|
клиент — vpn-gw |
IKEv2 поверх UDP | рукопожатие IPsec, внутри него — EAP |
vpn-gw — nps1 |
RADIUS, UDP 1812 | те же сообщения EAP, переложенные в атрибуты RADIUS |
nps1 — контроллеры |
LDAP и Netlogon | кто такой alice, в какой она группе, верен ли ответ на вызов MS-CHAPv2 |
LDAP NPS читает учётную запись и группы, а ответ MS-CHAPv2 проверяет не сам: передаёт его контроллеру по Netlogon — защищённому каналу RPC между членом домена и контроллером, по которому проверяется и NTLM (часть 3).
Шлюз не знает ни одного пароля и не умеет их проверять: он только передаёт EAP между клиентом и NPS и получает ответ «пускать» или «не пускать». Точно так же, как точка доступа в части 27, — с той разницей, что EAP едет не в кадрах EAPOL, а внутри сообщений IKE_AUTH.
Как это работает
В части 19 IKE_AUTH был одним обменом: обе стороны предъявили AUTH, и SA готова. С EAP обмен растягивается:
клиент (alice) vpn-gw (strongSwan) nps1 (NPS)
│ │ │
├─ IKE_SA_INIT ─────────────────────►│ │
│◄─ IKE_SA_INIT ─────────────────────┤ │
├─ IKE_AUTH: IDi, CP, SA, TS ───────►│ нет AUTH = клиент хочет EAP │
│◄─ IKE_AUTH: IDr, CERT, AUTH, │ │
│ EAP-Request/Identity ────────────┤ │
├─ EAP-Response/Identity: alice ────►│ │
│ ├─ Access-Request (EAP-Message) ─►│ alice есть в AD?
│ │◄─ Access-Challenge (EAP) ───────┤ MS-CHAPv2 Challenge
│◄─ EAP-Request: MS-CHAPv2 ──────────┤ │
├─ EAP-Response: MS-CHAPv2 ─────────►├─ Access-Request (EAP-Message) ─►│ проверить ответ
│ │◄─ Access-Challenge (EAP) ───────┤ MS-CHAPv2 Success
│◄─ EAP-Request: MS-CHAPv2 ──────────┤ │
├─ EAP-Response: MS-CHAPv2 ─────────►├─ Access-Request (EAP-Message) ─►│
│ │◄─ Access-Accept + MS-MPPE keys ─┤ политика VPN, группа
│◄─ EAP-Success ─────────────────────┤ │
├─ IKE_AUTH: AUTH (из ключей EAP) ──►│ ключи совпали = пароль верный │
│◄─ IKE_AUTH: AUTH, CP (адрес, DNS), │ │
│ SA, TS ──────────────────────────┤ │
│══ ESP ════════════════════════════►│ │
По шагам.
Клиент не предъявляет AUTH. В первом IKE_AUTH он отправляет свой идентификатор, но не доказательство: по RFC 7296 это и означает «хочу аутентифицироваться по EAP». Шлюз отвечает своим сертификатом и AUTH — то есть сначала аутентифицируется сам, и только потом спрашивает пользователя. Клиент проверяет сертификат: цепочка до доверенного корня, имя vpn.example.com в сертификате совпадает с тем, к чему подключались. Это единственная защита от поддельного шлюза, и ниже видно, почему она важна.
Сертификат шлюза едет в ответе, только если клиент о нём попросил: в первом IKE_AUTH клиент кладёт полезную нагрузку CERTREQ со списком центров, которым доверяет, а strongSwan по умолчанию (send_cert = ifasked) отправляет сертификаты только в ответ на неё. Windows перечисляет в CERTREQ все свои доверенные корни, поэтому сообщение получается большим. Большим получается и ответ шлюза: сертификат шлюза и промежуточного центра с ключами RSA 4096 бит — около 3,5 КБ, больше MTU. Такие сообщения IKEv2 режет на фрагменты сам (фрагментация IKE, RFC 7383: каждый фрагмент зашифрован и собирается получателем до разбора); Windows поддерживает её с версии 1803, strongSwan — по умолчанию. Без неё большие UDP-датаграммы разбиваются на IP-фрагменты, а их многие домашние роутеры и провайдеры отбрасывают.
EAP внутри IKE. Дальше внутри зашифрованного IKE_AUTH идут сообщения EAP — того же протокола, что в 802.1X: запрос идентификатора, ответ CORP\alice (имя в том виде, в каком его ввёл пользователь), затем метод. Шлюз каждое сообщение от клиента кладёт в атрибут EAP-Message пакета RADIUS Access-Request и отправляет NPS, а то, что NPS вернул в Access-Challenge, разворачивает обратно в EAP и отдаёт клиенту. Шлюз содержимого не понимает и понимать не должен.
Метод — EAP-MSCHAPv2. NPS предлагает клиенту MS-CHAPv2: присылает случайный вызов, клиент считает ответ из вызова и NT-хеша своего пароля, NPS передаёт вызов и ответ контроллеру домена по Netlogon и, получив подтверждение, присылает свой ответ, по которому клиент убеждается, что пароль знает и сервер; последний обмен — подтверждение успеха. На подключение получается три пары запрос — ответ RADIUS: два Access-Challenge и в конце Access-Accept. В Wi-Fi тот же метод заворачивали в PEAP — TLS-туннель с сертификатом NPS, — потому что кадры EAPOL в эфире никто не защищает. Здесь TLS не нужен: обмен уже идёт внутри IKE SA, зашифрованной ключами, о которых договорились в IKE_SA_INIT, а сторона, с которой договорились, подтвердила себя сертификатом vpn-gw. Поэтому для VPN в NPS включают EAP-MSCHAPv2 без PEAP; в списке методов NPS он называется «Microsoft: Secured password (EAP-MSCHAP v2)», и именно его Windows-клиент предлагает по умолчанию. Это не тот MS-CHAPv2 без EAP из таблицы части 26: тот передавался в PPP (Point-to-Point Protocol — протоколе модемных и PPTP-подключений) без защиты обмена, а здесь каждый байт закрыт IPsec.
Отсюда и важность проверки сертификата шлюза клиентом: если клиент согласится на любой сертификат, любой, кто перехватит соединение, получит от него ответ MS-CHAPv2 на свой вызов, а этот ответ можно подбирать офлайн. Windows по умолчанию сертификат проверяет и без доверенного корня не подключится.
Ключи EAP становятся AUTH. В Access-Accept NPS возвращает не только разрешение, но и ключевой материал — атрибуты MS-MPPE-Send-Key и MS-MPPE-Recv-Key (RFC 2548), зашифрованные общим секретом RADIUS. Из них strongSwan собирает MSK (master session key), тот же ключ выводит клиент из своего пароля и обмена MS-CHAPv2, и обе стороны используют его для финальных AUTH. Если пароль был неверный, ключи не совпадут и AUTH не сойдётся; поддельный RADIUS-сервер не сможет проверить ответ через контроллер и выдать правильные ключи. Так IKE связывает «пользователь прошёл EAP» с «этот туннель принадлежит этому пользователю».
Configuration Payload. Уже в первом IKE_AUTH клиент просит адрес и параметры (CP(CFG_REQUEST): Windows просит INTERNAL_IP4_ADDRESS, INTERNAL_IP4_DNS, INTERNAL_IP4_NBNS — сервер имён NetBIOS, — те же для IPv6 и пару служебных атрибутов), а в последнем ответе шлюз выдаёт CP(CFG_REPLY) с адресом из пула и DNS-серверами — то, что для них настроено. Селекторы трафика определяют, что пойдёт в туннель: 0.0.0.0/0 — всё (полный туннель), конкретные сети — раздельный.
Кроме пароля EAP умеет и сертификаты: метод EAP-TLS (RFC 5216, взаимная аутентификация TLS) с сертификатом пользователя из части 25 проходит через ту же цепочку RADIUS, в NPS это «Smart Card or other certificate», для strongSwan разницы нет. Ниже — вариант с паролем как самый частый; что поменять для EAP-TLS, сказано по ходу.
NPS
Шлюз регистрируется клиентом RADIUS, как в части 26 (vpn-gw, 192.168.1.1, New-NpsRadiusClient). Адрес клиента — тот, с которого приходят запросы, то есть внутренний адрес шлюза: в самих запросах strongSwan пишет NAS-IP-Address — внешний адрес, на котором принимает IKE, и NPS опознаёт клиента не по нему, а по адресу отправителя UDP.
Дальше две политики. Командлетов для политик NPS в PowerShell нет, их создают оснасткой или netsh nps; оснастка предлагает мастер «Configure VPN or Dial-Up», который создаёт обе, и вот как они выглядят на сервере, где мастер уже запускали (фрагмент вывода):
PS C:\> netsh nps show crp
Connection request policy configuration:
---------------------------------------------------------
Name = Virtual Private Network (VPN) Connections
State = Enabled
Processing order = 2
Policy source = 0
Condition attributes:
Name Id Value
---------------------------------------------------------
Condition0 0x3d "^5$"
Profile attributes:
Name Id Value
---------------------------------------------------------
Auth-Provider-Type 0x1025 "0x1"
Override-RAP-Auth 0x1fb0 "FALSE"
Политика запросов на подключение решает, где проверять запросы с NAS-Port-Type = Virtual. 0x3d — атрибут NAS-Port-Type, ^5$ — его значение 5, Virtual (strongSwan ставит его в каждый запрос; точки доступа ставили 19, Wireless-802.11). 0x1025 — Auth-Provider-Type, 1 — проверять здесь, а не пересылать другому серверу RADIUS. Политика по умолчанию «Use Windows authentication for all users» делает то же для всех запросов, так что отдельная нужна, только если остальные запросы пересылаются прокси. Командой — одним add crp, условие в нём обязательно:
PS C:\> netsh nps add crp name="VPN" state="enable" processingorder="2" policysource="0" conditionid="0x3d" conditiondata="^5$" profileid="0x1025" profiledata="0x1"
Сетевая политика — кого пускать и каким методом. Мастер делает её для выбранной группы (на сервере выше — Domain Users), но правильнее завести отдельную группу VPN Users и создать политику одной командой netsh nps add np со всеми условиями и параметрами — как в части 26:
PS C:\> $sid = (Get-ADGroup 'VPN Users').SID.Value
PS C:\> netsh nps add np name="VPN Users" state="enable" processingorder="2" policysource="0" conditionid="0x3d" conditiondata="^5$" conditionid="0x1023" conditiondata="$sid" profileid="0x100f" profiledata="TRUE" profileid="0x1005" profiledata="TRUE" profileid="0x1009" profiledata="0x5" profileid="0x100a" profiledata="1A000000000000000000000000000000" profileid="0x7" profiledata="0x1" profileid="0x6" profiledata="0x2"
Что в ней:
| Идентификатор | Что | Значение |
|---|---|---|
0x3d условие |
NAS-Port-Type | ^5$ — Virtual |
0x1023 условие |
Windows-Groups | SID группы CORP\VPN Users — именно SID, не имя |
0x100f |
NP-Allow-Dial-in | разрешить (Grant) |
0x1005 |
Ignore-User-Dialin-Properties | не смотреть флаг «Network Access Permission» в свойствах учётной записи: решает политика |
0x1009 |
NP-Authentication-Type | 0x5 — EAP, и только EAP: ни MS-CHAPv2 без EAP, ни PAP |
0x100a |
NP-Allowed-EAP-Type | первый байт — тип EAP: 1A = 26 — EAP-MSCHAPv2; для EAP-TLS добавить второе значение 0D000000000000000000000000000000 (13) |
0x7, 0x6 |
Framed-Protocol = PPP, Service-Type = Framed | атрибуты ответа, которые ставит и мастер; strongSwan их не читает |
Мастер добавляет ещё атрибуты Microsoft для RRAS — MS-Link-Utilization-Threshold, MS-Link-Drop-Time-Limit, MS-MPPE-Encryption-Policy, MS-MPPE-Encryption-Types; strongSwan их тоже игнорирует. Проверить результат — netsh nps show np (параметра name у него нет, он выводит все политики).
Порядок обработки важен: политика VPN должна стоять раньше политик без условия по типу порта, иначе запрос подхватит первая подходящая. Учётные данные те же, что везде: пароль домена; учётные записи в Protected Users через MS-CHAPv2 не пройдут, потому что для них запрещён NTLM, на котором MS-CHAPv2 построен, — как в части 26. Что ещё умеет NPS в ответе: Framed-IP-Address даст пользователю фиксированный адрес (strongSwan его применит, если в pools соединения указан пул radius), Session-Timeout ограничит сессию, а Class или Filter-Id с именем группы strongSwan может считать членством в группе (параметры class_group и filter_id плагина) и по нему выбирать конфигурацию с groups.
Шлюз: strongSwan
vpn-gw — Ubuntu с двумя интерфейсами: eth0 наружу с адресом 192.0.2.10 и eth1 внутрь с 192.168.1.1. Пакеты:
apt install charon-systemd strongswan-swanctl libcharon-extra-plugins
charon-systemd — сам демон IKE и служба strongswan.service, которая после запуска выполняет swanctl --load-all; libcharon-extra-plugins — там плагин eap-radius, без него strongSwan умеет только локальные EAP-методы. Проверить, что плагин загружен, можно после запуска: swanctl --stats в строке loaded plugins перечисляет загруженные плагины, eap-radius должен быть в списке.
Сертификат шлюза
Клиенты проверяют сертификат по тем же правилам, что браузер: цепочка до доверенного корня, имя. Два пути:
- Свой удостоверяющий центр, например из части 25. Шаблон — дубликат «Web Server» или «RAS and IAS Server» с именем из запроса, EKU Server Authentication; корневой сертификат есть на всех машинах домена, на личные устройства его нужно поставить руками. EKU IP security IKE intermediate (
1.3.6.1.5.5.8.2.2) Windows не нужен, но и не мешает, а старым версиям macOS он требуется. - Публичный центр (Let's Encrypt через ACME — протокол, которым шлюз сам доказывает центру владение именем и получает сертификат, RFC 8555): корень уже есть везде, на личных устройствах ничего ставить не нужно, но сертификат живёт 90 дней и после продления strongSwan нужно перечитать его (
swanctl --load-creds).
В обоих случаях имя vpn.example.com должно быть в сертификате — в Subject или Subject Alternative Name, надёжнее в SAN как dNSName; иначе Windows отвечает ошибкой 13801. Файлы раскладываются по каталогам swanctl:
/etc/swanctl/x509/vpn.example.com.pem сертификат шлюза
/etc/swanctl/private/vpn.example.com.pem его закрытый ключ, права 600
/etc/swanctl/x509ca/issuing-ca.pem промежуточный центр, чтобы отдавать клиенту цепочку
Конфигурация
/etc/swanctl/conf.d/vpn.conf:
connections {
ikev2-eap-radius {
version = 2
proposals = aes256-sha256-modp2048,aes256-sha256-modp1024
mobike = yes
unique = never
rekey_time = 0
dpd_delay = 30
pools = pool-v4, pool-v6
local {
auth = pubkey
certs = vpn.example.com.pem
id = vpn.example.com
}
remote {
auth = eap-radius
eap_id = %any
}
children {
ikev2-eap-radius-child {
local_ts = 0.0.0.0/0, ::/0
esp_proposals = aes256-sha256-modp2048,aes256-sha256,aes256-sha1
rekey_time = 0
dpd_action = clear
}
}
}
}
pools {
pool-v4 {
addrs = 192.168.50.0/24
dns = 192.168.1.10, 192.168.1.11
}
pool-v6 {
addrs = 2001:db8:50::/64
dns = 2001:db8:1::10, 2001:db8:1::11
}
}
Что здесь важно:
local { auth = pubkey }— шлюз аутентифицируется сертификатом;idдолжен совпадать с именем в сертификате и с тем, что клиент вводит как адрес сервера.remote { auth = eap-radius; eap_id = %any }— клиентов проверять через RADIUS, любые идентификаторы принимать (решает NPS). Идентификатор IKE у Windows-клиента — его собственный IP-адрес (за домашним роутером — внутренний, вроде10.0.0.15), а имя пользователя приходит уже в EAP;eap_id = %anyразводит эти два имени.proposals— AES-256 и SHA-256 с группами Диффи — Хеллмана 2048 и 1024 бит. Windows по умолчанию предлагает для IKEv2 толькоmodp1024, и без второго варианта в списке клиент не подключится, пока на нём не включилиmodp2048(ниже, в разделе о клиенте, и отдельной заметкой). Когда все клиенты настроены,modp1024из списка убирают.esp_proposals— алгоритмы самого туннеля. Windows без своей настройки предлагает для ESP целостность только SHA-1, поэтому безaes256-sha1такой клиент поднимет IKE SA, но не туннель.aes256-sha256-modp2048— для клиентов, которым включили PFS (-PfsGroupниже): при перевыпуске ключей туннеля они присылают обмен Диффи — Хеллмана, и шлюз должен его принять; для первого туннеля, который создаётся в IKE_AUTH, группа не используется.mobike = yes— переезд клиента между сетями без переподключения (MOBIKE, RFC 4555): ноутбук уснул на Wi-Fi и проснулся на телефоне в режиме модема — туннель жив. Это и умолчание.unique = never— один пользователь может подключиться с нескольких устройств одновременно; при умолчанииnoновое подключение с уведомлением INITIAL_CONTACT вытеснило бы старое.rekey_time = 0на обоих уровнях — не перевыпускать ключи по времени со стороны шлюза: Windows-клиент делает это сам по своему расписанию, а перевыпуск, начатый шлюзом, Windows принимает плохо: например, если шлюз предлагает для туннеля обмен Диффи — Хеллмана, а на клиенте PFS не настроен, клиент отвечает ошибкой 13816.dpd_delay = 30— Dead Peer Detection: если 30 секунд от клиента не было ни одного пакета, шлюз шлёт пустое сообщение INFORMATIONAL; не ответил после всех повторных передач (по умолчанию пять, около 165 секунд) — клиент считается ушедшим. Параметрdpd_timeoutдля IKEv2 не действует.dpd_action = clear— закрыть туннель и ничего больше не делать; это умолчание, иswanctl --list-connsпоказывает его какdpd action is none.local_ts = 0.0.0.0/0, ::/0— полный туннель: шлюз готов принять весь трафик клиента. Какой трафик клиент на самом деле отправит, решает клиент своими маршрутами (ниже); чтобы шлюз пропускал только офисные сети, здесь пишут их список, например192.168.0.0/16.pools— адреса и атрибуты для Configuration Payload;dns— контроллеры домена, чтобы внутренние имена разрешались. Адрес за пользователем закрепляется: strongSwan помнит, кому что выдавал, и при переподключении отдаёт тот же. Пул IPv6 размером/64strongSwan урезает до/97(2³¹ адресов) и пишет об этом при загрузке:virtual IP pool too large, limiting to 2001:db8:50::/97. Туда же при желанииsplit_include = 192.168.0.0/16— подсказка клиентам, которые понимают атрибут раздельного туннеля (Apple, strongSwan); Windows её игнорирует.
Плагин RADIUS настраивается отдельно, /etc/strongswan.d/charon/eap-radius.conf:
eap-radius {
load = yes
accounting = yes
servers {
nps1 {
address = 192.168.1.20
secret = <общий секрет, тот же, что у клиента RADIUS vpn-gw в NPS>
auth_port = 1812
acct_port = 1813
}
}
}
accounting = yes (по умолчанию выключено) — отправлять NPS Accounting-Request при старте и завершении сессии: тогда в журнале NPS видно не только входы, но и длительность и объёмы. Серверов можно перечислить несколько, strongSwan переключится на следующий, если первый не отвечает; второй NPS — тема, которую стоит закрыть сразу после первого, потому что без RADIUS VPN не работает вовсе. Файл с секретом — права 600. Остальное, что strongSwan кладёт в каждый запрос, — NAS-Port-Type = 5 (Virtual), Service-Type = 2 (Framed), NAS-Port — номер IKE SA, NAS-Port-Id — имя соединения, Called-Station-Id и Calling-Station-Id — адреса шлюза и клиента с портами, NAS-Identifier = strongSwan.
Применить:
systemctl restart strongswan # eap-radius.conf читается при старте
swanctl --list-conns
Файрвол и маршрутизация
Шлюз должен принимать IKE и ESP, пересылать расшифрованный трафик клиентов и, для полного туннеля, выпускать их в интернет под своим адресом:
sysctl -w net.ipv4.ip_forward=1
iptables -A INPUT -i eth0 -p udp -m multiport --dports 500,4500 -j ACCEPT
iptables -A INPUT -i eth0 -p esp -j ACCEPT
iptables -A FORWARD -s 192.168.50.0/24 -m policy --dir in --pol ipsec --proto esp -j ACCEPT
iptables -A FORWARD -d 192.168.50.0/24 -m policy --dir out --pol ipsec --proto esp -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -m policy --dir out --pol ipsec -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o eth0 -j MASQUERADE
iptables -t mangle -A FORWARD -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
-p esp — ESP без UDP, для клиентов, которые не за NAT (с NAT-T всё идёт на 4500). -m policy --pol ipsec — правило срабатывает только для пакетов, которые пришли из туннеля или уйдут в него; так трафик клиентов VPN отличают от трафика из интернета с теми же адресами. Правило ACCEPT в nat перед MASQUERADE не даёт подменять адрес пакетам, которые уходят в другие туннели шлюза (если они есть). Последняя строка — MSS clamping: ESP и UDP съедают часть MTU, и без уменьшения MSS большие TCP-сегменты через туннель не проходят (часть 4 сетевой серии — сам приём, часть 19 — во что обходится туннель); --mss 1361:1536 переписывает только значения больше 1360. Если внутренний роутер — не vpn-gw, ему нужен маршрут 192.168.50.0/24 → 192.168.1.1.
Для EAP-TLS вместо пароля на шлюзе не меняется ничего: EAP любого типа проходит через eap-radius прозрачно; меняются политика NPS (тип 13 в разрешённых) и клиент (сертификат пользователя вместо пароля).
Клиент
Подключение на Windows создаётся командой, и в ней всё, что было настроено на шлюзе:
PS C:\> Add-VpnConnection -Name 'Corp VPN' -ServerAddress vpn.example.com -TunnelType Ikev2 -AuthenticationMethod Eap -EncryptionLevel Required -RememberCredential -DnsSuffix corp.example.com
PS C:\> Set-VpnConnectionIPsecConfiguration -ConnectionName 'Corp VPN' -EncryptionMethod AES256 -IntegrityCheckMethod SHA256 -DHGroup Group14 -PfsGroup PFS2048 -CipherTransformConstants AES256 -AuthenticationTransformConstants SHA256128 -Force
Без -AllUserConnection подключение создаётся для текущего пользователя, с ним — для всех пользователей машины (нужны права администратора, и тот же ключ — в остальных командах ниже). -AuthenticationMethod Eap без XML-профиля — EAP-MSCHAPv2, «Secured password»: клиент спросит имя и пароль домена и предложит NPS ровно тот метод, который разрешён в политике. Так выглядит профиль, созданный этой командой:
PS C:\> Get-VpnConnection -Name 'Corp VPN' | Select-Object Name, TunnelType, AuthenticationMethod, EncryptionLevel, SplitTunneling, ConnectionStatus
Name TunnelType AuthenticationMethod EncryptionLevel SplitTunneling ConnectionStatus
---- ---------- -------------------- --------------- -------------- ----------------
Corp VPN Ikev2 {Eap} Required False Disconnected
PS C:\> $x = (Get-VpnConnection -Name 'Corp VPN').EapConfigXmlStream
PS C:\> $x.EapHostConfig.EapMethod.Type.'#text'
26
PS C:\> $x.EapHostConfig.Config.Eap.EapType.UseWinLogonCredentials
false
Тип EAP 26 и UseWinLogonCredentials = false: пароль спрашивается при подключении, а не берётся от входа в Windows. Для EAP-TLS передаётся другой XML-профиль EAP через -EapConfigXmlStream, снятый с настроенной вручную машины тем же свойством EapConfigXmlStream.
Вторая команда переводит клиента на modp2048 (Group14), AES-256 и SHA-256 и для IKE, и для ESP, и включает PFS — та же политика, что в proposals и esp_proposals шлюза. Без неё Windows предложит для IKE modp1024, а для ESP — SHA-1. Есть и способ для всех подключений машины сразу — параметр реестра NegotiateDH2048_AES256 в HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Parameters (1 — предлагать AES-256 и modp2048 вместе со старым, 2 — только их); он меняет только IKE, ESP остаётся на SHA-1:
PS C:\> Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Parameters -Name NegotiateDH2048_AES256
1
Доверие к сертификату шлюза. Если сертификат от своего центра, корень должен лежать в Cert:\LocalMachine\Root. У машин домена с центром предприятия он там с части 25; на личном ноутбуке — Import-Certificate -FilePath corp-root-ca.cer -CertStoreLocation Cert:\LocalMachine\Root из консоли администратора. Без этого — ошибка 13801 ниже.
Полный или раздельный туннель. По умолчанию Windows ставит маршрут по умолчанию через VPN: весь интернет клиента идёт через офис (для этого на vpn-gw и был NAT). Раздельный туннель — флаг -SplitTunneling и маршруты руками:
PS C:\> Set-VpnConnection -Name 'Corp VPN' -SplitTunneling $true
PS C:\> Add-VpnConnectionRoute -ConnectionName 'Corp VPN' -DestinationPrefix 192.168.0.0/16
Тогда через туннель идут только офисные сети, остальное — напрямую, со всеми оговорками про утечки из части 20. С IPv6 своя деталь: адрес из пула pool-v6 Windows получает, но маршрут IPv6 через VPN не ставит даже при полном туннеле. Маршрут по умолчанию ::/0 командлет добавить не даёт, зато хватает 2000::/3 — всех глобальных адресов IPv6: Add-VpnConnectionRoute -DestinationPrefix 2000::/3.
Подключиться из командной строки — rasdial 'Corp VPN' alice * (звёздочка — спросить пароль), отключиться — rasdial 'Corp VPN' /DISCONNECT; без аргументов rasdial перечисляет активные подключения:
PS C:\> rasdial
No connections
Command completed successfully.
Такой же профиль можно раздать машинам домена групповой политикой или через Intune (облачную службу управления устройствами Microsoft) как XML для CSP VPNv2 — узла конфигурации, через который MDM настраивает VPN; в нём же включают автоподключение (Always On VPN — не отдельный протокол, а способ доставки и автозапуска того же IKEv2-профиля). macOS и iOS подключаются встроенным клиентом IKEv2 с аутентификацией «Username» — это тот же EAP-MSCHAPv2; Android — приложением strongSwan с профилем «IKEv2 EAP (Username/Password)». Linux — тем же strongSwan в роли клиента или NetworkManager с плагином.
Что ломается
Клиент Windows сообщает об ошибках номерами; они же — в событиях источника RasClient в журнале Application:
| Код | Текст Windows | Что обычно значит |
|---|---|---|
| 809 | the remote server is not responding | шлюз не ответил на UDP 500/4500: файрвол, нет проброса портов |
| 13801 | IKE authentication credentials are unacceptable | сертификат шлюза не прошёл проверку: нет EKU Server Authentication, истёк, клиент не доверяет корню, имя не совпало с ServerAddress |
| 13868 | Policy match error | не совпали алгоритмы: у клиента modp1024, у шлюза только modp2048, или разные шифры |
| 691 | the user name and password combination … is not recognized, or the selected authentication protocol is not permitted | NPS отказал: пароль, группа, политика — смотреть 6273 на NPS |
| 812 | the authentication method used by the server … may not match the authentication method configured in your connection profile | политика NPS требует метод, которого нет в профиле клиента |
| 13806 | IKE failed to find valid machine certificate | у клиента нет нужного сертификата (EAP-TLS, аутентификация компьютера) |
Коды придуманы для шлюза RRAS, и за чужим шлюзом клиент видит причину не всегда так точно; точная причина отказа — в журнале NPS.
На стороне шлюза journalctl -u strongswan договаривает остальное. NPS не отвечает — retransmit 1 of RADIUS Access-Request и в конце RADIUS Access-Request timed out after … attempts: так выглядят и выключенный NPS, и неверный секрет, и шлюз, заведённый в NPS не с тем адресом, — NPS такие запросы молча отбрасывает и пишет в журнал System событие 13 (незнакомый клиент), 14 или 18 (не сходится Authenticator или Message-Authenticator, то есть секрет). Отказ NPS — RADIUS authentication of 'CORP\alice' failed и EAP method … failed for peer …. Клиент не принял аутентификацию шлюза — вместо следующего сообщения EAP приходит parsed INFORMATIONAL request 2 [ N(AUTH_FAILED) ], и полуоткрытая IKE SA удаляется по таймауту. На NPS — события 6273 с кодом причины: 16 — неверные учётные данные, 48 — ни одна сетевая политика не подошла (чаще всего тип порта или порядок политик), 66 — метод не разрешён, 65 — доступ запрещён разрешением в учётной записи или в совпавшей политике.
Доступность снаружи проверяется до всего остального: nmap -sU -p 500,4500 vpn.example.com покажет open или open|filtered, а клиент, получивший 13801 или 13868, уже доказал, что IKE отвечает.
Смотрим руками
Что strongSwan загрузил — соединение, пулы и сертификат:
# swanctl --list-conns
ikev2-eap-radius: IKEv2, no reauthentication, no rekeying, dpd delay 30s
local: %any
remote: %any
local public key authentication:
id: vpn.example.com
certs: CN=vpn.example.com
remote EAP_RADIUS authentication:
eap_id: %any
ikev2-eap-radius-child: TUNNEL, no rekeying, dpd action is none
local: 0.0.0.0/0 ::/0
remote: dynamic
# swanctl --list-pools
pool-v4 192.168.50.0 0 / 0 / 254
pool-v6 2001:db8:50:: 0 / 0 / 2147483646
# swanctl --list-certs --subject "CN=vpn.example.com" | grep -vE 'serial|keyid|subjkey|authkey'
List of X.509 End Entity Certificates
subject: "CN=vpn.example.com"
issuer: "CN=Example Corp Issuing CA"
validity: not before Feb 02 18:39:58 2026, ok
not after Feb 02 18:39:58 2031, ok (expires in 1556 days)
altNames: vpn.example.com
flags: serverAuth ikeIntermediate
CRL URIs: http://pki.example.com/issuing-ca.crl
pubkey: RSA 4096 bits, has private key
no rekeying — это rekey_time = 0, remote: dynamic — селектор клиента станет его виртуальным адресом. В пулах три числа — подключённые сейчас (online), отключившиеся, но с закреплённым адресом (offline), и размер; размер пула IPv6 — те самые 2³¹ − 2. У сертификата оба EKU и has private key — ключ найден в /etc/swanctl/private.
Активные подключения показывает swanctl --list-sas: у подключения удалённого доступа к строке remote с идентификатором и адресом клиента добавляются EAP: '<имя пользователя>' и в квадратных скобках виртуальный адрес, а режим туннеля за NAT — TUNNEL-in-UDP.
Журнал одного подключения Windows-клиента, без строк с идентификаторами производителей, запросов сертификатов неизвестных центров, фрагментов и отправки пакетов:
# journalctl -u strongswan -o cat --since '2026-10-26 20:22:40' --until '2026-10-26 20:22:55' | grep -vE 'vendor ID|unknown ca with|endpoint changed|received fragment|EF\(|\[NET\]'
11[ENC] parsed IKE_SA_INIT request 0 [ SA KE No N(FRAG_SUP) N(NATD_S_IP) N(NATD_D_IP) V V V V ]
11[IKE] 203.0.113.77 is initiating an IKE_SA
11[IKE] IKE_SA (unnamed)[201] state change: CREATED => CONNECTING
11[CFG] selected proposal: IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048
11[IKE] remote host is behind NAT
11[IKE] sending cert request for "CN=Example Corp Root CA"
11[IKE] sending cert request for "CN=Example Corp Issuing CA"
11[ENC] generating IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) CERTREQ N(FRAG_SUP) N(CHDLESS_SUP) N(MULT_AUTH) ]
05[ENC] parsed IKE_AUTH request 1 [ IDi CERTREQ N(MOBIKE_SUP) CPRQ(ADDR DNS NBNS SRV ADDR6 DNS6 SRV6) SA TSi TSr ]
05[IKE] received cert request for "CN=Example Corp Root CA"
05[IKE] received 62 cert requests for an unknown ca
05[CFG] looking for peer configs matching 192.0.2.10[%any]...203.0.113.77[10.0.0.15]
05[CFG] selected peer config 'ikev2-eap-radius'
05[IKE] initiating EAP_IDENTITY method (id 0x00)
05[IKE] processing INTERNAL_IP4_ADDRESS attribute
05[IKE] processing INTERNAL_IP4_DNS attribute
05[IKE] processing INTERNAL_IP4_NBNS attribute
05[IKE] processing INTERNAL_IP4_SERVER attribute
05[IKE] processing INTERNAL_IP6_ADDRESS attribute
05[IKE] processing INTERNAL_IP6_DNS attribute
05[IKE] processing INTERNAL_IP6_SERVER attribute
05[IKE] peer supports MOBIKE
05[IKE] authentication of 'vpn.example.com' (myself) with RSA signature successful
05[IKE] sending end entity cert "CN=vpn.example.com"
05[IKE] sending issuer cert "CN=Example Corp Issuing CA"
05[ENC] generating IKE_AUTH response 1 [ IDr CERT CERT AUTH EAP/REQ/ID ]
05[ENC] splitting IKE message (3488 bytes) into 3 fragments
06[ENC] parsed IKE_AUTH request 2 [ EAP/RES/ID ]
06[IKE] received EAP identity 'CORP\alice'
06[CFG] sending RADIUS Access-Request to server 'nps1'
06[CFG] received RADIUS Access-Challenge from server 'nps1'
06[IKE] initiating EAP_MSCHAPV2 method (id 0x01)
06[ENC] generating IKE_AUTH response 2 [ EAP/REQ/MSCHAPV2 ]
16[ENC] parsed IKE_AUTH request 3 [ EAP/RES/MSCHAPV2 ]
16[CFG] sending RADIUS Access-Request to server 'nps1'
16[CFG] received RADIUS Access-Challenge from server 'nps1'
16[ENC] generating IKE_AUTH response 3 [ EAP/REQ/MSCHAPV2 ]
09[ENC] parsed IKE_AUTH request 4 [ EAP/RES/MSCHAPV2 ]
09[CFG] sending RADIUS Access-Request to server 'nps1'
09[CFG] received RADIUS Access-Accept from server 'nps1'
09[IKE] RADIUS authentication of 'CORP\alice' successful
09[IKE] EAP method EAP_MSCHAPV2 succeeded, MSK established
09[ENC] generating IKE_AUTH response 4 [ EAP/SUCC ]
15[ENC] parsed IKE_AUTH request 5 [ AUTH ]
15[IKE] authentication of '10.0.0.15' with EAP successful
15[IKE] authentication of 'vpn.example.com' (myself) with EAP
15[IKE] peer requested virtual IP %any
15[CFG] reassigning offline lease to 'CORP\alice'
15[IKE] assigning virtual IP 192.168.50.1 to peer 'CORP\alice'
15[IKE] peer requested virtual IP 2001:db8:50::2
15[CFG] reassigning offline lease to 'CORP\alice'
15[IKE] assigning virtual IP 2001:db8:50::1 to peer 'CORP\alice'
15[IKE] building INTERNAL_IP4_DNS attribute
15[IKE] building INTERNAL_IP4_DNS attribute
15[IKE] building INTERNAL_IP6_DNS attribute
15[IKE] building INTERNAL_IP6_DNS attribute
15[IKE] IKE_SA ikev2-eap-radius[201] established between 192.0.2.10[vpn.example.com]...203.0.113.77[10.0.0.15]
15[IKE] IKE_SA ikev2-eap-radius[201] state change: CONNECTING => ESTABLISHED
15[CFG] selected proposal: ESP:AES_CBC_256/HMAC_SHA1_96/NO_EXT_SEQ
15[IKE] CHILD_SA ikev2-eap-radius-child{219} established with SPIs c5f5e571_i 8151acd4_o and TS 0.0.0.0/0 ::/0 === 192.168.50.1/32 2001:db8:50::1/128
15[ENC] generating IKE_AUTH response 5 [ AUTH CPRP(ADDR ADDR6 DNS DNS DNS6 DNS6) SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_6_ADDR) N(ADD_6_ADDR) N(ADD_6_ADDR) ]
Здесь видна вся схема из начала статьи. Первый IKE_AUTH клиента — IDi CERTREQ … CPRQ(…) SA TSi TSr без AUTH: просьба об EAP, запрос адреса и 63 запроса сертификатов — по одному на каждый доверенный корень Windows, из которых шлюз узнал один. Идентификатор клиента — 10.0.0.15, его адрес за домашним роутером. Шлюз отвечает IDr CERT CERT AUTH EAP/REQ/ID — свой сертификат, промежуточный центр, подпись и запрос идентификатора — и режет ответ на три фрагмента. Дальше три пары RADIUS: идентификатор → вызов MS-CHAPv2, ответ → сообщение об успехе от сервера, подтверждение → Access-Accept. MSK established — те самые ключи из Access-Accept, из которых затем считается последний AUTH. Пул выдаёт пользователю тот же адрес, что в прошлый раз (reassigning offline lease), и ответ уходит с CPRP(ADDR ADDR6 DNS DNS DNS6 DNS6): только адреса и DNS, NBNS и прочее strongSwan не заполняет. IKE договорился о MODP_2048 — на этом клиенте включён NegotiateDH2048_AES256, — а ESP о HMAC_SHA1_96: своей политики IPsec у подключения нет, и SHA-1 — единственная целостность, которую Windows предлагает для ESP. Без aes256-sha1 в esp_proposals это подключение не поднялось бы.
Тот же разговор глазами NPS — файл учёта (о его формате — в части 26), только записи политики VPN:
PS C:\> Invoke-Command -ComputerName nps1 -ScriptBlock {
$type = @{ '1'='Access-Request'; '2'='Access-Accept'; '3'='Access-Reject'; '11'='Access-Challenge' }
Get-Content (Get-ChildItem C:\Windows\System32\LogFiles\IN*.log | Sort-Object Name | Select-Object -Last 1).FullName |
Where-Object { $_ -like '*Virtual Private Network*' } | Select-Object -Last 6 | ForEach-Object {
$e = ([xml]$_).Event
[PSCustomObject]@{ Time = $e.Timestamp.'#text'.Split(' ')[1]; Packet = $type[$e.'Packet-Type'.'#text']; Port = $e.'NAS-Port'.'#text'; Calling = $e.'Calling-Station-Id'.'#text'; Eap = $e.'EAP-Friendly-Name'.'#text' } }
} | Format-Table Time, Packet, Port, Calling, Eap -AutoSize
Time Packet Port Calling Eap
---- ------ ---- ------- ---
20:22:48.502 Access-Request 201 203.0.113.77[43113]
20:22:48.502 Access-Challenge
20:22:48.546 Access-Request 201 203.0.113.77[43113]
20:22:48.546 Access-Challenge
20:22:48.576 Access-Request 201 203.0.113.77[43113] Microsoft: Secured password (EAP-MSCHAP v2)
20:22:48.576 Access-Accept Microsoft: Secured password (EAP-MSCHAP v2)
Три пары за 74 миллисекунды. NAS-Port 201 — тот же номер, что у ikev2-eap-radius[201] в журнале шлюза: по нему запись NPS связывается с подключением на шлюзе. Первый запрос целиком — все элементы строки:
Timestamp=10/26/2026 20:22:48.502 | Computer-Name=NPS1 | Event-Source=IAS | User-Name=CORP\alice | NAS-Port-Type=5 | Service-Type=2 | NAS-Port=201 | NAS-Port-Id=ikev2-eap-radius | NAS-IP-Address=192.0.2.10 | Called-Station-Id=192.0.2.10[4500] | Calling-Station-Id=203.0.113.77[43113] | NAS-Identifier=strongSwan | Client-IP-Address=192.168.1.1 | Client-Vendor=0 | Client-Friendly-Name=vpn-gw | Proxy-Policy-Name=Virtual Private Network (VPN) Connections | Provider-Type=1 | SAM-Account-Name=CORP\alice | Fully-Qualifed-User-Name=CORP\alice | Class=311 1 192.168.1.20 10/26/2026 10:43:14 104 | Authentication-Type=5 | NP-Policy-Name=Virtual Private Network (VPN) Connections | Packet-Type=1 | Reason-Code=0
NAS-IP-Address — внешний адрес шлюза, Client-IP-Address — внутренний, с которого пришёл пакет и по которому NPS нашёл клиента vpn-gw. Calling-Station-Id — внешний адрес пользователя с портом, вместо MAC у Wi-Fi. В Access-Challenge NPS кладёт Session-Timeout = 60 — сколько секунд ждать следующего шага EAP, — а в Access-Accept — Framed-Protocol, Service-Type, атрибуты Microsoft из политики и MS-CHAP-Domain = 01434F5250 (байт-идентификатор и CORP); ключи MS-MPPE в файл учёта не пишутся. Записей Accounting-Request нет: на этом шлюзе accounting не включён. По тем же полям в журнале Security пишется событие 6272 — с Client Friendly Name = vpn-gw и внешним адресом пользователя в Calling Station Identifier; журнал Security перезаписывается быстро, поэтому для истории подключений VPN надёжнее файл учёта.
Источники
IKEv2 и IPsec:
- RFC 7296 — IKEv2: IKE_SA_INIT и IKE_AUTH, EAP в IKE_AUTH и MSK в финальном AUTH (раздел 2.16), CERTREQ, Configuration Payload, проверка живости;
- RFC 7383 — фрагментация сообщений IKEv2;
- RFC 4555 — MOBIKE;
- RFC 3948 — ESP в UDP для прохода через NAT;
- RFC 4303 — ESP.
EAP и RADIUS:
- RFC 3748 и RFC 3579 — EAP и его перенос в RADIUS;
- RFC 2759 — MS-CHAPv2;
- RFC 5216 — EAP-TLS;
- RFC 2548 — атрибуты Microsoft:
MS-MPPE-Send-Key,MS-MPPE-Recv-Key,MS-CHAP-Domain; - RFC 2865 и RFC 2866 — RADIUS (
NAS-Port-TypeVirtual = 5,Calling-Station-Id,Class) и учёт; - RFC 8555 — ACME.
strongSwan:
- swanctl.conf — параметры соединения:
unique,mobike,rekey_time,dpd_delay(иdpd_timeout, не действующий в IKEv2),send_cert,eap_id,dpd_action, атрибуты пулов; - swanctl —
--list-conns,--list-pools,--list-certs,--list-sas,--stats,--load-creds; - eap-radius Plugin — серверы RADIUS,
accounting,class_group,filter_id,nas_identifier, атрибуты, которые плагин передаёт клиенту; - Virtual IP Addresses — пулы, закрепление адреса за идентификатором, пул
radiusдляFramed-IP-Address; - Retransmission — повторные передачи и время до признания собеседника недоступным;
- Windows Clients — предложения Windows по умолчанию (
modp1024, SHA-1 в ESP),NegotiateDH2048_AES256, PFS и ошибка 13816,rekey_time = 0, фрагментация с 1803, маршрут IPv62000::/3; - Windows Certificate Requirements — EKU, имя в Subject или SAN, ikeIntermediate для старых macOS, ошибка 13801;
- Forwarding and Split-Tunneling — пересылка, NAT и раздельный туннель на шлюзе;
- iptables-extensions — модули
policyиtcpmss, цельTCPMSS.
Network Policy Server:
- Connection Request Policies и Network Policies — две очереди политик, условия, ограничения, игнорирование Dial-in;
- RADIUS Clients — регистрация шлюза как клиента RADIUS;
- Netsh Commands for Network Policy Server —
netsh nps add crp,add np,show np; - Interpret NPS Database Format Log Files — поля файла учёта;
- NPS Reason Codes — коды причин отказа;
- Audit Network Policy Server — события 6272 и 6273;
- Protected Users Security Group — запрет NTLM для членов группы.
Клиент Windows:
- Add-VpnConnection, Set-VpnConnection, Get-VpnConnection — профиль подключения, метод аутентификации, раздельный туннель;
- Set-VpnConnectionIPsecConfiguration — алгоритмы IKE и ESP, группы Диффи — Хеллмана и PFS;
- Add-VpnConnectionRoute — маршруты через VPN;
- VPNv2 CSP и About Always On VPN — профиль через MDM и автоподключение;
- Troubleshoot Always On VPN — ошибки 809, 812, 13801, 13806 и их причины, события RasClient;
- Routing and Remote Access Error Codes и System Error Codes (12000-15999) — тексты кодов 691, 809, 812 и 13801, 13806, 13868.
Блок о сертификатах и доступе к сети закончен: один каталог, один NPS, одни и те же учётные записи для Wi-Fi, провода и VPN, а шлюз может быть любым — лишь бы умел RADIUS. Следующие две части — о машинах, которые во всех предыдущих частях были только клиентами DNS и целью для ssh: о Linux-серверах, которые становятся членами домена наравне с Windows.
Дальше — часть 29: ввод Linux в домен. Что значит «член домена» для машины без Netlogon; realmd и sssd, krb5.conf, PAM и NSS; сопоставление SID в идентификаторы POSIX; короткие имена и домен по умолчанию; ограничение входа группами и sudo для доменных групп; регистрация в DNS; и каноническое имя в /etc/hosts, из-за которого Kerberos часто не работает на только что введённой машине.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.