Hook събития на OpenVPN: защо името и времевата марка не са ключ
Hook-ът за прекъсване на връзката съобщава времето на свързване, а не времето на прекъсване. Съчетан с потребителското име, той може да приключи грешната сесия; притежавайте идентичност вместо това.
Запис за прекъсване пристига, след като втора връзка под същото име вече е започнала, и манипулатор, който обновява най-новия ред за този потребител, затваря грешната сесия. Грешката не е в сравняването на времевите марки. Тя е в предположението, че име плюс цяла секунда идентифицират една сесия. Hook събитията описват връзки, а една връзка се нуждае от ключ, който принадлежи само на нея.
Започнете с това какво всъщност означават променливите за прекъсване. Само тогава изборът на идентификатор има смисъл.
Какво всъщност носи hook-ът за прекъсване
В Community OpenVPN 2.6 справката за hook-ове time_unix определя като момента, в който клиентът се е свързал; той се задава преди изпълнението на hook-а за свързване и не се предефинира, когато по-късно се задейства hook-ът за прекъсване. time_duration дава дължината на тази сесия в секунди, така че краят е приблизително времето на свързване плюс продължителността. И двете са цели секунди и нито едно от тях не е моментът, в който вашата база данни е обработила събитието. Именно разделянето на тези три времена позволява на корелацията да се задържи, вместо само да изглежда правилна по време на тестване.
Тази аритметика заслужава да бъде записана, защото това е единственото крайно време, което самият hook предлага. Закъснението при обработка и разликите в часовниците между хостовете все още остават извън нея.
Защо очевидната двойка не е уникална
Съчетаването на името с времето на свързване стеснява търсенето и все още не идентифицира сесия. Клиентът може да се свърже отново в същата секунда, така че две сесии могат да споделят и двете стойности. Два екземпляра на демона или подмяна на сървър, която запазва същата конфигурация, могат да произведат тази двойка от различни процеси. Тунелните адреси се рециклират по същата причина, което ги прави атрибути на сесията, а не идентификатори за нея.
Режимът на отказ е тих. Късно прекъсване за първата сесия съвпада с най-новия ред и приключва разпределение, което втората сесия все още използва, така че потребителят преживява случайна загуба на достъп, за която не е допринесъл. Броят на редовете, който след това изглежда правилен, прави проблема по-труден за откриване.
Откъде идва стабилна идентичност
Интерфейсът за управление дава на всеки клиент идентификатор на клиентски екземпляр и този идентификатор е ограничен до екземпляра на демона, който го е издал. Работоспособен външен ключ съчетава стабилен идентификатор на сървъра, епоха, която се променя при стартиране на демона, и този клиентски ID. Епохата е важна, защото идентификаторите се рестартират заедно с процеса, а гол ID на процес може по-късно да бъде използван от нещо несвързано.
Променливите на средата, зададени в дъщерен скрипт за свързване, не преминават към по-късния скрипт за прекъсване на родителския демон, защото тези скриптове се изпълняват като отделни процеси със собствени среди. Затова генерирането на UUID при свързване е половината от решението. Страната на прекъсването трябва да може да назове същия UUID, което означава, че идентичността трябва да живее някъде, достъпно и за двете събития.
Направете приключването условно и идемпотентно
Приключвайте по собственик на сесията, не по потребител. Манипулатор, който изтрива каквото разпределение в момента принадлежи на дадено име, в крайна сметка ще изтрие грешното, а единствената защита е да се провери, че записът, който се премахва, е записът, който това събитие описва.
Интерфейсът за управление приема един свързан управляващ клиент наведнъж, затова се интегрирайте с този, който вече го притежава, вместо да прикачвате втори наблюдател, който се състезава за същия сокет.
Обработвайте повторените събития, без да променяте резултата два пъти, и пазете несдвоените събития за съгласуване, вместо да ги изхвърляте. Извикването при прекъсване има документирани условия на жизнения цикъл и може никога да не дойде след срив или твърдо спиране, така че дизайнът се нуждае от начин да представи сесия, чието събитие за край липсва.
Тествайте неудобните случаи нарочно: две повторни свързвания в рамките на една секунда, два екземпляра на демона, свързване, чиято обработка е неуспешна, и рестартиране на демона в средата на сесия. Ключ, който оцелява и при четирите, си струва да му се вярва.