The current common varnish code for decoding the client IP + XFF header into X-Client-IP, X-Trusted-Proxy, etc works fairly well now for the overwhelming majority of traffic (modulo the fact that the zero-sourced trusted proxy database is still fairly incomplete: it only knows OperaMini and Nokia, but not Google proxies and others).
However, there are still a few corner cases it doesn't handle well for the current trusted proxies in some very rare cases (on the order of < 0.001% of all traffic). We've logged traffic where, behind the trusted proxy IP of a Nokia or OperaMini server, the remainder of the XFF list is either entirely meaningless private IPs, or contains the real client IP hidden within both prefixed and suffixed private IPs, as in X-Forwarded-For: 192.0.2.1, <RealClientIP>, 192.0.2.2, <OperaMiniProxyIP>. In both such cases, we currently end up setting X-Client-IP to meaningless private address, which could be confused for our own internal use of the same address space by internal code consumers.
I think the correct pseudocode for solving all related problems correctly (including internal clients and such) looks something like this (inserted into the varnish logic where XFF-decoding takes place currently):
// Default X-Client-IP to X-Real-IP in case it's not set due to strangeness below X-Client-IP = X-Real-IP // Place the true client connection network address on the end of the transmitted XFF for iterative processing: XFFull = X-Forwarded-For + ", " + (client.ip or X-Real-IP as appropriate) // This ACL matches all of our own networks (public and private, both protocols), // all private/invalid spaces in both protocols, and all loopbacks in both protocols. // The logical inverse of this ACL is "all valid public networks which are not WMF-owned" wmf_and_private_and_lo_acl = [ all_wmf_networks, all_private_networks, all_loopback_networks ] // Private IPs are implicitly always "trusted" to set a further entry in XFF, // because they're local to the infrastructure of another trustable server // (either our own servers in the earliest loop iterations, or the most-recent // trusted proxy from the database later). While private, loopback, and WMF // IPs never terminate searching through the list, we never copy them to XCIP // as potential output if we've previously set XCIP to a public IP that's not // our own, as anything private past that point would be private to a remote // network, not our own. Thus, if XCIP ends up as a private IP, it definitely has // our own local meaning (it really is us). seen_nonwmf_public = 0 while (!XFFull.empty()) { checkip = XFFull.pop_from_end() if (!checkip.validate_legal_IP()) { break; } if (checkip =~ wmf_and_private_and_lo_acl) { if (!seen_nonwmf_public) { X-Client-IP = checkip; } } else { seen_nonwmf_public = 1; X-Client-IP = checkip; prox = netmapper.map("proxies", checkip); if (!prox) { break; } // This code only uses the first proxy to set XTP but silently believes // other nested trusted proxies as well. We could also put an array of // them in XTP, or make a separate header for the array form. // Also, if the trusted proxy is the special case TestProxy, that never gets // recorded here, just silently believed (so that we can test other proxies // as primary via fake XFF) if (!X-Trusted-Proxy && prox != "TestProxy") { X-Trusted-Proxy = prox; } } }
However, we can't really do that in straight VCL realistically because VCL lacks any form of loop control structure (not to mention the rest would be painful too). We could do this with inline C, but it will take a fair amount of work, review, and validation to translate from the above. We might be best off waiting until we've transitioned to post-varnish3 software before tackling this since it's relatively non-critical and it's one more bit of hacky C code to worry about porting into varnish4 or any alternative.