简体   繁体   中英

Varnish, Nginx, SSL - IP detection issue

Hi I have a very strange issue and the hosting company is telling me its not possible I am hoping you guys can help.

I have a nginx / varnish which acts as a load balancer and cache sitting in front of my webservers. My web servers are standard apache web servers.

I am having problems with detecting the genuine IP address of users connecting to the system. The $_SERVER["REMOTE_ADDR"] shows the IP address of the load balancer/cache and not of the user visiting the site.

We have written a hack so that with HTTP traffic we send through an X-Forwarded-For header with the correct IP address, but I have been told this is not possible with SSL.

Unfortunately due to strict security requirements we are not able to offload SSL onto the load balancer/cache it has to go to apache.

Has anyone got any ideas on how we could detect the IP address, when using SSL?

thanks for the help, Si

--- VCL Config ---

import std;

C{
        #include <stdlib.h>
}C



sub vcl_recv {    
/* Add X-Forwarded-For header */
    if (req.restarts == 0) {
        if (req.http.X-Forwarded-For) {
            set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }



    /* Fix compression */
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(ico|png|jpe?g|gif|xpm|swf|flv|pdf|mp3|ogg|zip|gz|tgz|bz2|xz|7z)$") {
            remove req.http.Accept-Encoding;
        } else if (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } else if (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            remove req.http.Accept-Encoding;
        }
    }

    /* Handle SSL offloading */
    if (client.ip == "127.0.0.1") {
        std.log("SSL offloading detected " + client.ip + " " + req.http.X-Real-IP);
        set client.identity = req.http.X-Real-IP;
    } else {
        set client.identity = client.ip;
    }

    if (req.http.Cookie) {
       set client.identity = req.http.Cookie;
    }


    set req.http.X-Varnish-XID = req.xid;
    set req.backend = lb231;

    if (req.backend.healthy) {
        set req.grace = 30s;
    } else {
        set req.grace = 1h;
    }

        call normalise_user_agent;

    if (req.request == "PURGE") {
        if (!client.ip ~ purge) {
            error 405 "Not allowed.";
        }
        return(lookup);
    }

    if (req.request != "GET" &&
       req.request != "HEAD" &&
       req.request != "PUT" &&
       req.request != "POST" &&
       req.request != "TRACE" &&
       req.request != "OPTIONS" &&
       req.request != "DELETE") {
        return (pipe);
    }

    if (req.http.Cookie) {
                set req.http.Cookie = ";" + req.http.Cookie;
                set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
                set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1="); # Cookies to keep here
                set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
                set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

                if (req.http.Cookie == "") {
                        remove req.http.Cookie;
                }
    }

    if (req.request != "GET" && req.request != "HEAD") {
         return (pass);
    }


    if (req.url ~ "/nocache") {
        set req.http.X-No-Cache = "true";
        return (pass);
    }

    if (req.request == "BAN") {
        if (client.ip ~ purge) {
            error 401 "Forbidden";
        }
        ban("req.http.Host == " + req.http.X-VCL-Ban-Host + " && req.url ~ " + req.http.X-VCL-Ban-URL);
        error 200 "Ban OK " + req.url + " " + req.http.Host;
    }

    if (req.request == "REFRESH") {
        set req.request = "GET";
        set req.hash_always_miss = true;
    }

    if (req.request != "GET" && req.request != "HEAD") {
        return (pass);
    }

    return (lookup);
}


    if (beresp.status >= 400) {
        /* Cache error pages for a short amount of time */
        set beresp.ttl = 5s;
        set beresp.grace = 5s;
        set beresp.http.Cache-Control = "max-age=5, must-revalidate";
        unset beresp.http.Cookie;
        unset beresp.http.Set-Cookie;
    }

    if (beresp.status == 503) {
        /* Do not cache 503s at all */
        set beresp.ttl = 0s;
        set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate";
        set beresp.http.Pragma = "no-cache";
    }

     if (beresp.http.X-Varnish-TTL) {
        C{
            char *ttl;
            ttl = VRT_GetHdr(sp, HDR_BERESP, "\016X-Varnish-TTL:");
            VRT_l_beresp_ttl(sp, atoi(ttl));
        }C
        #unset beresp.http.X-Varnish-TTL;
    } else {
        set beresp.ttl = 0s;
    }

    if (beresp.ttl <= 0s) {
        set beresp.http.X-Cacheable = "No, not cacheable.";
    } elsif (req.http.Cookie ~ "(UserID|_session)") {
        set beresp.http.X-Cacheable = "No, got session.";
        return(hit_for_pass);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.http.X-Cacheable = "No, Cache-Control=private";
        return(hit_for_pass);
    } else {
        set beresp.http.X-Cacheable = "Yes.";
        set beresp.grace = 1h;
    }
}

sub vcl_deliver {
    if (resp.http.reset-client-side-age) {
       unset resp.http.reset-client-side-age;
       set resp.http.Age = "0";
    }

    if (obj.hits > 0) {
        set resp.http.X-Cache = "Hit";
        set resp.http.X-Cache-Hits = obj.hits;
    } else {
        set resp.http.X-Cache = "Miss";
    }

    if (resp.http.Server == "Varnish") {
        set resp.http.Server = "OnCommerce Framework Ltd";
    }

    unset resp.http.X-Varnish;
    unset resp.http.X-Scrubbed-For;
}

sub vcl_hit {
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
}

sub vcl_miss {
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
}

sub vcl_pipe {
    set bereq.http.Connection = "close";
}

sub vcl_error {

}

sub normalise_user_agent
{
                if(req.http.user-agent ~ "Mobile"){
                                set req.http.X-UA = "mobile";
                }
                else if (req.http.user-agent ~ "Android")
                {
                                set req.http.X-UA = "android";
                }
                else if (req.http.user-agent ~ "Opera Mini/")
                {
                                set req.http.X-UA = "mobile";
                }
                else if (req.http.user-agent ~ "Opera Mobi/")
                {
                                set req.http.X-UA = "mobile";
                }
                else if (req.http.user-agent ~ "iP(ad|od|hone)/")
                {
                                set req.http.X-UA = "iOS";
                }
                else if (req.http.user-agent ~ "MSIE/")
                {
                                if(req.http.user-agent ~ "MSIE\s[1-7]/")
                                {
                                                set req.http.X-UA = "desktop-old";
                                }
                                else
                                {
                                                set req.http.X-UA = "desktop";
                                }
                }
                else if (req.http.user-agent ~ "Chrome/")
                {
                                set req.http.X-UA = "desktop";
                }
                else if (req.http.user-agent ~ "Firefox/")
                {
                                set req.http.X-UA = "desktop";
                }
                else if (req.http.user-agent ~ "Waterfox/")
                {
                                set req.http.X-UA = "desktop";
                }
                else if (req.http.user-agent ~ "Safari/")
                {
                                set req.http.X-UA = "desktop";
                }
                else if (req.http.user-agent ~ "Opera/")
                {
                                set req.http.X-UA = "desktop";
                }
                else if (req.http.user-agent ~ "curl/")
                {
                                set req.http.X-UA = "desktop";
                }
                else
                {
                                set req.http.X-UA = req.http.user-agent;
                }
}

in my default.vcl file I rewrite de "X-Forwarded-For" header this way:

sub vcl_recv {

    # your code

    remove req.http.X-Forwarded-For;
    set req.http.X-Forwarded-For = client.ip;

    # your code again
}

Don't know if this is the hack you were referring, but this way it's working for us. It's important to remove the actual header, so that it doesn't get transmitted as well as the new one.

We have CloudFlare + Varnish, and everything seems working fine!

If you're using Nginx as a cache you need to add

proxy_set_header        X-Real-IP $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header        Host $http_host;

in location . Similar thing goes for Varnish.

sub vcl_rev { 
    set bereq.http.X-Real-IP = client.ip;
    set bereq.http.X-Forwarded-For = req.http.X-Forwarded-For;
    set bereq.http.host = req.http.host;
}

Can you clarify this?

In this scenario you can't even use anything besides a tcp balancer since you can't simply put a plain http cache/proxy like nginx or varnish between as it needs to read (and alter) the http headers which would beat all the ssl/encryption purpose ("man in the middle").

Of course nginx can proxy also https traffic (like proxy_pass https:// yoursite;) to backends but it would still need the SSL certificates for the https to function on client side.

This is why usually the SSL offloading is done on the top level (in your case it would be nginx which then passes the X-Forwarded-For header to varnish which further passes it to apache and apache converts it to client ip).

Ok the solution we went with was to route all SSL traffic around varnish, as were not caching it anyway it made more sense to do this. However if anyone does come up with a solution I would be very interested to hear.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM