HTTP VS: Only allow specific client IP but open specific /uri for all.

Hi,

Currently I have an iRule on a HTTP VS that discards requests if the client IP is not in the allow class. Now I have to add an extra requirement to allow ‘all’ if a specific /uri is given. Here’s my current iRule (thanks to previous posts found in the forum).

class myallowedclients {

host 111.22.33.1

host 111.22.33.2

}

rule restrict-rule {

when CLIENT_ACCEPTED {

if { [matchclass [IP::client_addr] equals $::myallowedclients] }{

Do nothing…irule will complete and request will be sent to the pool based on virtual server definition

} else {

discard }

}

}

I may have to use this requirement for multiple HTTP VS with the same client allow list, but different pools so it’d be great if I can use one rule for all.

Thanks in advance.

Victor

It sounds like what you’re looking for is pretty simple, something like this:

when CLIENT_ACCEPTED { 
  if { ! (([matchclass [IP::client_addr] equals $::myallowedclients]) || ([string tolower [HTTP::uri]] eq "/letmein"))}{ 
    discard 
  } 
}

I dumped the empty if case and added the ! to make it a little more straight-forward, but basically I just added an or case along with your current class match.

Colin

Hi Victor,

Since you’re not specifying a different pool in the iRule, it will default to using the virtual server’s pool if the request is allowed through. You can use logic like this to allow all requests from a specific set of client IPs or to an allowed URI:

when CLIENT_ACCEPTED {
   if { not [matchclass [IP::client_addr] equals $::myallowedclients] }{
       Client is not in the allowed class
      set allowed 0
   } else {
      set allowed 1
   }
}
when HTTP_REQUEST {
   if {$allowed or [HTTP::uri] starts_with "/my_global_allowed_uri"}{
       Allow request to go to pool
   } else {
       Disallowed request

       Reset connection
      reject

       Send HTTP reject message
      HTTP::respond 403 content {blocked!}
   }
}

Or for a set of whitelisted URIs:

when CLIENT_ACCEPTED {
   if { not [matchclass [IP::client_addr] equals $::myallowedclients] }{
       Client is not in the allowed class
      set allowed 0
   } else {
      set allowed 1
   }
}
when HTTP_REQUEST {

    Check if requested URI is whitelisted
   switch -glob [HTTP::uri] {
      "/allowed_starts_with/*" -
      "*/allowed_contains/*" -
      "/allowed_exact" {
          set allowed 1
      }
   }
   if { $allowed == 0 }{
       Disallowed request

       Reset connection
      reject

       Send HTTP reject message
      HTTP::respond 403 content {blocked!}
   }
}

Also, if you’re on 9.4.4 or higher, you should remove the $:: prefix from the datagroup references in the iRule:

And if you’re running IIS, there are simple ways to bypass this logic:

DevCentral - An F5 Technical Community

Aaron

Wow you guys are quick! Thanks for the replies. I will test them out with the requester. Thanks again!

Sure thing, let us know how it goes. :wink:

Colin

Colin,

I get the following error:

line 2: [command is not valid in current event context (CLIENT_ACCEPTED)] [HTTP::uri]

Hoolio,

I tried your 2nd sample with the glob because it looks like we’ll have more /uri to allow in the future. However there is something interesting. The iRule works when I hit the home page (it gets blocked and confirmed when I turned on logging). When I go to /myuri it is working as well. However, when I hit the Back button on the browser to the original home page, it lets me in even though the log says I’m blocked. Some kind of loophole, eh? I even cleared my cache and cookies just to be sure.

Thanks

Woops, I didn’t even pay attention to what event you were working in. You’d need to move the iRule (or at least the HTTP portion) into the HTTP_REQUEST event.

If you want help with that I’m more than happy to whip it up for you. just let me know.

Colin

Can you clear your browser cache and retest? If the issue still recurs, can you add debug logging and check /var/log/ltm for the output?

when CLIENT_ACCEPTED {
   if { [matchclass [IP::client_addr] equals $::myallowedclients] }{
      set allowed 1
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is in allowed class"
   } else {
       Client is not in the allowed class
      set allowed 0
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is not in allowed class"
   }
}
when HTTP_REQUEST {

    Check if requested URI is whitelisted
   switch -glob [HTTP::uri] {
      "/allowed_starts_with/*" -
      "*/allowed_contains/*" -
      "/allowed_exact" {
          set allowed 1
         log local0. "[IP::client_addr]:[TCP::client_port]: URI is allowed per whitelist"
      }
   }
   if { $allowed == 0 }{
       Disallowed request

       Reset connection
      reject

       Send HTTP reject message
      HTTP::respond 403 content {blocked!}
      log local0. "[IP::client_addr]:[TCP::client_port]: Blocking request"
   }
}

Aaron

Ok, I’ve cleared my cookies and cache. Here are the steps I took, followed by what I see in the log. (I’m using the HTTP::respond 403 content)

Went to the home page and see the blocked! message:

Jun 21 11:36:37 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Added /myuri in the URL bar without closing browser:

Jun 21 11:36:52 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53477: URI is allowed per whitelist

Hit the Back button on the browser to bring me back to the home page, nothing is logged (and I see the blocked! message from webpage; seems like from cache)

Hit Refresh on the browser a few times and I can see contents on the home page, which I shouldn’t.

Jun 21 11:37:03 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53488: Client is not in allowed class

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53489: Client is not in allowed class

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53488: Blocking request

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53490: Client is not in allowed class

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53491: Client is not in allowed class

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53489: Blocking request

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53490: Blocking request

Jun 21 11:37:07 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53491: Blocking request

Jun 21 11:37:08 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:37:08 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53488: Blocking request

Jun 21 11:37:08 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53489: Blocking request

Jun 21 11:37:08 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53490: Blocking request

Jun 21 11:37:08 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53491: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53488: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53489: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53490: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53491: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:37:12 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53488: Blocking request

Waited a minute, hit Refresh again, and this time I’m blocked.

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53489: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53490: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53491: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53488: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53489: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53490: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53491: Blocking request

Jun 21 11:38:04 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53478: Blocking request

Strange right?

Sorry, I should have included the URI in the logs. Can you try this?

when CLIENT_ACCEPTED {
   if { [matchclass [IP::client_addr] equals $::myallowedclients] }{
      set allowed 1
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is in allowed class"
   } else {
       Client is not in the allowed class
      set allowed 0
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is not in allowed class"
   }
}
when HTTP_REQUEST {

    Check if requested URI is whitelisted
   switch -glob [HTTP::uri] {
      "/allowed_starts_with/*" -
      "*/allowed_contains/*" -
      "/allowed_exact" {
         set allowed 1
         log local0. "[IP::client_addr]:[TCP::client_port]: URI is allowed per whitelist [HTTP::uri]"
      }
   }
   if { $allowed == 0 }{
       Disallowed request

       Reset connection
      reject

       Send HTTP reject message
      HTTP::respond 403 content {blocked!}
      log local0. "[IP::client_addr]:[TCP::client_port]: Blocking request to [HTTP::uri]"
   }
}

Aaron

Home page (shows blocked!)

Jun 21 12:03:56 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53563: Client is not in allowed class

Jun 21 12:03:56 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53563: Blocking request to /

Accessed /myuri (no block)

Jun 21 12:04:08 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53563: URI is allowed per whitelist /myuri

Hit Back button, seeing cached block! page (but no log entries)

Hit Refresh and seeing content (although the page is not completely rendered)

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53571: Client is not in allowed class

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53571: Blocking request to /css/fmw_top_frame_welcome_area.css

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53573: Client is not in allowed class

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53572: Client is not in allowed class

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53574: Client is not in allowed class

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53575: Client is not in allowed class

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53573: Blocking request to /JSLibrary/fmw_all.js

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53572: Blocking request to /css/fmw_bottom_area.css

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53574: Blocking request to /JSLibrary/educational_text.js

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53575: Blocking request to /JSLibrary/DHTMLAPI.js

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53573: Blocking request to /welcome_images/oracle_logo_red.png

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53571: Blocking request to /JSLibrary/getIllustration.js

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53572: Blocking request to /JSLibrary/getIllustration.js

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53574: Blocking request to /welcome_images/oracle_logo_red.png

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53575: Blocking request to /welcome_images/header_back.png

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53573: Blocking request to /welcome_images/background_top.png

Jun 21 12:04:30 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53571: Blocking request to /welcome_images/explore_interactive_overview.png

Hit Refresh again (shows blocked!)

Jun 21 12:09:13 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53578: Client is not in allowed class

Jun 21 12:09:13 tmm tmm[1085]: Rule restrict-rule : 10.10.10.5:53578: Blocking request to /

It seems I can reproduce this only when I get into /myuri and then hit the Back button and Refresh.

Thanks

So you’re testing by sending a 403 for blocked responses, you clear your cache, make a request to the / URI, see the logging for a blocked message but see the content for the root document displayed in the browser? That’s really odd. I don’t see how a request could be made to / and log the 403 response but still allow the request through to the pool.

Can you try adding logging of the SERVER_CONNECTED and HTTP_RESPONSE events:

when CLIENT_ACCEPTED {
   if { [matchclass [IP::client_addr] equals $::myallowedclients] }{
      set allowed 1
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is in allowed class"
   } else {
       Client is not in the allowed class
      set allowed 0
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is not in allowed class"
   }
}
when HTTP_REQUEST {

    Save the URI for logging in HTTP_RESPONSE
   set uri [HTTP::uri]

    Check if requested URI is whitelisted
   switch -glob [HTTP::uri] {
      "/allowed_starts_with/*" -
      "*/allowed_contains/*" -
      "/allowed_exact" {
         set allowed 1
         log local0. "[IP::client_addr]:[TCP::client_port]: URI is allowed per whitelist [HTTP::uri]"
      }
   }
   if { $allowed == 0 }{
       Disallowed request

       Reset connection
      reject

       Send HTTP reject message
      HTTP::respond 403 content {blocked!}
      log local0. "[IP::client_addr]:[TCP::client_port]: Blocking request to [HTTP::uri]"
   }
}
when SERVER_CONNECTED {
   log local0. "[IP::client_addr]:[TCP::client_port]: connected: [IP::server_addr]:[TCP::server_port]"
}
when HTTP_RESPONSE {
   log local0. "[IP::client_addr]:[TCP::client_port]: \$uri=$uri, status=[HTTP::status]"
}

Aaron

Home page blocked

Jun 21 13:51:44 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: Client is not in allowed class

Jun 21 13:51:44 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: Blocking request to /

/myuri is allowed

Jun 21 13:51:50 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: URI is allowed per whitelist /myuri

Jun 21 13:51:50 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: connected: 10.228.152.90:10611

Jun 21 13:51:50 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: $uri=/myuri, status=302

Went back to home page and hit Refresh and it shows some content

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: $uri=/, status=200

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: $uri=/css/fmw.css, status=200

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54096: Client is not in allowed class

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54096: Blocking request to /css/fmw_top_frame_welcome_area.css

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: $uri=/JSLibrary/coneEventHandeler.js, status=200

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54098: Client is not in allowed class

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54099: Client is not in allowed class

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54099: Blocking request to /JSLibrary/educational_text.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54098: Blocking request to /JSLibrary/fmw_all.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54097: Client is not in allowed class

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54100: Client is not in allowed class

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54097: Blocking request to /css/fmw_bottom_area.css

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54100: Blocking request to /JSLibrary/DHTMLAPI.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54096: Blocking request to /JSLibrary/GeneratePopup.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54097: Blocking request to /welcome_images/oracle_logo_red.png

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: $uri=/JSLibrary/getIllustration.js, status=200

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54098: Blocking request to /JSLibrary/educational_text.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54099: Blocking request to /JSLibrary/DHTMLAPI.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54100: Blocking request to /JSLibrary/GeneratePopup.js

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54096: Blocking request to /welcome_images/oracle_logo_red.png

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54097: Blocking request to /welcome_images/header_back.png

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54098: Blocking request to /welcome_images/explore_interactive_overview.png

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54099: Blocking request to /welcome_images/FingerTab.png

Jun 21 13:52:12 tmm tmm[1085]: Rule restrict-rule2 : 10.10.10.5:54089: $uri=/welcome_images/background_top.png, status=200

You’re not setting $allowed back to 0 anywhere outside of the CLIENT_ACCEPTED event. If someone goes to an authorized URI and gets it set to allowed, everything else will be allowed from then on. You’ll need to add a bit to your code by setting a default case in your switch statement that sets allowed to 0 unless the IP is allowed.

Colin

I didn’t think you’d need it in this scenario, but can you add a OneConnect profile to the virtual server and retest? If you’re using SNAT on the serverside connections, you can use the default OneConnect profile with a 0.0.0.0 mask. Else with no SNAT create a custom OneConnect profile with a 255.255.255.255 source mask.

Aaron

Or frankly use a different variable name. uri_allowed vs ip_allowed. Make ip_allowed override uri_allowed, but if ip_allowed != 1 or doesn’t exist, etc. then uri_allowed must == 1.

Colin

Doh… thanks for catching that Colin. I was going a bit nuts.

How about this then:

when CLIENT_ACCEPTED {
   if { [matchclass [IP::client_addr] equals $::myallowedclients] }{
      set allowed_ip 1
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is in allowed class"
   } else {
       Client is not in the allowed class
      set allowed_ip 0
      log local0. "[IP::client_addr]:[TCP::client_port]: Client is not in allowed class"
   }
}
when HTTP_REQUEST {

    Skip the URI checking if the client IP is allowed
   if {$allowed_ip}{

       Exit this event in this rule
      return
   }

    Check if requested URI is whitelisted
   switch -glob [HTTP::uri] {
      "/allowed_starts_with/*" -
      "*/allowed_contains/*" -
      "/allowed_exact" {
         log local0. "[IP::client_addr]:[TCP::client_port]: URI is allowed per whitelist [HTTP::uri]"
      }
      default {
          Send HTTP reject message
         HTTP::respond 403 content {blocked!}
         log local0. "[IP::client_addr]:[TCP::client_port]: Blocking request to [HTTP::uri]"
      }
   }
}

Aaron

Looks good to me. Though due to personal preference I’d use

if{!($allowed_ip)}

Rather than having an empty if case with a return in it, but the functionality is the same.

Colin

Oh, and you’ll want to remove the

set allowed_ip 0

Because you’re only checking for the existence of $allowed_ip later, not it’s value, this might foul you up. Regardless, there’s no need for it.

Colin

Actually, it still doesn’t make sense that the iRule would log that it was blocking but allow the request through. I can see how $allowed would be 1 after making a request to an allowed URI and then a blocked one, but it shouldn’t have logged the ‘blocking request’ message. Odd.

Aaron