Натрупването на втори OpenVPN клиент на един Linux хост се проваля на едно от две места. Или вътрешният клиент не може да достигне собствения си сървър, защото външният тунел е поел този транспорт, или вторият клиент пренаписва таблицата на маршрутите и първият тунел спира да работи. И двата проблема са в маршрутизацията, не в криптирането, и се решават преди новият тунел да понесе какъвто и да е приложен трафик. Адресите тук са илюстративни: tun0 е външният тунел, 203.0.113.9 е вътрешният сървър, а TCP 9999 е потокът, който трябва да мине по вътрешния път.

Два режима на отказ, една таблица на маршрутите

Вътрешният клиент отваря UDP сокет към своя сървър и този сокет следва текущата таблица на маршрутите като всеки друг. Ако външният тунел вече притежава маршрута по подразбиране, пакетите за ръкостискане влизат във външния тунел. Това работи само когато външният сървър може да достигне вътрешния сървър и отсрещната страна се съгласи да носи потока; иначе ръкостискането засяда и клиентът опитва отново.

Вторият отказ идва от push от сървъра. Обичайният push е redirect-gateway def1, който инсталира 0.0.0.0/1 и 128.0.0.0/1 вместо да замени маршрута по подразбиране. Затова два клиента, всеки от които инсталира своята /1 двойка, се сблъскват и най-новата двойка печели за почти всяка дестинация. Търсете тези два префикса в таблицата, когато тунел загине след свързването на втория, защото самият маршрут по подразбиране може никога да не е бил променен.

Маркирайте потока и му дайте таблица

Насочвайте избрания трафик, като го маркирате в таблицата mangle и съпоставите маркировката в правило за политика. Правилото сочи към отделна таблица, чийто маршрут по подразбиране е вътрешното устройство, така че само маркираните пакети поемат вътрешния път.

iptables -t mangle -A PREROUTING -p tcp -m multiport --dport 9999 -j MARK --set-mark 0x9999
ip rule add fwmark 0x9999 table 200 pref 1000
ip route add default dev tun1 table 200

Повечето откази в този набор правила идват от две грешки. Стойността на маркировката, зададена от защитната стена, и стойността, съпоставена от правилото, трябва да са идентични, защото несъответствие не дава нито грешка, нито ефект. А PREROUTING вижда само пакети, идващи отвън, затова връзка, започната на този хост, се нуждае от правило OUTPUT със същото съпоставяне, преди изобщо да бъде маркирана.

Отнемете собствеността върху маршрутите от втория клиент

Две опции пречат на втория клиент да редактира таблицата и те действат на различни места. route-nopull игнорира изпратените опции за маршрути, така че нищо не се инсталира. route-noexec приема изпратените опции в средата на клиента, но оставя промяната на таблицата на hook, което е точно каквото искате, когато трябва да видите какво е предложил сървърът.

Hook работи с изпратените стойности в средата си и трябва да добавя само записите, които сте одобрили. Направете го идемпотентен: изчистете таблицата, която притежава, преди да я изградите отново, и не предполагайте, че предишното изпълнение е оставило чисто състояние. Повторно свързване извиква hook отново и hook, който добавя без изчистване, ще натрупа дублиращи маршрути.

Изберете един собственик и го запишете. Профил, който задава и двете опции, или hook, който се състезава с ръчен скрипт, създава таблица, която никой не може да обясни по-късно.

Закрепете и двата адреса на сървърите

Вътрешният сървър се нуждае от маршрут до хост през външното устройство, преди вътрешният клиент да се свърже. Точно този запис държи вътрешния транспорт вътре във външния тунел, вместо да му позволи да следва маршрута по подразбиране.

ip route add 203.0.113.9/32 dev tun0
ip route get 203.0.113.9

Добавете съответен маршрут до хост за външния сървър и в главната таблица, иначе повторно свързване може да насочи собствения транспорт на външния клиент в собствения му тунел. Редът не е по избор: добавяйте тези записи след като съответният интерфейс съществува, тоест чрез hook или скрипт, който се изпълнява след свързването. Маршрут, сочещ към изключено устройство, се премахва или игнорира, така че запис, направен при зареждане, не оцелява последователността, която сте предполагали.

Проверявайте резултата от търсенето, вместо да четете цялата таблица. Изходът от търсенето на маршрут посочва устройството и адреса на източника, които ядрото ще използва, а точно това получава клиентът, когато отвори сокета си.

Тествайте веригата и направете отказа видим

Правете захват на външния интерфейс, докато вътрешният клиент се свързва. Вътрешното ръкостискане трябва да се появи като криптиран полезен товар във външния тунел; ако се появи като обикновен UDP към 203.0.113.9, маршрутът до хост липсва и веригата никога не е била изградена.

Верига, която тихо се връща към физическия ъплинк, е най-лошият изход, защото състоянието на интерфейса не обявява нищо и вътрешният трафик излиза от грешен адрес на източника. Ако вторият клиент може да се свърже директно, когато външният тунел падне, изключете този път или го обвържете с външния интерфейс, за да е видим отказът.

Завършете с два потока и един дъмп на правилата: маркирана връзка и немаркирана трябва да поемат различни пътища, а ip rule show трябва да изброи приоритета, който сте имали предвид. Ако двата потока поемат един и същ път, маркировката не се прилага там, където мислите.

Подобни статии