OpenVPN PHP-hook: trusted_ip versus ifconfig_pool_remote_ip
Twee adressen met twee verschillende betekenissen, gelezen door een proces dat u niet hebt gestart. Wat de hookvariabelen zijn, en waarom een lege $_ENV niets bewijst.
Een lege $_ENV-array in een hookscript voelt als bewijs dat OpenVPN niets heeft doorgegeven. Meestal bewijst het iets heel anders: PHP was zo geconfigureerd dat die superglobal niet werd gevuld, en de waarden stonden al die tijd in de procesomgeving. De tweede verkeerde conclusie is subtieler, omdat de twee adresvariabelen in een verbindingshook verschillende dingen betekenen en door elkaar worden gebruikt. Beide fouten kosten een middag als je met de verkeerde begint.
Twee adressen, twee verschillende beweringen
Het buitenste eindpunt is wat de server waarnam toen de client verbond, beschikbaar als trusted_ip voor IPv4 en trusted_ip6 voor IPv6. Het virtuele adres is wat de pool voor deze sessie toewees, beschikbaar als ifconfig_pool_remote_ip.
Ze beantwoorden verschillende vragen, en een record heeft meestal één ervan nodig, niet beide. De eerste is waar de verbinding vandaan kwam, wat een gedeeld NAT-adres kan zijn dat honderden mensen gebruiken. De tweede is waar deze sessie binnen de tunnel leeft, en het kan later aan een andere sessie worden toegewezen. De twee verwarren levert records op die aannemelijk lijken en onjuist zijn.
Een minimale uitlezing ziet er zo uit.
$outer4 = getenv('trusted_ip');
$outer6 = getenv('trusted_ip6');
$inner = getenv('ifconfig_pool_remote_ip');
$outer = $outer4 !== false ? $outer4 : $outer6;Geen van beide waarden identificeert een persoon. De ene is een netwerkwaarneming, de andere een lease, en een record dat een van beide als stabiele apparaatidentificatie behandelt, is onjuist zodra een client opnieuw verbindt vanaf een ander netwerk of een ander pooladres krijgt. Kies het veld op basis van de vraag die het record moet beantwoorden en schrijf die vraag naast de kolom.
Controleer hoe PHP werd gestart, niet je shell
De hook draait als kind van de VPN-daemon met een omgeving die de daemon samenstelt. Je interactieve shell heeft een andere, en het script met de hand uitvoeren test de omgeving van de shell, niet die van de hook.
Begin met te bevestigen dat de hook echt afging in de fase die je verwachtte. Een script dat aan de verkeerde fase hangt, of dat nooit liep omdat de richtlijn naar een ander pad wijst, geeft precies hetzelfde symptoom als een ontbrekende variabele. Voeg een bewuste markeringsregel bovenaan het script toe en bevestig dat die verschijnt voordat je iets anders onderzoekt. Eén regel met tijdstempel in het log scheidt «nooit gelopen» van «gelopen zonder de waarde».
Bevestig daarna welke interpreter en welk configuratiebestand die aanroep gebruikt. De PHP-configuratie voor een webserver aanpassen verandert niets aan een CLI-proces dat OpenVPN start, en beide kunnen volledig verschillende php.ini-bestanden laden.
Waarom een lege $_ENV niets bewijst
PHP's getenv-referentie beschrijft een directe uitlezing van de procesomgeving, die false teruggeeft wanneer de naam ontbreekt. De superglobal $_ENV wordt apart gevuld, en de richtlijn variables_order bepaalt welke superglobals worden gevuld.
Als die richtlijn E weglaat, blijft $_ENV leeg, wat de omgeving ook bevat. Code die erop indexeert meldt dan een ontbrekende variabele die er wel is, en het onderzoek gaat richting OpenVPN terwijl het antwoord in php.ini staat. Controleer de richtlijn specifiek voor de CLI-configuratie, niet voor de webserver.
Lezen met getenv vermijdt de hele verwarring, en een wrapperscript dat de omgeving leegmaakt voordat het de interpreter aanroept, brengt die in een andere vorm terug.
Sla een koppeling op, geen rij-update
Bepaal welke waarde het record nodig heeft voordat je schrijft. Als het doel is vast te leggen waar een sessie vandaan kwam, sla dan het buitenste eindpunt op en label het als een waarneming. Als het doel is een sessie aan een tunneladres te koppelen voor de duur van een verbinding, sla dan het pooladres op en koppel het aan de sessie.
Werk bij op de geauthenticeerde identiteit en de huidige sessie, niet op elke rij die een tunneladres deelt, want pooladressen worden hergebruikt en een late verbrekingsgebeurtenis kan anders de gegevens van een nieuwere verbinding overschrijven. Behandel IPv6 expliciet in plaats van stil alleen de IPv4-waarde te schrijven. Een rij met een NULL-buitenadres is beter dan een rij met het verkeerde, omdat de eerste zichtbaar onvolledig is.
Koppel het schrijven aan de sessie-identificatie die OpenVPN levert, zodat opnieuw verbinden een nieuw record maakt in plaats van het oude te bewerken. Gebruik geparameteriseerde statements en een account met alleen de rechten die de bewerking nodig heeft. Een hook draait met het gezag van de daemon, dus een slordige query is een groter probleem dan een verkeerde logregel.
Diagnosticeer zonder de omgeving te dumpen
Log welke namen aanwezig waren, welke fase liep en welke interpreter werd gebruikt. Print niet de hele omgeving, want een hookproces kan ongerelateerde waarden erven die niet in een logbestand horen.
Verifieer één verbinding van begin tot eind: lees het buitenste eindpunt, lees het toegewezen adres, en bevestig beide tegen de eigen kijk van de server op die sessie. Zodra het paar voor één client is bevestigd, is de koppeling elders te vertrouwen.