Moodle und Reverse Proxies

Heu­te wird es sehr tech­nisch – aber wofür sind Feri­en denn da… Ich hat­te ein­mal meh­re­re Mood­le­sys­te­me hin­ter einem Rever­se Pro­xy lau­fen – das wird den meis­ten nicht viel sagen, daher eine kur­ze Erklärung.

Das Pro­blem

Mood­le ist extremst resour­cen­hung­rig und kann unter hoher Last einen schlecht kon­fi­gu­rier­ten Web­ser­ver in die Knie zwin­gen, beson­ders auf schwach­brüs­ti­gen Maschi­nen (die man pri­vat so finan­zie­ren kann). Da liegt dar­an, das für Mood­le vom Web­ser­ver ein soge­nann­ter PHP-Inter­pre­ter auf­ge­ru­fen wer­den muss, der dann aus zahl­rei­chen Script­vor­ga­ben eine stink­nor­ma­le HTML-Sei­te baut und über den Web­ser­ver an den Brow­ser des Benut­zers aus­lie­fert. Erschwe­rend kommt hin­zu, dass die Scrip­ten von Mood­le zusätz­lich vie­le Daten­bank­ab­fra­gen erzeu­gen und so durch den erfor­der­li­chen Daten­bank­ser­ver (meist MyS­QL) Last erzeu­gen. Ein gut kon­fi­gu­rier­ter Mood­le­ser­ver wird also dafür sor­gen, dass mög­lichst wenig Last beim PHP-Inter­pre­ter und bei der MyS­QL-Daten­bank ankommt – man sagt: Das Backend (PHP&MySQL) muss „geschützt“ werden.

Rever­se Pro­xy als Lösung

Dafür führt kein Weg an einem Rever­se Pro­xy vor­bei. Was macht die­ser genau? Der PHP-Inter­pre­ter und die Daten­bank bau­en ja eine stink­nor­ma­le HTML-Sei­te zusam­men – das kann z.B. die Start­sei­te eines Mood­le­kur­ses sein. Immer wenn der glei­che Nut­zer die glei­che Sei­te auf­ruft, muss die­se wie­der und wie­der gebaut wer­den. Ein Rever­se Pro­xy spei­chert die­se Sei­te im HTML-Code zwi­schen und lie­fert sie bei zwei­ten Auf­ruf direkt an den Brow­ser ohne den Web­ser­ver, den PHP-Inter­pre­ter oder die MyS­QL-Daten­bank zu bemü­hen. Ein Rever­se Pro­xy ist sehr schlank und braucht nur weni­ge Resour­cen. Selbst wenn ein Opcode-Cache wie eac­ce­le­ra­tor oder xcache die PHP-Sei­te direkt bedie­nen kann, sind vor­her zwei Instan­zen mit viel höhe­rem RAM-Ver­brauch betei­ligt (bei Apa­che ein kom­plet­ter Thread, bei ligh­ty der Web­ser­ver­pro­zess und ein fast­C­GI-Thread) – das ist in Last­si­tua­tio­nen nach mei­ner Erfah­rung immer sub­op­ti­ma­ler als gleich per Pro­xy aus­zu­lie­fern. Der Opcode-Cache ist trotz­dem eine wich­ti­ge zusätz­li­che Vor­keh­rung.  Der Rever­se Pro­xy löst gera­de bei meh­re­ren Mood­lein­stan­zen auf dem glei­chen Ser­ver noch eini­ge  wei­te­re Pro­ble­me, aber dazu wei­ter unten mehr.

Rever­se Pro­xy und Moodle

Mood­le ist eine sehr wäh­le­ri­sche Dame. Nach mei­ner Erfah­rung arbei­tet Mood­le nur mit einem Rever­se Pro­xy zusam­men, wenn der Web­ser­ver, der für Mood­le zustän­dig ist, auf den Port 80 lauscht. Ansons­ten gibt es in der Kurs­an­sicht merk­wür­di­ge Bul­let­points und diver­se „Hän­ger“ im Admin­be­reich, obwohl das Sys­tem sonst gut arbei­tet. Auf Port 80 muss dum­mer­wei­se auch der Rever­se Pro­xy lau­schen, damit der Anwen­der nichts von unse­rem Kunst­griff merkt. Wenn der Rever­se Pro­xy also auf dem glei­chen Sys­tem arbei­ten soll (typi­scher­wei­se bei begrenz­tem Bud­get), so muss man sich etwas ein­fal­len las­sen. Mit einer 2. IP geht es ganz gut: Den Web­ser­ver bin­det man an die ers­te und den Rever­se Pro­xy an die zwei­te. Den Web­ser­ver kon­fi­gu­riert man so, dass er nur Ver­bin­dun­gen von der ers­ten IP, also unse­rem Rever­se Pro­xy entgegennimmt.

Ein kon­kre­tes Beispiel

Bei mir zu Hau­se läuft fol­gen­de Konfiguration:

Es gibt auf dem Rou­ter ein Port­for­war­ding von Port 3128 des Mood­le­ser­vers (pri­va­ter Port) nach Port 80 (öffent­li­cher Port). Der Rever­se Pro­xy Squid redet mit dem Web­ser­ver lighttpd und reicht des­sen Ant­wor­ten an den Brow­ser des Benut­zers wei­ter. 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 orig­in­ser­ver name=foo1
cache_peer_domain foo1 foo1.somedomain.tld
cache_peer_access sma allow localnet

cache_peer 127.0.0.1 parent 80 0 no-query orig­in­ser­ver name=webserver
cache_peer_domain web­ser­ver foo2.somedomain.tld
cache_peer_access web­ser­ver allow all

Mood­le läuft öffent­lich unter der Domain foo2.somedomain.tld. Dar­auf dür­fen alle zugrei­fen. Die Web­kon­fi­gu­ra­ti­on eines Gerä­tes in mei­nem Haus­halt liegt auf der Domain foo1.somedomain.tld – dar­auf dür­fen nur Cli­ents aus dem loka­len Netz zugrei­fen (ach­ja – einen loka­len DNS-Ser­ver, der auch Zonen mei­nen Heim­net­zes ver­wal­tet, habe ich natür­lich, spä­tes­tens seit Zen­sur­su­la). Auch ein exter­ner Zugriff via VPN ist denkbar.

Man kann auf die­se Wei­se auch sehr vie­le ver­schie­de­ne Ober­flä­chen (z.B. VDR, Medi­atomb usw.) trans­pa­rent unter simp­len Domain­na­men ver­füg­bar machen, auch wenn die Port­num­mer noch so exo­tisch ist, dar­um ging es mir in die­sem Fall haupt­säch­lich: Die Erhö­hung des WAF war genug Antrieb.

Ich kann also mit dem Rever­se Pro­xy nicht nur mein Backend schüt­zen, ich kann für jede Domain auch Zugriffs­re­geln fest­le­gen – dar­in ist Squid sehr mäch­tig. Ich kann Zugrif­fe auf meh­re­re Web­ser­ver ver­tei­len (Load-Balan­cing) und ich kann ein SSL-Zer­ti­fi­kat für meh­re­re Domains nut­zen (SSL-Pro­xy). Ich moch­te die­ses Set­up damals schon – durch einen Zufall habe ich es heu­te wiederentdeckt.

Facebook Like

9 Kommentare

  • Hal­lo,
    ich habe grad nen squid3 ein­ge­rich­tet mit url_regex whitelist.
    Das funkt alles, aus­ser die log­in page, die will ein­fach nicht.
    wenn ich in der white­list moodle2.unifr.ch ein­ge­be, ist alles ok, aber sobald ich den URL mit /login/ erwei­te­re, kommt im Brow­ser eine Feh­ler­mel­dung. bei allen ande­ren URL Erwei­te­run­gen, z.B. moodle2.unifr.ch/user geht’s. Ich hab mir den Source­code der /login/index.php Sei­te mal ange­guckt und alle URL’s, die dort drin sind, in die White­list rein­ge­schrie­ben. Das Log­in funkt aber lei­der immer noch nicht.
    Kann mir jemand sagen, weshalb??
    Vie­len Dank!

  • a) Das sieht gar nicht danach aus, dass der Pro­xy als Rever­se Pro­xy (vgl. Arti­kel) arbeitet
    b) Könn­te es sein, dass die Log­in­sei­te via SSL (https) abge­si­chert ist?

    Gruß,

    Maik

    • birgit

      ja, ist sie. Soll­te doch aber auch mit https gehn, habe jeden­falls den Link so ein­ge­ge­ben. Aber da Du Dich so gut aus­kennst, dach­te ich, dass ich hier mal nach­fra­ge, es war die ein­zigs­te Sei­te, die intel­li­gent aus­sah, wo was über squid und mood­le gleich­zei­tig stand …

    • Dann ist das (https) die Ursa­che. Ver­su­che mal die vol­le URL zur Log­in­sei­te zu white­lis­ten, also mit https. 

      Je nach Squid-Con­fig kann es sein, dass du dafür noch eine getrenn­te acl brauchst.

  • Birgit

    Ich hat­te es schon pro­biert mit der vol­len URL. Sogar so: https://bla/bla/index.php. Geht lei­der nicht. Auch mit * und $ und was man noch so alles machen kann. Funkt alles nicht. Was meinst Du mit „getrenn­te acl“? Was ande­res als url_regex? – Jeden­falls schon mal vie­len Dank für die Tipp’s, das hilft mir, nicht im Lee­ren zu suchen…

  • … kommt im Brow­ser eine Feh­ler­mel­dung“. Wel­che? Was steht in den Log­da­tei­en von Squid?

  • Birgit

    Feh­ler­mel­dung im Fire­fox: „The pro­xy ser­ver is refu­sing con­nec­tions“. Ich hab jetzt eine whitelist1 gemacht mit nur der https. Geht nicht. Hier die rele­van­ten Ein­trä­ge der squid.conf:

    acl mana­ger pro­to cache_object
    acl local­host src 127.0.0.1/32 ::1
    acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1

    acl white­list 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 # unre­gis­tered 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 # mul­ti­l­ing http
    acl CONNECT method CONNECT

  • Birgit

    log Ein­trag, wenn ich die Sei­te anklicke:
    1354266849.951 0 192.168.171 TCP_DENIED/403 3598 CONNECT moodle2.unifr.ch:443 – NONE/- text/html

  • Letz­ter Absatz – ver­su­che 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 # unre­gis­tered 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 # mul­ti­l­ing http
    acl CONNECT method CONNECT

    MIr ist nicht ganz klar, war­um du

    acl Safe_ports port 80

    zwei­mal dekla­rierst. Zusätz­lich kannst du das Debug­ging erhö­hen, um im Log­file die Regel zu fin­den, an der es scheitert:

    debug_options ALL,1 33,2

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert