Moodle und Reverse Proxies
Heute wird es sehr technisch – aber wofür sind Ferien denn da… Ich hatte einmal mehrere Moodlesysteme hinter einem Reverse Proxy laufen – das wird den meisten nicht viel sagen, daher eine kurze Erklärung.
Das Problem
Moodle ist extremst resourcenhungrig und kann unter hoher Last einen schlecht konfigurierten Webserver in die Knie zwingen, besonders auf schwachbrüstigen Maschinen (die man privat so finanzieren kann). Da liegt daran, das für Moodle vom Webserver ein sogenannter PHP-Interpreter aufgerufen werden muss, der dann aus zahlreichen Scriptvorgaben eine stinknormale HTML-Seite baut und über den Webserver an den Browser des Benutzers ausliefert. Erschwerend kommt hinzu, dass die Scripten von Moodle zusätzlich viele Datenbankabfragen erzeugen und so durch den erforderlichen Datenbankserver (meist MySQL) Last erzeugen. Ein gut konfigurierter Moodleserver wird also dafür sorgen, dass möglichst wenig Last beim PHP-Interpreter und bei der MySQL-Datenbank ankommt – man sagt: Das Backend (PHP&MySQL) muss „geschützt“ werden.
Reverse Proxy als Lösung
Dafür führt kein Weg an einem Reverse Proxy vorbei. Was macht dieser genau? Der PHP-Interpreter und die Datenbank bauen ja eine stinknormale HTML-Seite zusammen – das kann z.B. die Startseite eines Moodlekurses sein. Immer wenn der gleiche Nutzer die gleiche Seite aufruft, muss diese wieder und wieder gebaut werden. Ein Reverse Proxy speichert diese Seite im HTML-Code zwischen und liefert sie bei zweiten Aufruf direkt an den Browser ohne den Webserver, den PHP-Interpreter oder die MySQL-Datenbank zu bemühen. Ein Reverse Proxy ist sehr schlank und braucht nur wenige Resourcen. Selbst wenn ein Opcode-Cache wie eaccelerator oder xcache die PHP-Seite direkt bedienen kann, sind vorher zwei Instanzen mit viel höherem RAM-Verbrauch beteiligt (bei Apache ein kompletter Thread, bei lighty der Webserverprozess und ein fastCGI-Thread) – das ist in Lastsituationen nach meiner Erfahrung immer suboptimaler als gleich per Proxy auszuliefern. Der Opcode-Cache ist trotzdem eine wichtige zusätzliche Vorkehrung. Der Reverse Proxy löst gerade bei mehreren Moodleinstanzen auf dem gleichen Server noch einige weitere Probleme, aber dazu weiter unten mehr.
Reverse Proxy und Moodle
Moodle ist eine sehr wählerische Dame. Nach meiner Erfahrung arbeitet Moodle nur mit einem Reverse Proxy zusammen, wenn der Webserver, der für Moodle zuständig ist, auf den Port 80 lauscht. Ansonsten gibt es in der Kursansicht merkwürdige Bulletpoints und diverse „Hänger“ im Adminbereich, obwohl das System sonst gut arbeitet. Auf Port 80 muss dummerweise auch der Reverse Proxy lauschen, damit der Anwender nichts von unserem Kunstgriff merkt. Wenn der Reverse Proxy also auf dem gleichen System arbeiten soll (typischerweise bei begrenztem Budget), so muss man sich etwas einfallen lassen. Mit einer 2. IP geht es ganz gut: Den Webserver bindet man an die erste und den Reverse Proxy an die zweite. Den Webserver konfiguriert man so, dass er nur Verbindungen von der ersten IP, also unserem Reverse Proxy entgegennimmt.
Ein konkretes Beispiel
Bei mir zu Hause läuft folgende Konfiguration:
Es gibt auf dem Router ein Portforwarding von Port 3128 des Moodleservers (privater Port) nach Port 80 (öffentlicher Port). Der Reverse Proxy Squid redet mit dem Webserver lighttpd und reicht dessen Antworten an den Browser des Benutzers weiter. In der squid.conf sieht das so aus:
http_port ip_des_squid:3128 accel defaultsite=somedomain.tld vhost
cache_peer ip_eines_gerätes_im lokalen_netz 80 0 no-query originserver name=foo1
cache_peer_domain foo1 foo1.somedomain.tld
cache_peer_access sma allow localnetcache_peer 127.0.0.1 parent 80 0 no-query originserver name=webserver
cache_peer_domain webserver foo2.somedomain.tld
cache_peer_access webserver allow all
Moodle läuft öffentlich unter der Domain foo2.somedomain.tld. Darauf dürfen alle zugreifen. Die Webkonfiguration eines Gerätes in meinem Haushalt liegt auf der Domain foo1.somedomain.tld – darauf dürfen nur Clients aus dem lokalen Netz zugreifen (achja – einen lokalen DNS-Server, der auch Zonen meinen Heimnetzes verwaltet, habe ich natürlich, spätestens seit Zensursula). Auch ein externer Zugriff via VPN ist denkbar.
Man kann auf diese Weise auch sehr viele verschiedene Oberflächen (z.B. VDR, Mediatomb usw.) transparent unter simplen Domainnamen verfügbar machen, auch wenn die Portnummer noch so exotisch ist, darum ging es mir in diesem Fall hauptsächlich: Die Erhöhung des WAF war genug Antrieb.
Ich kann also mit dem Reverse Proxy nicht nur mein Backend schützen, ich kann für jede Domain auch Zugriffsregeln festlegen – darin ist Squid sehr mächtig. Ich kann Zugriffe auf mehrere Webserver verteilen (Load-Balancing) und ich kann ein SSL-Zertifikat für mehrere Domains nutzen (SSL-Proxy). Ich mochte dieses Setup damals schon – durch einen Zufall habe ich es heute wiederentdeckt.
Hallo,
ich habe grad nen squid3 eingerichtet mit url_regex whitelist.
Das funkt alles, ausser die login page, die will einfach nicht.
wenn ich in der whitelist moodle2.unifr.ch eingebe, ist alles ok, aber sobald ich den URL mit /login/ erweitere, kommt im Browser eine Fehlermeldung. bei allen anderen URL Erweiterungen, z.B. moodle2.unifr.ch/user geht’s. Ich hab mir den Sourcecode der /login/index.php Seite mal angeguckt und alle URL’s, die dort drin sind, in die Whitelist reingeschrieben. Das Login funkt aber leider immer noch nicht.
Kann mir jemand sagen, weshalb??
Vielen Dank!
a) Das sieht gar nicht danach aus, dass der Proxy als Reverse Proxy (vgl. Artikel) arbeitet
b) Könnte es sein, dass die Loginseite via SSL (https) abgesichert ist?
Gruß,
Maik
ja, ist sie. Sollte doch aber auch mit https gehn, habe jedenfalls den Link so eingegeben. Aber da Du Dich so gut auskennst, dachte ich, dass ich hier mal nachfrage, es war die einzigste Seite, die intelligent aussah, wo was über squid und moodle gleichzeitig stand …
Dann ist das (https) die Ursache. Versuche mal die volle URL zur Loginseite zu whitelisten, also mit https.
Je nach Squid-Config kann es sein, dass du dafür noch eine getrennte acl brauchst.
Ich hatte es schon probiert mit der vollen URL. Sogar so: https://bla/bla/index.php. Geht leider nicht. Auch mit * und $ und was man noch so alles machen kann. Funkt alles nicht. Was meinst Du mit „getrennte acl“? Was anderes als url_regex? – Jedenfalls schon mal vielen Dank für die Tipp’s, das hilft mir, nicht im Leeren zu suchen…
„… kommt im Browser eine Fehlermeldung“. Welche? Was steht in den Logdateien von Squid?
Fehlermeldung im Firefox: „The proxy server is refusing connections“. Ich hab jetzt eine whitelist1 gemacht mit nur der https. Geht nicht. Hier die relevanten Einträge der squid.conf:
acl manager proto cache_object
acl localhost src 127.0.0.1/32 ::1
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1
acl whitelist url_regex ‑i „/etc/squid3/whitelist“
acl whitelist1 url_regex ‑i „/etc/squid3/whitelist1“
acl F230 src 192.168.1.171
acl time_before_examen time MTWHF 06:00–09:53
acl examen time MTWHF 09:54–12:45
acl time_after_examen time MTWHF 12:46–23:00
http_access allow whitelist
http_access allow whitelist1
http_access allow time_before_examen F230
http_access allow time_after_examen F230
http_access deny all examen
http_access deny all
acl SSL_ports port 443
acl Safe_ports port 80 # http
http_access allow whitelist
http_access allow whitelist1
http_access allow time_before_examen F230
http_access allow time_after_examen F230
http_access deny all examen
http_access deny all
acl SSL_ports port 443
acl Safe_ports port 80 # http
#acl Safe_ports port 21 # ftp
acl Safe_ports port 443 # https
#acl Safe_ports port 70 # gopher
#acl Safe_ports port 210 # wais
#acl Safe_ports port 1025–65535 # unregistered ports
#acl Safe_ports port 280 # http-mgmt
#acl Safe_ports port 488 # gss-http
#acl Safe_ports port 591 # filemaker
#acl Safe_ports port 777 # multiling http
acl CONNECT method CONNECT
log Eintrag, wenn ich die Seite anklicke:
1354266849.951 0 192.168.171 TCP_DENIED/403 3598 CONNECT moodle2.unifr.ch:443 – NONE/- text/html
Letzter Absatz – versuche es mal so:
acl SSL_ports port 443
acl Safe_ports port 80 # http
#acl Safe_ports port 21 # ftp
acl Safe_ports port 443 # https
http_access allow whitelist
http_access allow whitelist1
http_access allow time_before_examen F230
http_access allow time_after_examen F230
http_access deny all examen
http_access deny all
#acl Safe_ports port 70 # gopher
#acl Safe_ports port 210 # wais
#acl Safe_ports port 1025–65535 # unregistered ports
#acl Safe_ports port 280 # http-mgmt
#acl Safe_ports port 488 # gss-http
#acl Safe_ports port 591 # filemaker
#acl Safe_ports port 777 # multiling http
acl CONNECT method CONNECT
MIr ist nicht ganz klar, warum du
acl Safe_ports port 80
zweimal deklarierst. Zusätzlich kannst du das Debugging erhöhen, um im Logfile die Regel zu finden, an der es scheitert:
debug_options ALL,1 33,2