Когато скрипт client-connect завърши с ненулев код, OpenVPN отхвърля клиента и администраторите обикновено предполагат, че съответният скрипт client-disconnect ще почисти. Често той изобщо не се изпълнява, защото обработката на disconnect зависи от това какво е успял да завърши етапът connect. Разглеждането на отхвърлянето и връщането назад като един механизъм превръща неуспешния вход в изтекло правило на защитна стена или в осиротял запис. Това са две отделни задължения и само едно от тях е автоматично. Прочетете собствения изходен път на куката, преди да обвинявате сървъра, че е оставил състояние.

Ненулевият изход отхвърля клиента, но не отменя работата ви

Изходният статус на client-connect е решение за допускане. Нула означава допускане, всичко друго означава разрушаване на инстанцията, преди да е напълно установена. Нищо в този договор не проверява какво скриптът вече е направил във външния свят. Ако скриптът е добавил правило на iptables, е записал ред или е увеличил брояч, тези промени оцеляват при отхвърлянето.

Същата асиметрия важи и за плъгините, където за една инстанция могат да се изпълняват няколко функции connect. По-късен провал не отменя със задна дата по-ранен успех и по-ранната функция няма как да научи, че съседна функция се е провалила.

Отхвърлянето е автоматично, почистването не е

Ръководството на OpenVPN 2.6 обвързва обработката на client-disconnect с успешното завършване на предходната обработка connect. Документирано изключение покрива каскадни вериги: ако една стъпка connect вече е върнала успех, съответните стъпки disconnect все пак се изпълняват при разрушаването на инстанцията, дори по-късна стъпка connect да се е провалила. Това изключение е тясно и зависи от броя конфигурирани обратни извиквания, затова тестването на един shell скрипт не ви казва нищо за верига.

Начертайте действителната конфигурация, преди да проектирате около нея: кой скрипт, кой плъгин и в какъв ред се изпълняват. Таблицата по-долу изброява какво оставя след себе си половинчатата настройка.

What the hook did before failingWhat the client seesWhat stays behind
Created a firewall entryA rejection, with no tunnelA rule matching an address nobody owns
Wrote a state rowA rejection, with no tunnelA record of a session that never started
Allocated a tunnel addressA rejection, with no tunnelAn address marked busy until something releases it

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

Компенсирайте в скрипта, който се е провалил

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

Конкретен случай: скриптът създава правило на защитна стена за всеки клиент, след което се проваля при записването на реда за състоянието, който го описва. Провалилият се клон трябва да премахне току-що създаденото правило и след това да излезе с ненулев код. Чакането на събитие disconnect, което може никога да не бъде планирано, е начинът едно правило да надживее сесията, за която е било предназначено.

Направете компенсацията идемпотентна и с притежател

Почистването трябва да е безопасно за двукратно изпълнение и точно относно това какво може да изтрие. Късно събитие от по-ранен опит не бива да премахва разпределението на по-нова сесия само защото двете споделят общо име или преизползван адрес на тунел. Записвайте конкретния манипулатор или идентификатор на операцията заедно с ресурса и изтривайте по този ключ, а не по име. Транзакциите на базата данни помагат само със собствените промени на базата; правило на защитна стена извън тази граница изисква изрична стъпка за компенсация.

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

Тествайте трите пътя, които наистина се различават

Упражнете три случая: отхвърляне преди да е разпределено нещо, отхвърляне след частично разпределение и нормална допусната сесия, която по-късно приключва. Тези три дават различни редове в журнала и различни остатъци. Проверявайте външното състояние след всеки от тях, не само съобщението за провал на клиента, защото провалът на клиента е очакваният резултат в два от трите случая.

Записвайте етапа на куката, изходния статус и какво е било разпределено или освободено, без данните за достъп. Журнал на куката, който назовава създадения ресурс, е това, което прави изтекло правило откриваемо шест месеца по-късно.

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