IP Allow-listing on the basis of ApiKey??

Hi,
Firstly, I am trying to set up an allow-list for particular IPs with the help of access control policy.

In addition to this, there is also a requirement where I should be able to enforce IP filtering/whitelisiting based on API key that has been mapped to that particular IP range. For example, lets take an Api key(client_id) ABC and IP range XYZ, then this api key should be mapped to this XYZ IP range. In other words, I should be able to enforce different IP filters based on API key.
If this could be done, please provide the steps and also if possible provide some sample policy.
Thank you!!!

4 REPLIES 4

There's an AccessControl policy in Apigee that can enforce an allow list or a deny list. This is what it looks like to "allow" specific IP ranges, but deny all others (the allow list scenario). 

<AccessControl name='AC-1'>
  <IgnoreTrueClientIPHeader>true</IgnoreTrueClientIPHeader> <!-- recommended always -->

  <!-- optional. If not present, policy uses XFF -->
  <ClientIPVariable>VARIABLE_NAME</ClientIPVariable>

  <IPRules noRuleMatchAction = 'DENY'>
    <MatchRule action = 'ALLOW'>
      <SourceAddress mask='24'>216.14.72.0</SourceAddress>
    </MatchRule>
  </IPRules>
</AccessControl>

The SourceAddress accepts variables - in other words the mask and IP range can be dynamically determined at runtime. It looks like this: 

  <IPRules noRuleMatchAction = 'DENY'>
    <MatchRule action = 'ALLOW'>
      <SourceAddress mask='{mask-variable}'>{ip-range-variable}</SourceAddress>
    </MatchRule>
  </IPRules>

In this case, the mask-variable and ip-range-variable are context variables that must be set prior to executing this AccessControl policy.  Now, how can you do that?  How can you set those variables for each specific API Key?

The "custom attribute" mechanism for developer apps in Apigee is ideal for this scenario. 

Here's a screencast walkthrough of how that works.  The result is , when you use VerifyAPIKey, the values of the custom attributes get loaded into context variables implicitly.  And then you can refer to those variables in your AccessControl policy. 

 

Hi @dchiesa1 ,


Thank you for your quick reply as always!!!
But I couldn't find what I am trying to achieve in your previous reply. The solution what you have posted above is what I have already implemented. In addition to this, I am looking for is to validate the IP address by client(by API-key/Consumerkey). In other words,  registering valid IP address ranges for each client ID(apikey).

 Please let me know if this could be done.
 

Ahh, I see. 

There is no possibility to register custom attributes on a per-clientID basis.  In Apigee, the closest you can come to that, is to attach custom attributes to the "Developer app" entity.  This developer app may have multiple Client IDs.  Normally people use multiple clientIDs only in support of key rotation, and so the assumption that "developer app = client" holds, and using the developer app to store the custom attributes is appropriate.  I understand that you are saying that this approach is not suitable for your purposes. In other words, the assumption that "developer app = client" does not hold for you. 

One possible way to work around this is to use a custom attribute with a name that embeds the client id in question. So imagine that the developer app has two client IDs:   ABCDEFG and also  XYZREMHS .  Then, set custom attributes in the developer app which are "iprange-ABCDEFG" and "iprange-XYZREMHS" .  Then you can use AssignMessage to load the correct IP range into a different variable.  Like this: 

<AssignMessage name='AM-Get-IP-Range'>
  <AssignVariable>
    <Name>var-containing-var-name</Name>
    <Template>developer.app.iprange-{clientid}</Template>
  </AssignVariable>
  <!-- at this point,  var-containing-var-name holds something like 
     developer.app.iprange-ABCDEFG -->
  <AssignVariable>
    <Name>variable-containing-ip-range</Name>
    <Template>{var-containing-var-name}</Template>
  </AssignVariable>
  <!-- at this point, variable-containing-ip-range now holds the IP range that
     was stored as a custom attribute on the developer app -->
</AssignMessage>

Another option is to store a JSON hash as a single custom attribute, maybe named "iprange". The value of that custom attribute might look like this: 

{ 
  "ABCDEFG" : "216.14.172.0" ,
  "XYZREMOHS" : "174.53.128.0",
  ...
}

and use a JSONPath within AssignMessage to extract the  appropriate IP range for your particular client ID.  Similar idea to the above, but it uses the static JSONPath function to get the value. Something like this:

<AssignMessage name='AM-Get-IP-Range'>
  <AssignVariable>
    <Name>jsonpath1</Name>
    <Template>$.{clientid}</Template>
  </AssignVariable>
  <!-- at this point,  jsonpath1 holds something like 
     $.ABCDEFG -->
  <AssignVariable>
    <Name>variable-containing-ip-range</Name>
    <Template>{jsonPath(jsonpath1,developer.app.iprange)}</Template>
  </AssignVariable>
  <!-- at this point, variable-containing-ip-range now holds the IP range that
     was stored as a custom attribute on the developer app -->
</AssignMessage>

 

Just few thoughts..

Personally not a great fan of simple api key verification which is low level security & also with ip whitelisting - anyone can spoof it..
if you are dealing with small set of apps it may be easy but for a large scale you need proper process to keep up with changing ip ranges per client.

rather make sure of your api platform to achieve better things like

1.Use of standards as OAuth 2.0(https://oauth.net/2/) or OAuth 2.1(https://oauth.net/2.1/), OpenID Connect and JSON web tokens to authenticate API traffic
and to define access control rules or grant types that determine which users, groups and roles can access specific API resources.
2.Apply proper rate-limits, quotas
3.Enable transport level mTLS for public facing api's
4.Enable content security with json web signatures - https://docs.apigee.com/api-platform/reference/policies/generate-jws-policy
3.Always make sure to validate the request..may be use - (https://cloud.google.com/apigee/docs/api-platform/reference/policies/oas-validation-policy)
4.make sure you are covering all owasp protection -https://docs.apigee.com/api-platform/faq/owasp-protection
5.

Which ever path you choose on implementation on access control policy, have a fail safe(on worst case) to quickly recover with changing ip's sometimes you may want to turn on/off feature on custom attributes like ip_enabled true or false.

Here is some suggestion..

>During on-boarding you will need apps with custom attributes -https://docs.apigee.com/api-platform/publish/creating-apps-surface-your-api
(custom attributes brings you lot of power - you can enable data masking, logging feature to turn on/off (if you have some custom shared flows) for any pci specific needs,
apply quota's - https://docs.apigee.com/api-platform/reference/policies/quota-policy etc)

In your case say if you add custom attributes like below..

ip_enabled true
allowed_ips x.y.1.1,x.z.2.2

In the proxy you can simply verify using VerifyAPIKey from Authorization header.
VerifyAPIKey policy gives all required custom attributes by default & then apply the logic either using js or AccessControl as suggested & check
if ip_enabled is true , validate against the accepted ip list to accept or reject.
If ip_enable is false you can trust and move on..

With flag you can easily recover(corner case for a short period) on an event of ip range changes, if client doesn't inform the new ranges & you will reject the requests (it happens :))
so to quickly recover you can work on disable with a flag till you get proper information.

Rethink on your implementation & treat your products( & underline implementation) like an asset & enable better security.