Три хости 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, а резолвер, просунутий концентратором, відповість звідти, звідки запит справді виходить.

Схожі статті