Veröffentlicht von & unter Allgemein, Apache, ColdFusion, Varnish.

varnish-server-configIch benutze im Moment eine sehr proxylastige Konfiguration für eine sehr gut besuchte Webseite. Dabei geht es über zwei Proxyserver zum eigentlichen Ziel.

Cloudflare (Global Proxy1) -> Varnish ( Local Proxy1)

Cloudflare dient als Application Firewall und als CDN. Zu dem habe ich die Möglichkeit schnell und automatisiert auf ein Backup System zu routen. Feine Sache!

Varnish habe ich vor allem im Einsatz für Chats die über Websockets laufen. Damit kommt Varnish prima klar. Ich kann so die Chats an eine Domain binden und diese lokal über verschiedene Ports ansteuern. Aber auch WordPress und eine Railo (ColdFusion) Installation via Tomcat werden darüber angesteuert.

Das ganze lief ohne Probleme, bis ich den Cloudflare Proxy dazu geschaltet hatte. Die IP des Users ist nun nicht mehr am Railo bzw. ColdFusion Server angekommen. Dass dieses Problem auftauchen würde, war mir schon klar aber für eine gute Lösung musste ich doch eine ganze Weile recherchieren und selbst testen.

Es gibt viele Möglichkeiten wieder an die IP zu kommen. Meine Lösung sollte aber eine Umstellung des Railo/ Coldfusion Servers vermeiden, so dass die CGI Variable (cgi.remote_addr) in Railo ganz normal die User IP bekommen soll.

Um es kurz zu machen, hier die Lösung.

Im Varnish Server wird folgendes in der default.vcl unter sub vcl_recv eingetragen:

    if (req.http.cf-connecting-ip) {
 	set req.http.remote_addr = req.http.cf-connecting-ip;
    }
    else {
	set req.http.remote_addr = client.ip;
    }

Cloudflare übergibt die User IP freundlicherweise im HEADER „cf-connecting-ip“. Um das ganze nachvollziehbarer zum machen, setze ich die cf-connecting-ip auf den HEADER.remote_addr. Ist Cloudflare nicht aktive (nur DNS Routing) wird die direkte Client IP benutzt und auf HEADER.cf-connecting-ip gesetzt.

Damit haben wir die User IP im Header als remote_addr stehen. Damit kann aber Railo/ Coldfusion noch nichts anfangen, denn die Variable cgi.remote_addr ist eine Server Environment Variable und keine Header Variable. Mit Varnish kann man keine Environment Variablen setzen, zumindest habe ich in der Doku zu Varnish nichts dazu gefunden.

Auch hier gibt es mehrere Möglichkeiten. Die einfachste für mich war, die Erweiterung der Server.xml im Tomcat, welche unter /conf zu finden ist.

Man sucht erst einmal die Host Definition in der XML, welche so ausschauen könnte:

 <Host name="127.0.0.1"  appBase="webapps"
            unpackWARs="false" autoDeploy="false">

Danach wird ein Codeschnipsel gesetzt:

<Valve className="org.apache.catalina.valves.RemoteIpValve" 
 remoteIpHeader="remote_addr"
 requestAttributesEnabled="true"/>

Eine genaue Beschreibung der Klasse RemoteIpValve findet ihr hier. Im Prinzip sagt der Code „Benutze den Header remote_addr als Client IP“.

requestAttributesEnabled=“true“ – wird wahrscheinlich ab Tomcat 8 benötigt! Bei Tomcat 6 und 7 brauchte ich das nicht.

Man könnte den Eintrag in Varnish auch weglassen und gleich folgendes eintragen:

<Valve className="org.apache.catalina.valves.RemoteIpValve" 
 remoteIpHeader="cf-connecting-ip"
 requestAttributesEnabled="true" />

Persönlich finde ich es transparenter und später nachvollziehbarer den Umweg über Varnish zu gehen. Macht es so wie ihr am besten damit klar kommt. Bei der zweiten Version solltet Ihr aber schauen, dass der Header cf-connecting-ip auch übergeben wird, wenn Cloudflare deaktiviert ist.

Verbesserungen, Fragen und Kritik sind wie immer gerne in den Kommentaren gesehen.

Nachtrag:
Achtet bei beim Tomcat Container auf eine Verbindung zu 127.0.0.1 oder Localhost. Mir ist aufgefallen, dass eine Hostname in dem Container wohl nicht funktioniert. Folgendes klappt bei mir zum Beipiel nicht…

  <Host name="www.meinedomain.com"  appBase="webapps"
            unpackWARs="false" autoDeploy="false">

Auch ein Alias in dem Container brachte kein Erfolg. Warum dies so ist, habe ich leider noch nicht rausgefunden.

Log

Das Tomcat Log kann man dann einfach noch durch die Remote IP erweitern.

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log." suffix=".txt"
    pattern="%{X-Forwarded-For}i %l %u %t %r %s %b %{User-Agent}i %{Referer}i" resolveHosts="false"/>