Як клієнт OpenVPN за NAT стає виходом в інтернет
Вихід сам встановлює з'єднання назовні, тому його маршрутизатору не потрібен вхідний порт. Простежте, що маршрутизує концентратор, що пересилає вихід і звідки береться зворотний шлях.
Три хости Linux, одна схема: публічно доступний концентратор OpenVPN, споживчий ноутбук і вихідна машина за звичайним домашнім маршрутизатором, який нічого не пересилає всередину. Вихід сам підключається до концентратора. Далі інтернет-трафік споживача входить у тунель на ноутбуці й виходить через власний висхідний канал виходу. Усе дивовижне в цій схемі випливає з однієї деталі: з'єднання ініціював вихід, тому шлях усередину вже існує і для його створення не потрібне переспрямування порту.
Далі простежується шлях пакета, а не порядок налаштування, бо налаштування має сенс лише тоді, коли відомо, який компонент ухвалює яке рішення. Кожен рівень ухвалює рівно одне рішення, а помилки збираються там, де два рівні вважають одним.
Два транспортні сеанси, один внутрішній шлях
Кожен хост тримає власне зовнішнє з'єднання з концентратором, і жодне з них не знає про інше. Трафік споживача в інтернет — це окремий внутрішній потік, який випадково переноситься всередині тунелю споживача, а потім знову випускається з тунелю виходу. Саме цей потік концентратор має маршрутизувати, і саме його вихід має пересилати.
outer: consumer --TLS--> hub
outer: exit --TLS--> hub
inner: consumer --tun--> hub --internal--> exit --tun--> uplink
Оскільки обидва зовнішні з'єднання виходять із боку клієнта, маршрутизатор виходу ніколи не бачить вхідного SYN і ніколи не потребує правила NAT призначення. Йому треба лише підтримувати встановлений транспорт живим, що він уже робить для будь-якого тривалого з'єднання. Ніщо в публічному інтернеті не повинно спершу досягти виходу, тому динамічна адреса на цьому висхідному каналі не є проблемою. Маршрутизатор усе одно має дозволяти вихідний сеанс, але це той самий дозвіл, який уже використовує браузер.
Що iroute насправді встановлює всередині сервера
Концентратор — єдиний пристрій, що вирішує, якому партнеру належить яке призначення, і ухвалює це рішення у власній таблиці маршрутизації простору користувача, а не в таблиці ядра. Запис client-config-dir для виходу з іменем exit-node може вимагати там типовий префікс:
# /etc/openvpn/server/ccd/exit-node
iroute 0.0.0.0 0.0.0.0
Цей рядок каже процесу сервера «внутрішні пакети з будь-яким призначенням належать цьому клієнту». Він не додає типового маршруту до операційної системи концентратора і не робить концентратор інтернет-шлюзом. Довідник OpenVPN 2.6 тримає внутрішню маршрутизацію, специфічну для клієнта, і маршрутизацію ядра як окремі механізми, і саме це розділення є причиною, чому схема працює, не торкаючись типового маршруту концентратора.
Споживачеві потрібна дзеркальна інструкція, доставлена як push, щоб його власний трафік входив у тунель, а не виходив через локальний шлюз. За топології підмережі пул — це підключений маршрут на пристрої tun виходу, тому зворотному напрямку не потрібен додатковий push; чого вихід не повинен отримувати, то це переспрямування нижче, яке відправило б його власний висхідний трафік назад у тунель, який він намагається використати. Надсилайте його лише споживачеві й читайте застосовані маршрути на клієнті, замість того щоб вірити, що файл CCD обрано.
# /etc/openvpn/server/ccd/consumer-01
push "redirect-gateway def1"
Пересилання виконує ядро виходу
Усередині виходу пакет від споживача надходить на інтерфейс tun, адресований якомусь інтернет-хосту. Ядро дивиться в свою таблицю маршрутизації, знаходить справжній висхідний канал як найкращий збіг і потім застосовує дві незалежні політики: чи можна цей пакет узагалі пересилати і чи може він вийти через цей інтерфейс із цією адресою джерела. Пересилання — це sysctl, і в багатьох дистрибутивах воно вимкнене за замовчуванням.
Тож виходу потрібне постійно ввімкнене пересилання IPv4, правило, що допускає джерела тунелю споживача до висхідного каналу, і шлях відповіді назад через tun. Увімкнення пересилання для всього хоста змінює й поведінку всього іншого, що маршрутизується через нього, тому правило має називати джерела та інтерфейси, а не приймати все.
Зворотний шлях купується трансляцією джерела
Інтернет-хост не має маршруту до адреси 10.90.0.x, тому відповіді були б відкинуті ще до того, як досягли виходу. Перезапис джерела, коли пакет залишає висхідний канал, вирішує це, а відстеження з'єднань розвертає перезапис на зворотному шляху.
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.90.0.0/24 -o enp1s0 -j MASQUERADE
ip -4 route get 10.90.0.9 # run on the exit: must point at the tun
Правило вище — відправна точка для одного висхідного інтерфейсу, а не політика міжмережевого екрана. Звузьте джерело до призначень, які ви справді авторизували, і тримайте власну політику входу виходу окремо, щоб маскування для трафіку тунелю не розкривало тихо локальні служби. На хості з динамічною адресою висхідного каналу маскування позбавляє й ручного відстеження адреси.
Де концентратор може застосовувати правила, а де ні
З client-to-client і традиційним шляхом даних у просторі користувача концентратор передає внутрішні пакети між партнерами всередині процесу сервера. Вони ніколи не проходять через ланцюг FORWARD ядра концентратора, тому правило в ядрі там не може обмежити проходження між партнерами, хоч би як ретельно воно було написане. Застосування має жити у власній політиці сервера: кому дозволено підключатися, який файл CCD він отримує і чи надається взагалі володіння виходом за замовчуванням.
Тому iroute за замовчуванням — широке рішення про довіру. Вихід має законно приймати зворотний трафік із довільних інтернет-адрес, і це має бути машина, якою ви керуєте, яку оновлюєте й спостерігаєте. Дати те саме володіння партнерові, якому ви не довірили б маршрутизатор — та помилка, до якої запрошує ця схема.
Членство — справжній контроль. Будь-який клієнт, який може налаштувати власні маршрути, може спробувати використати вихід, тому обмеження того, хто може підключатися або яким партнерам вихід служитиме, — частина схеми, а не пізніша думка.
Безпечно відмовити, коли вихід зникає
Вимкніть вихід, і внутрішнє володіння за замовчуванням зникає разом з його сеансом. Чого не повинно статися — концентратор тихо стає замінювальним виходом: жодного маршруту за замовчуванням із пулу VPN у висхідний канал концентратора і жодного широкого правила маскування на концентраторі, яке охоче переслало б той самий трафік. Перевірте цю відмову навмисно, а потім перевірте відновлення свіжим потоком, а не сторінкою, яка вже була відкрита.
# on the consumer, with the exit up and then down
ip -4 route get 1.1.1.1
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com
Якщо трафік споживача все ще досягає інтернету після зникнення виходу, його несе щось інше, і задуманого кордону не існує. До DNS і IPv6 теж ставтеся як до окремих рішень: маршрут IPv4 плюс маскування нічого не кажуть про трафік AAAA, а резолвер, просунутий концентратором, відповість звідти, звідки запит справді виходить.