Home

Awesome

api-gateway-request-tracking

Usage and Tracking Handler for the API Gateway.

Table of Contents

Status

This module is under active development and is considered production ready.

Dependencies

This library requires an nginx build with OpenSSL, the ngx_lua module, LuaJIT 2.0, api-gateway-zmq-logger, api-gateway-request-validation, and api-gateway-hmac module.

Design considerations

This module is usually used for enforcing rate limiting and throttling policies. It is by design that business rules for the policies are not implemented in this module. A separate microservice should obtain usage information in order to track the requests and then notify the gateways when an action needs to be taken.

Bellow are the design principles used by this module:

Back to TOC

Definitions

Tracking Domain

A Tracking Domain is a formula involving a group of variables and their associated values. It represents an identifier used to match requests that require a special action ( track, block, delay, rewrite).

The table bellow provides some examples of tracking domains:

VariableExpected ValueUsed for tracking requests ...
$service_idservice_1... hitting the service_1 service.
$app_namemy_application... generated by my_application calling any service.
$service_id;$app_nameservice_1;my_application... generated by my_application but limited to the service_1 service.
$service_id;$request_methodservice_1;POST... hitting the service_1 service using the POST http method.

Pretty much any NGINX variable or user defined variable can be used to create the domain for tracking requests. This includes HTTP headers, query parameters, or URI parts.

The Expected Value can also be a wildcard (*) besides a static value. This is useful to track any value for a given variable.

Tracking Rules

A Tracking Rule is a tuple created from a tracking domain, an expiration time, and an action. For example to block all requests having the header x-api-key:1234 the following rule can be added:

{
  "id": 777,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "BLOCK"
}

This module implements the following actions:

TRACK

This action is useful to log usage information. This info is pushed into a message queue. The default implementation uses ZMQ. As mentioned above this module relies on a microservice to use this data and decide which action to be taken next.

In the example bellow the following Tracking Rule logs all requests containing a header named X-Api-Key with a value of 1234:

{
  "id": 777,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "TRACK"
}

It's also possible to specify a wildcard for the http_x_api_key variable in order to precisely capture all its possible values:

{
  "id": 777,
  "domain" : "example.com;*",
  "format": "$host;$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "TRACK"
}

In this case the Gateway logs the value of the $host along with the value of the X-Api-Key header. This information can potentially be used to enforce a different quota per individual api-keys.

BLOCK

This action blocks requests with a 429 HTTP response code until the rule expires.

{
  "id": 10,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "BLOCK"
}

DELAY

This action slows down requests by delaying them. The current implementation uses a random number between the specified delay and delay/2. For example for a delay of 5 the incoming requests may be delayed with 2.5 seconds to 5 seconds. Delaying strategy works great for short spikes in traffic. It is best to start delaying requests before blocking them.

The following rule delays requests with the header X-Api-Key:1234 for max 5 seconds:

{
  "id": 10,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "DELAY",
  "data": 5
}

The data field is used to specify the delay.

REWRITE

This action enables a per request routing override. It can be used for blue-green deployments or canary traffic. The following rule enables a rewrite for a specific X-Api-Key header:

{
  "id": 200,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1583910454,
  "action" : "REWRITE",
  "meta": "canary.example.com"
}

Back to TOC

Sample usage

# initial setup 
# ...
 init_worker_by_lua_block {
        -- initialize a ZMQ Logger 
        local ZmqLogger = require "api-gateway.zmq.ZeroMQLogger"
        local zmqLogger = ZmqLogger:new()
        zmqLogger:connect(ZmqLogger.SOCKET_TYPE.ZMQ_PUB, "ipc:///tmp/nginx_queue_listen")
            
        ngx.apiGateway = ngx.apiGateway or {}
        ngx.apiGateway.zmqLogger = zmqLogger                                  
        ngx.apiGateway.validation = require "api-gateway.validation.factory"  -- enforce ACTIONs
        ngx.apiGateway.tracking = require "api-gateway.tracking.factory"      -- track requests
     }
 # dictionaries used for storing the throttling / rate limiting rules
 lua_shared_dict tracking_rules_dict 5m;
 lua_shared_dict blocking_rules_dict 5m;
 lua_shared_dict delaying_rules_dict 5m;
 lua_shared_dict rewriting_rules_dict 5m;    
# ... 

#
# default validator for service plans that looks for Blocking rules to see if they match the request
#
location /validate_service_plan {
    internal;
    content_by_lua_block  { 
        ngx.apiGateway.tracking.validateServicePlan()
    }
}    

location /t {
    set $service_plan_validator "on; path=/validate_service_plan; ";                                            
    access_by_lua_block {
        ngx.apiGateway.validation.validateRequest()          -- enforce ACTIONS
    }
    ...
    log_by_lua_block {
           ngx.apiGateway.tracking.track()                   --  track requests
    }
}

Expose a simple HTTP Service to register rules and list the active ones:

server {
    listen 5000;
    server_name $hostname;
    
    include tracking_service.conf;
}

See tracking_service.conf that details the HTTP endpoints.

The following request adds a new blocking rule (expiring on 7/1/2016):

curl -i -X POST http://<docker_host_ip>:5000/tracking \ 
    --data '{  "id": 10,  "domain" : "1234",  "format": "$http_x_api_key",  "expire_at_utc": 1467331200000,  "action" : "BLOCK"}'
    
{"result":"success"}

To check which rules are active:

curl http://<docker_host_ip>:5000/tracking/block

[{"domain":"1234","format":"$http_x_api_key","id":10,"action":"BLOCK","expire_at_utc":1467331200000}]

Back to TOC

Developer guide

Running the tests

 make test-docker

Test files are located in test/perl folder and are based on the test-nginx library. This library is added as a git submodule under test/resources/test-nginx/ folder, from https://github.com/agentzh/test-nginx.

The other libraries such as Redis, test-nginx would be located in test/resources/. Other files used when running the test are also located in test/resources.

If you want to run a single test edit docker-compose.yml and replace in entrypoint /tmp/perl with the actual path to the test ( i.e. /tmp/perl/my_test.t)

The complete entrypoint config would look like:

 entrypoint: ["prove", "-I", "/usr/local/test-nginx-0.24/lib", "-I", "/usr/local/test-nginx-0.24/inc", "-r", "/tmp/perl/my_test.t"]

This will only run my_test.t test file.

Running the tests with a native binary

The Makefile also exposes a way to run the tests using a native binary:

 make test

This is intended to be used when the native binary is present and available on $PATH.

Back to TOC