]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add spoof_client_ip access control
authorSteve Hill <steve@opendium.com>
Mon, 13 May 2013 03:57:03 +0000 (21:57 -0600)
committerAmos Jeffries <squid3@treenet.co.nz>
Mon, 13 May 2013 03:57:03 +0000 (21:57 -0600)
... to control whether TPROXY requests have their source IP address
spoofed by Squid.  The ACL defaults to allow (i.e. the current
behaviour), but using an ACL that results in a deny result will
disable spoofing for that request.

Example config to disable spoofing for all requests:
    spoof_client_ip deny all

Also, polish the TPROXY related flags:

1. The flags.spoofClientIp flag was a general "this is a TPROXY
   request" flag, which was a bit confusing given the name of the
   flag.  So the flags.spoofClientIp flag now only indicates
   whether we want to spoof the source IP or not.

2. The flags.interceptTproxy is added to flag whether the request
   was received on a "tproxy" port or not.

13 files changed:
src/HttpRequest.cc
src/RequestFlags.h
src/SquidConfig.h
src/acl/DestinationIp.cc
src/auth/Acl.cc
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side_request.cc
src/format/Format.cc
src/forward.cc
src/peer_select.cc
src/tunnel.cc

index c064124bba62485950a210d1e08a6bb4c32dde71..f718da1bdfdc1b7f49832da931c898ec2c15b1a6 100644 (file)
@@ -590,7 +590,7 @@ HttpRequest::maybeCacheable()
     // Because it failed verification, or someone bypassed the security tests
     // we cannot cache the reponse for sharing between clients.
     // TODO: update cache to store for particular clients only (going to same Host: and destination IP)
-    if (!flags.hostVerified && (flags.intercepted || flags.spoofClientIp))
+    if (!flags.hostVerified && (flags.intercepted || flags.interceptTproxy))
         return false;
 
     switch (protocol) {
index a6e36abe65403b3c5a5fabb8df5aceceb6ff3e25..f8ab00f958a04cc17187e176d4bb94182d46e21d 100644 (file)
@@ -87,7 +87,11 @@ public:
     bool intercepted :1;
     /** set if the Host: header passed verification */
     bool hostVerified :1;
-    /** request to spoof the client ip */
+    /// Set for requests handled by a "tproxy" port.
+    bool interceptTproxy :1;
+    /// The client IP address should be spoofed when connecting to the web server.
+    /// This applies to TPROXY traffic that has not had spoofing disabled through
+    /// the spoof_client_ip squid.conf ACL.
     bool spoofClientIp :1;
     /** set if the request is internal (\see ClientHttpRequest::flags.internal)*/
     bool internal :1;
index b64ae893565d52bee56aeead0450b054e978ab4b..3d3cfaaf3bd1bf2c36a07e8b3c25c5556ff13126 100644 (file)
@@ -403,6 +403,10 @@ public:
 #if ICAP_CLIENT
         acl_access* icap;
 #endif
+
+        /// spoof_client_ip squid.conf acl.
+        /// nil unless configured
+        acl_access* spoof_client_ip;
     } accessList;
     AclDenyInfoList *denyInfoList;
 
index 86825b74f4f1fe52fbaa19bbf87449e70821fa73..c2a4f53259eb135d338c772d7d93ee43255e8625 100644 (file)
@@ -56,7 +56,7 @@ ACLDestinationIP::match(ACLChecklist *cl)
     // Bypass of browser same-origin access control in intercepted communication
     // To resolve this we will force DIRECT and only to the original client destination.
     // In which case, we also need this ACL to accurately match the destination
-    if (Config.onoff.client_dst_passthru && (checklist->request->flags.intercepted || checklist->request->flags.spoofClientIp)) {
+    if (Config.onoff.client_dst_passthru && (checklist->request->flags.intercepted || checklist->request->flags.interceptTproxy)) {
         assert(checklist->conn() && checklist->conn()->clientConnection != NULL);
         return ACLIP::match(checklist->conn()->clientConnection->local);
     }
index e388d684968f9432e13095a58e49743f5c514147..8f664d54ebdd5175bf1b79724b6b719c1f07db37 100644 (file)
@@ -34,7 +34,7 @@ AuthenticateAcl(ACLChecklist *ch)
     } else if (request->flags.accelerated) {
         /* WWW authorization on accelerated requests */
         headertype = HDR_AUTHORIZATION;
-    } else if (request->flags.intercepted || request->flags.spoofClientIp) {
+    } else if (request->flags.intercepted || request->flags.interceptTproxy) {
         debugs(28, DBG_IMPORTANT, "NOTICE: Authentication not applicable on intercepted requests.");
         return ACCESS_DENIED;
     } else {
index dc7d899356df9a238d7ecfe774f4eceb2c52bf19..6d154335647a04b4fad4f95a4d712adf263b6a33 100644 (file)
@@ -3601,8 +3601,7 @@ parse_port_option(AnyP::PortCfg * s, char *token)
         s->flags.tproxyIntercept = true;
         Ip::Interceptor.StartTransparency();
         /* Log information regarding the port modes under transparency. */
-        debugs(3, DBG_IMPORTANT, "Starting IP Spoofing on port " << s->s);
-        debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (IP spoofing enabled)");
+        debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (TPROXY enabled)");
 
         if (!Ip::Interceptor.ProbeForTproxy(s->s)) {
             debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: TPROXY support in the system does not work.");
index 808063e669d43508ac1e5d6be375a67572e402af..5ea1568b9bb53fb6914fdbbbfb200b0e20c28974 100644 (file)
@@ -1224,6 +1224,26 @@ DOC_START
        sources is required to prevent abuse of your proxy.
 DOC_END
 
+NAME: spoof_client_ip
+TYPE: acl_access
+LOC: Config.accessList.spoof_client_ip
+DEFAULT: none
+DEFAULT_DOC: Allow spoofing on all TPROXY traffic.
+DOC_START
+       Control client IP address spoofing of TPROXY traffic based on
+       defined access lists.
+
+       spoof_client_ip allow|deny [!]aclname ...
+
+       If there are no "spoof_client_ip" lines present, the default
+       is to "allow" spoofing of any suitable request.
+
+       Note that the cache_peer "no-tproxy" option overrides this ACL.
+
+       This clause supports fast acl types.
+       See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
+DOC_END
+
 NAME: http_access
 TYPE: acl_access
 LOC: Config.accessList.http
@@ -2960,6 +2980,7 @@ DOC_START
        
        no-tproxy       Do not use the client-spoof TPROXY support when forwarding
                        requests to this peer. Use normal address selection instead.
+                       This overrides the spoof_client_ip ACL.
        
        proxy-only      objects fetched from the peer will not be stored locally.
        
index f9061a3cafedb806efda6c9fe45de0fa271997e7..18eadc8912d9efb383c90100554bfc5923bc4f94 100644 (file)
@@ -2766,7 +2766,16 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
      */
     if (http->clientConnection != NULL) {
         request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
-        request->flags.spoofClientIp = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
+        request->flags.interceptTproxy = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
+        if (request->flags.interceptTproxy) {
+            if (Config.accessList.spoof_client_ip) {
+                ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
+                request->flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
+                delete checklist;
+            } else
+                request->flags.spoofClientIp = true;
+        } else
+            request->flags.spoofClientIp = false;
     }
 
     if (internalCheck(request->urlpath.termedBuf())) {
@@ -3658,7 +3667,7 @@ httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext, Ssl::BumpMode bum
     else {
         char buf[MAX_IPSTRLEN];
         assert(bumpMode != Ssl::bumpNone && bumpMode != Ssl::bumpEnd);
-        HttpRequest *fakeRequest = new HttpRequest;
+        HttpRequest::Pointer fakeRequest(new HttpRequest);
         fakeRequest->SetHost(details->local.NtoA(buf, sizeof(buf)));
         fakeRequest->port = details->local.GetPort();
         fakeRequest->clientConnectionManager = connState;
@@ -3667,10 +3676,19 @@ httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext, Ssl::BumpMode bum
         fakeRequest->indirect_client_addr = connState->clientConnection->remote;
 #endif
         fakeRequest->my_addr = connState->clientConnection->local;
-        fakeRequest->flags.spoofClientIp = ((connState->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
+        fakeRequest->flags.interceptTproxy = ((connState->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
         fakeRequest->flags.intercepted = ((connState->clientConnection->flags & COMM_INTERCEPTION) != 0);
+        fakeRequest->myportname = connState->port->name;
+        if (fakeRequest->flags.interceptTproxy) {
+            if (Config.accessList.spoof_client_ip) {
+                ACLFilledChecklist checklist(Config.accessList.spoof_client_ip, fakeRequest.getRaw(), NULL);
+                fakeRequest->flags.spoofClientIp = (checklist.fastCheck() == ACCESS_ALLOWED);
+            } else
+                fakeRequest->flags.spoofClientIp = true;
+        } else
+            fakeRequest->flags.spoofClientIp = false;
         debugs(33, 4, HERE << details << " try to generate a Dynamic SSL CTX");
-        connState->switchToHttps(fakeRequest, bumpMode);
+        connState->switchToHttps(fakeRequest.getRaw(), bumpMode);
     }
 }
 
@@ -4205,7 +4223,7 @@ clientListenerConnectionOpened(AnyP::PortCfg *s, const Ipc::FdNoteId portTypeNot
 
     debugs(1, DBG_IMPORTANT, "Accepting " <<
            (s->flags.natIntercept ? "NAT intercepted " : "") <<
-           (s->flags.tproxyIntercept ? "TPROXY spoofing " : "") <<
+           (s->flags.tproxyIntercept ? "TPROXY intercepted " : "") <<
            (s->flags.tunnelSslBumping ? "SSL bumped " : "") <<
            (s->flags.accelSurrogate ? "reverse-proxy " : "")
            << FdNote(portTypeNote) << " connections at "
index 97f858d39d27073e560a3207cddf12df32378818..4d5054a97d10047390c757e7875aac9715859c30 100644 (file)
@@ -670,7 +670,7 @@ ClientRequestContext::hostHeaderVerify()
     }
 
     debugs(85, 3, HERE << "validate host=" << host << ", port=" << port << ", portStr=" << (portStr?portStr:"NULL"));
-    if (http->request->flags.intercepted || http->request->flags.spoofClientIp) {
+    if (http->request->flags.intercepted || http->request->flags.interceptTproxy) {
         // verify the Host: port (if any) matches the apparent destination
         if (portStr && port != http->getConn()->clientConnection->local.GetPort()) {
             debugs(85, 3, HERE << "FAIL on validate port " << http->getConn()->clientConnection->local.GetPort() <<
@@ -970,7 +970,7 @@ clientHierarchical(ClientHttpRequest * http)
     const wordlist *p = NULL;
 
     // intercepted requests MUST NOT (yet) be sent to peers unless verified
-    if (!request->flags.hostVerified && (request->flags.intercepted || request->flags.spoofClientIp))
+    if (!request->flags.hostVerified && (request->flags.intercepted || request->flags.interceptTproxy))
         return 0;
 
     /*
index 4ea43ff474137297692118bd9023c23d5e0e134d..09d976088d11d3a56a513393889c1349601d15c8 100644 (file)
@@ -383,7 +383,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         case LFT_LOCAL_LISTENING_IP: {
             // avoid logging a dash if we have reliable info
             const bool interceptedAtKnownPort = al->request ?
-                                                (al->request->flags.spoofClientIp ||
+                                                (al->request->flags.interceptTproxy ||
                                                  al->request->flags.intercepted) && al->cache.port :
                                                 false;
             if (interceptedAtKnownPort) {
index d872d347c413aaefc6ea78c4b14936d3c19fe251..8ada85e5c0b8685e4f72946432ac61e4ea17f28b 100644 (file)
@@ -151,7 +151,7 @@ void FwdState::start(Pointer aSelf)
     // Bug 3243: CVE 2009-0801
     // Bypass of browser same-origin access control in intercepted communication
     // To resolve this we must force DIRECT and only to the original client destination.
-    const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.spoofClientIp);
+    const bool isIntercepted = request && !request->flags.redirected && (request->flags.intercepted || request->flags.interceptTproxy);
     const bool useOriginalDst = Config.onoff.client_dst_passthru || (request && !request->flags.hostVerified);
     if (isIntercepted && useOriginalDst) {
         selectPeerForIntercepted();
index 897a06fe67166d4692139cae1a8784eac3c6db30..a1be9d59428590cb4718dbaafb85dad0d93be50f 100644 (file)
@@ -237,7 +237,7 @@ peerSelectDnsPaths(ps_state *psstate)
     // on intercepted traffic which failed Host verification
     const HttpRequest *req = psstate->request;
     const bool isIntercepted = !req->flags.redirected &&
-                               (req->flags.intercepted || req->flags.spoofClientIp);
+                               (req->flags.intercepted || req->flags.interceptTproxy);
     const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
     const bool choseDirect = fs && fs->code == HIER_DIRECT;
     if (isIntercepted && useOriginalDst && choseDirect) {
@@ -339,7 +339,7 @@ peerSelectDnsResults(const ipcache_addrs *ia, const DnsLookupDetails &details, v
             if (psstate->paths->size() >= (unsigned int)Config.forward_max_tries)
                 break;
 
-            // for TPROXY we must skip unusable addresses.
+            // for TPROXY spoofing we must skip unusable addresses.
             if (psstate->request->flags.spoofClientIp && !(fs->_peer && fs->_peer->options.no_tproxy) ) {
                 if (ia->in_addrs[n].IsIPv4() != psstate->request->client_addr.IsIPv4()) {
                     // we CAN'T spoof the address on this link. find another.
index af4711824c227525f9e47c3d9d4cd222ba577c1f..ec050ac2995199296fe1c789ade27d52ae053f23 100644 (file)
@@ -553,7 +553,7 @@ tunnelConnected(const Comm::ConnectionPointer &server, void *data)
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     debugs(26, 3, HERE << server << ", tunnelState=" << tunnelState);
 
-    if (tunnelState->request && (tunnelState->request->flags.spoofClientIp || tunnelState->request->flags.intercepted))
+    if (tunnelState->request && (tunnelState->request->flags.interceptTproxy || tunnelState->request->flags.intercepted))
         tunnelStartShoveling(tunnelState); // ssl-bumped connection, be quiet
     else {
         AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",