SymLinksIfOwnerMatch performance
Ik kwam toevallig het onderstaande tegen.
http://httpd.apache.org/docs/2.4/misc/perf-tuning.html#symlinks
Ondanks dat ik FollowSymLinks (via Plesk) heb uitgeschakeld, werkt rewriting nog steeds. Ik ga er dus vanuit dat SymLinksIfOwnerMatch op een hoger niveau wél is ingeschakeld. Als ik nu de bovenstaande link lees, dan staat daar dat er telkens wordt gecontroleerd op eigenaar.
Wat ik nu niet begrijp uit de documentatie ... stel, ik zet in mijn index.php het volgende:
require '/var/www/framework/index.php';
Gaat ie dan voor iedere map de eigenaar checken, ook als het géén symlink betreft? Of gebeurt dit alleen als het wél een symlink betreft? Bijv. als ik dit zou doen:
require '/framework/index.php';
... waarbij /framework dan een symbolische link is naar /var/www/framework/ ?
Gewijzigd op 30/05/2016 02:27:51 door Ozzie PHP
En dat is dan maar goed ook, want je wil niet hebben dat men "zomaar" van alles kunnen schrijven op je server. Stel je voor dat men kan schrijven in /etc/passwd . hmmm... Dan zou ik toch de rillingen krijgen.
Als je issue zou zijn performance, dan denk ik dat die maar 1000-ste milliseconden wel mee zal vallen ;)
Trouwens, het staat daar ook heel goed beschreven:
Quote:
and a request is made for the URI /index.html, then Apache will perform lstat(2) on /www, /www/htdocs, and /www/htdocs/index.html. The results of these lstats are never cached, so they will occur on every single request.
De vraag is alleen hoe heb jij je configuratie?
Gewijzigd op 30/05/2016 07:58:53 door Bart V B
Bart heeft gelijk, echter is de inhoud van je scripts niet onderhevig aan deze controles, dit geldt alleen voor binnenkomende requests en eventuele rewrites die hieruit volgen. PHP, Perl, Python, pick your poison staan hier los van en doen hun eigen controles.
Volgens mij gaat het niet zozeer om de rechten, maar om de juiste eigenaar (maar wellicht bedoel je dat ook).
Maar doet ie dat dan alleen bij een symbolische link ... of ook bij een 'normale' link? Dus stel, ieder request komt binnen op (een centraal) index.php en vanuit daar wil ik het framework aanroepen.
Nu kan ik dat doen via de 'echte' link:
require '/var/www/framework/index.php';
... of ik kan een symoblische link aanmaken naar de 'framework' map. In index.php krijg je dan:
require '/framework/index.php'; // de map framework is hier dus een symbolische link
In het eerste geval is er geen sprake van een symbolische link. Gaat ie dan toch dan van iedere map ('var' 'www' en 'framework' ) controleren of die dezelfde eigenaar heeft als het centrale index.php bestand? Of doet ie dat alleen in het tweede geval waar ook daadwerkelijk sprake is van een symbolische link?
Anders gezegd: komt SymLinksIfOwnerMatch altijd in actie, of alleen als het ook echt een symbolische link betreft?
>> Bart heeft gelijk, echter is de inhoud van je scripts niet onderhevig aan deze controles ...
Bedoel je daarmee dat als ik vanuit de centrale index.php het framework aanroep de controles plaatsvinden tot aan het index.php bestand in het framework, maar daarna niet meer?
>> Als je issue zou zijn performance, dan denk ik dat die maar 1000-ste milliseconden wel mee zal vallen ;)
Dat zou inderdaad kunnen wat je zegt ... maar omdat het specifiek wordt vermeld als performance issue op de website van Apache ben ik toch wel benieuwd. Stel dat ik in mijn centrale index.php het framework aanroep via een symbolische link (minder typwerk) maar het gevolg is dat vervolgens alle aanroepen worden gecheckt op eigenaar ... dan kan (wellicht) de performance daar ineens wel merkbaar onder gaan lijden. Maar dat hangt dus af van hoe SymLinksIfOwnerMatch nu eigenlijk precies werkt. Vandaar ook mijn vraag.
Gewijzigd op 30/05/2016 15:25:25 door Ozzie PHP
Ik bedoel hiermee dat de controles plaatsvinden tot het moment dat een request slaagt, ofwel de uitvoer van het script begint. Wat er in je script gebeurt staat niet onder controle van de webserver, en als zodanig worden beveiligingen vanuit Apache niet afgedwongen. Het zijn twee verschillende lagen. Ongeacht het FollowSymlinks gedrag binnen Apache zal PHP symlinks gewoon volgen.
De werking van SymlinkIfOwnerMatch is heel eenvoudig: wanneer je een request doet wordt gecontroleerd of het pad symlinks bevat. Dit gebeurt zoals eerder gezegd via lstat() op de verschillende elementen van het pad. Wanneer een symlink "gezien" wordt, wordt de eigenaar van de link vergeleken met de eigenaar van het pad waar deze link naar wijst. Wanneer deze ongelijk zijn wordt het request geweigerd. FollowSymlinks bevat dezelfde controle, minus de gebruikerscontrole: wanneer een symlink wordt aangetroffen wordt deze niet gevolgd en er volgt direct een Forbidden melding.
Gewijzigd op 30/05/2016 15:36:08 door Ben van Velzen
>> Ik bedoel hiermee dat de controles plaatsvinden tot het moment dat een request slaagt ...
Wat bedoel je met "dat een request slaagt"? Als ik require '/framework/index.php'; doe, is het request dan geslaagd als index.php wordt geladen? En wat zou er gebeuren als ik vanuit die index.php weer een nieuwe symlink zou aanroepen?
>> De werking van SymlinkIfOwnerMatch is heel eenvoudig: wanneer je een request doet wordt gecontroleerd of het pad symlinks bevat.
Ah oké .. ik denk dat ik het nu begrijp. Dus zelfs als het geen symlink is, dan worden toch de controles uitgevoerd óf het een symlink is. Correct? En zo ja, dan volgt de controle van de eigenaar.
En bij FollowSymlinks wordt niet gecontroleerd of het een symlink is, maar wordt de link gewoon altijd gevolgd. Op die manier?
Een request is geslaagd zodra de controle wordt overgedragen naar PHP, of er een bestand wordt teruggegeven. Binnen PHP bestaan deze controles niet, en zal de request via een symlink altijd slagen, zolang de user waar PHP onder draait toegang heeft. Welke user dat is is afhankelijk van de configuratie van de brug tussen Apache en PHP. Dit kan via CGI, FastCGI, libphp, etc. Per geval is het verschillend wat configureerbaar is. Zo is er SuEXEC, mod_ruid en suphp om een paar te noemen.
>> Ah oké .. ik denk dat ik het nu begrijp. Dus zelfs als het geen symlink is, dan worden toch de controles uitgevoerd óf het een symlink is. Correct? En zo ja, dan volgt de controle van de eigenaar.
Correct.
>> En bij FollowSymlinks wordt niet gecontroleerd of het een symlink is, maar wordt de link gewoon altijd gevolgd. Op die manier?
Afhankelijk van de instelling wordt hij altijd gevolgd of altijd geweigerd.
Gewijzigd op 30/05/2016 15:57:44 door Ben van Velzen
Ik heb nu PHP 7 FastCGI draaien. In de (overkoepelende) virtual host settings geef ik aan (via rewriting) dat iedere request naar index.php moet worden doorgestuurd. Een bezoeker bezoekt nu www.mijnsite.nl/blabla
Die request wordt dus doorgestuurd naar de index.php in de document root. In die index.php zet ik:
require '/framework/index.php';
De map 'framework' in de bovenstaande link is een symbolische link naar '/var/www/framework'.
Als ik jou nu goed begrijp dan wordt er (als SymlinkIfOwnerMatch is ingeschakeld) gekeken of 'framework' een symlink is. Dat is zo. Vervolgens wordt dan gekeken of de eigenaar van de map 'framework' hetzelfde is als de eigenaar van index.php in de document root. Dit is zo ... is de request nu dan geslaagd en houdt vanaf dit punt het controleren van de eigenaar op, ook al zou ik vanuit het framework weer een andere symlink aanroepen?
Gewijzigd op 30/05/2016 16:08:43 door Ozzie PHP
Dus:
Gebruiker bezoekt www.mijnsite.nl/blabla
Apache bepaalt dat de request gaat naar index.php
Afhankelijk van je configuratie controleert Apache of er symlinks in het pad naar index.php staan en handelt overeenkomstig
Je script wordt aangeroepen (de controle wordt overgedragen aan PHP), verdere controles zullen door Apache niet gedaan worden.
Gewijzigd op 30/05/2016 16:18:31 door Ben van Velzen
En wie is dan eigenlijk de "eigenaar" van die allereerste request? (aan de hand waarvan de controle wordt uitgevoerd)
Maar dat houdt dan dus ook in dat het qua performance eigenlijk niks uitmaakt of je followsymlinks of SymlinkIfOwnerMatch gebruikt, aangezien het enkel om het aanroepen van index.php gaat?
De eigenaar is in dit geval de eigenaar van de link (dus de user/group die aan de link gehangen zijn met chown/chgrp). De eigenaar wordt opgevraagd door Apache, en het doel van de link wordt hiermee vergeleken. Als jij als root een link /framework maakt naar /var/www/framework en die map is eigendom van de user ozzie, en je verandert de eigenaar van de link niet naar ozzie zal Apache het volgen van de link niet toestaan.
Maar dit speelt dus alleen bij de aanroep TOTDAT index.php in de doc root is geladen? En daarna maakt het niet meer uit? Dus als index.php is geladen en ik roep van daaruit de symlink 'framework/index.php/' aan, dan maakt het niet uit als de symlink door root is gemaakt en de map door ozzie?
Toevoeging op 30/05/2016 17:40:15:
Oh ja, als het enkel om het stukje tot aan index.php gaat dan neem ik aan dat het qua performance niet echt uitmaakt of je SymlinkIfOwnerMatch of FollowSymlinks gebruikt? Aangezien Apache op haar website voor performanceverlies waarschuwt??
PHP heeft geen notie van de Apache configuratie, en doet als zodanig niets met symlinks. Ze worden gewoon gevolgd. Zolang de user toegang heeft tot de bestanden die je opvraagt met require, include of aanverwanten worden deze gewoon geopend.
>> Zolang de user toegang heeft tot de bestanden die je opvraagt met require, include of aanverwanten worden deze gewoon geopend.
En dat is dan denk ik weer te regelen met open_basedir correct?
Ook dat, en natuurlijk met gewoon basis bestandsrechten.
Oké cool ... thanks voor je hulp en inzichten!!
Ozzie PHP op 30/05/2016 17:37:51:
Oh ja, als het enkel om het stukje tot aan index.php gaat dan neem ik aan dat het qua performance niet echt uitmaakt of je SymlinkIfOwnerMatch of FollowSymlinks gebruikt? Aangezien Apache op haar website voor performanceverlies waarschuwt??
Om die performance hoef je je denk ik niet zo druk te maken. De webserver op mijn werk heeft vandaag (vrij rustige dag ;-) ) een kleine 2 miljoen requests afgehandeld en praktisch elke request loopt door een of meerdere symlinks. Daar komt nog eens bij dat het een virtuele machine is die een groot deel van de requests via een reverse proxy laat afhandelen door webservers op andere virtuele machines (die ook aan elkaar hangen van symlinks). De content wordt vervolgens dynamisch gegenereerd door een script dat data uit een database ophaalt, in XML-formaat zet en door een XSLT-parser haalt die er HTML van maakt. En dat alles zonder noemenswaardige vertraging.
Ik heb geen idee vanaf welke getallen het performanceverlies merkbaar zou moeten zijn. Ik ben het in ieder geval nog niet tegengekomen. De voornaamste reden dat op piekmomenten onze performance inzakt is doordat onze gigabit-uplink het niet meer trekt.
Thanks voor je toelichting Willem. Fijn om te horen. Wel vreemd dan dat de website van Apachte er voor "waarschuwt". Beetje storm in een glas water dan ...
Ozzie PHP op 01/06/2016 00:01:32:
Thanks voor je toelichting Willem. Fijn om te horen. Wel vreemd dan dat de website van Apachte er voor "waarschuwt". Beetje storm in een glas water dan ...
Ik sluit niet uit dat die alinea een jaar of 20 geleden is geschreven, toen een dergelijke instelling nog wel merkbaar kon zijn voor je performance. :-)
Tja, het zou kunnen, maar het staat toch vermeld bij versie 2.4 ... wel slordig dan.
Je moet je ook blijven afvragen waar je (veel) winst kan pakken he. Mogelijk is het beste wat je kunt halen met het spelen met deze instellingen een micro optimalisatie. Die mogelijk weer teniet wordt gedaan door brakke code of trage queries.
Zo begrijp ik bijvoorbeeld niet (als dit echt een concrete situatie is en niet enkel bedoeld was ter illustratie) dat je vanuit index.php #1 index.php #2 required. Waarom zet je niet meteen index.php #2 in de document root en laad je de rest dynamisch in met behulp van een autoloader (en dus niet met require of include)? Indien dit een concreet geval was dan zit naar alle waarschijnlijkheid de echte bottleneck nog altijd in de applicatie zelf. Weinig optimalisatie hieromheen zal dat verhelpen :).