IP whitelisting using a list at run time

Sorry, I accidentally posted early.

We would like to add the ability to whitelist by requestor IP. We have done this before using the Access Control policy, but this time we would like to load a list of them from Custom Attributes per Developer App so that we don't have to re-deploy or manually change in the gateway if these change. So we wouldn't know at deployment what the values are nor how many there are.

Is this even possible? Would we need to do this using javascript to access the object associated with access control?

Solved Solved
2 19 2,028
2 ACCEPTED SOLUTIONS

IP whitelisting details are here, http://apigee.com/docs/api-services/reference/access-control-policy

You need to provide concrete IPs for this work, so you cannot use variables, to resolve at runtime.

So back to your question, you could update [only] this policy at runtime using the API

PUT /v1/o/{orgname}/apis/{apiname}/revisions/{revision}/policies/{access_control_policy}

.. new payload with updated IPs..

Thanks,

View solution in original post

All - Apologies for the delay on this. I've created the following SmartDocs topics Unfortunately, the API for update of a policy definition is still under internal review and and should currently be considered deprecated. While you can delete policy using the update/get URI, I didn't include it in SmartDocs because that could be a little dangerous. Deleting policies, IMO, should be more deliberate. There's no "Are you sure?" involved with the API policy delete, and if the policy was being used in a deployed proxy, that would wreak havoc. Thanks.

View solution in original post

19 REPLIES 19

IP whitelisting details are here, http://apigee.com/docs/api-services/reference/access-control-policy

You need to provide concrete IPs for this work, so you cannot use variables, to resolve at runtime.

So back to your question, you could update [only] this policy at runtime using the API

PUT /v1/o/{orgname}/apis/{apiname}/revisions/{revision}/policies/{access_control_policy}

.. new payload with updated IPs..

Thanks,

When using the API call mentioned above to load a new version of the policy, would it require a new deployment or will it truly update in place?

its in place, Sean- no need to deploy

Thanks. Looks like that is our only option, but if we do it correctly, I think it should fulfill our use case. I think that knowing how to update a policy in-place is a potentially useful thing to be able to do.

Sean - FYI, the CIDR notation gives you wildcard-like functionality for specifying ranges of addresses: http://apigee.com/docs/api-services/reference/access-control-policy#cidr

Yep, we were going to use the patterns and also the masks when we can, but don't want to count on the possibility of the ips being in blocks--and not having ips within those block which we don't want to allow

Thanks @mukundha@apigee.com

@Sean - One thing I suggest here is to re-visit this consumption of MGMT API in your runtime as you don't want to end up slowing down your runtime to take care of such policy updates.

If there is a way to get this IP list out side your runtime path then good to push the policy update.

@Srini, thanks. I should have mentioned that we would just be loading this occasionally--it wouldn't be part of the usual flow(s). Our use case was just to be able to make edits/additions without having to change code or redeploy. What has been provided here should be able to handle our requirements.

I think my request for the management api call above may be incorrect. This is the cUrl command I am currently using:

{code}

curl "https://api.enterprise.apigee.com/v1/o/{our org}/apis/{api name}/revisions/{revision number}/policies/{policy name}" -H "Content-Type: application/octet-stream" -X PUT --data-binary '{policy xml}' -u userid:password

{code}

the only documentation I could find was associated with the POST to add the policy:

http://apigee.com/docs/management/apis/post/organizations/%7Borg_name%7D/resourcefiles

Hi Sean, the content-type need to be either application/xml or application/json and post body would be the policy xml or json

@mukundha@apigee.com is that mgmt api call atomic, i.e. is it possible for this call to leave the proxy in a broken state?

Former Community Member
Not applicable

Interested in knowing the same thing as well. @mukundha@apigee.com how do you envision the call to the management API, is that via a ServiceCallout policy in the same API proxy? I am concerned about circular dependencies.

I don think this should be called within the proxy,

Ideally there is a risk model built based on analytics or lot of other sources of information, when this model detects a IP to be blocked, [the ACT part], it will invoke this API.

But depends on the usecase, you could technically do it the service callout - or probably its a completely different proxy [maybe a nodejs script running in apigee]

yes, I think its atomic, this is basically zero-downtime deployment behind the scenes - @Srini Chebrolu, can you pls confirm?

Yes it is atomic and taken care by the system.

thanks for the confirmation @Srini Chebrolu

Thanks, guys. I'll add this to the list to include the API reference docs.

All - Apologies for the delay on this. I've created the following SmartDocs topics Unfortunately, the API for update of a policy definition is still under internal review and and should currently be considered deprecated. While you can delete policy using the update/get URI, I didn't include it in SmartDocs because that could be a little dangerous. Deleting policies, IMO, should be more deliberate. There's no "Are you sure?" involved with the API policy delete, and if the policy was being used in a deployed proxy, that would wreak havoc. Thanks.

Not applicable

IP white listing is possible with just Developer App custom attributes with out using Access Control Policy.

define two custom attributes "Allow" and "Deny" (if plan to have IP's blocked).

On the proxy after verifying the token(which resolves the run time api call to an Developer App), retrieve these two variable like"app.Allow" and "app.Deny" in Java Script Policy and compare with "proxy.client.ip".

An example JS code is below.. It is not thoroughly tested but gives a basic idea how to implement it.

Deny and Allow will support pipe delimited IP's with CIDR notation.

var custom_whitelist = context.getVariable("app.Allow");
var custom_blocklist = context.getVariable("app.Deny");
if (custom_whitelist && custom_whitelist != "*" && custom_whitelist != "" && custom_whitelist != null) {
    var ranges = convertAsRangesArray(custom_whitelist);
    var ip = context.getVariable("proxy.client.ip");
    var found = ipIsInNet(ip, ranges);
    if (!found) {
        context.setVariable("invalidIP","TRUE");
    }
}
if (custom_blocklist && custom_blocklist != "*" && custom_blocklist != "" && custom_blocklist != null) {
    var ranges = convertAsRangesArray(custom_blocklist);
    var ip = context.getVariable("proxy.client.ip");
    var found = ipIsInNet(ip, ranges);
    if (found) {
        context.setVariable("invalidIP","TRUE");
    }
}
function ipIsInNet(ipaddr, ranges) {
    var found = false;
    if (ipaddr != null) {
        for(inx = 0; inx < ranges.length; inx++) {
            if (isInRange(ipaddr, ranges[inx][0], ranges[inx][1])) {
                found = true;
             break;
            }
        }
    }
    return found;
}
function isInRange(ip, start, end){
var ipInt = dot2num(ip);
    var startInt = dot2num(start);
    var endInt = dot2num(end);
  if( ipInt >= startInt && ipInt <= endInt)
      return true;
    else
      return false;
}
function convertAsRangesArray(rangesString) {
    var ranges = rangesString.split("|");
  var rangeArray = new Array(ranges.length);
  for(var i=0; i< ranges.length; i++){
      //check if the IP is in cidr notation.
      if(ranges[i].indexOf('/') != -1 ){
        rangeArray[i] = getCIDRRange(ranges[i]);
      } else {
      rangeArray[i] = new Array(2);
      rangeArray[i][0] = ranges[i];
      rangeArray[i][1] = ranges[i];
      }
  }
    return rangeArray;
}
function getCIDRRange(cidr){
 var range = [2];
    cidr = cidr.split('/');
    var start = dot2num(cidr[0]);
    range[0] = num2dot(start);
    range[1] = num2dot(Math.pow(2, 32 - cidr[1]) + start - 1);
    return range;
}
function dot2num(dot) 
{
    var d = dot.split('.');
    return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
}
function num2dot(num) 
{
    var d = num%256;
    for (var i = 3; i > 0; i--) 
    { 
        num = Math.floor(num/256);
        d = num%256 + '.' + d;
    }
    return d;
}