Kong Dynamic User Routing Plugin
This routing capability enables companies to deploy the Curity Identity Server to multiple regions, while forwarding OAuth requests for users to their home region. The overall solution is described in the Implementing Dynamic User Routing walkthrough.
The default docker image has been customized to copy in the following two plugins:
|Zone Transfer||Plugin created in this how-to, which implements custom routing logic|
|lua-resty-jwt||Used by the plugin to parse JWTs in LUA|
FROM kong:2.4.1-alpine USER root RUN luarocks install lua-resty-jwt COPY reverse-proxy/kong/zone-transfer-plugin/* /usr/local/share/lua/5.1/kong/plugins/zone-transfer/ USER kong
The example uses the DB-less and Declarative Configuration which provides the simplest local deployment options. The Kong proxy routes are provided by a
kong.yml file, which would be different for each stage of a company's deployment pipeline.
_format_version: '2.1' _transform: true services: - name: curity url: http://internal-curity-eu:8443 routes: - name: default paths: - / plugins: - name: zone-transfer config: eu_host: internal-curity-eu us_host: internal-curity-us cookie_name: zone claim_name: zone
The plugin comes into effect for all paths and supplies values for host names to the plugin's logic. The host name in the URL field will then be overridden at runtime by the plugin's calculated value.
The code for the plugin is easy to follow and simply involves reading HTTP request values. The various responsibilities are broken down into functions in a standard way.
-- -- Try to read the zone value from an OAuth request -- function _M.run(config) local method = string.lower(ngx.var.request_method) if method == 'options' or method == 'head' then return nil end -- First try to find a value in the zone cookie local zone = get_zone_from_cookie(config.cookie_name) -- Otherwise, for POST messages look in the form body if zone == nil and method == 'post' then zone = get_zone_from_form(config.claim_name) end -- Update the host name if a zone is found if zone == 'eu' then ngx.ctx.balancer_address.host = config.eu_host elseif zone == 'us' then ngx.ctx.balancer_address.host = config.us_host end end
In the repository's Docker Compose file, the overall deployment of the Kong reverse proxy is defined, which includes copying in the environment specific
kong.yml file. In the example setup Kong runs on port 8080 and is exposed to the host PC at port 80:
kong: image: custom_kong:2.4.1 hostname: internal-kong ports: - 80:8080 volumes: - ./reverse-proxy/kong/kong.yml:/usr/local/kong/declarative/kong.yml environment: KONG_DATABASE: 'off' KONG_DECLARATIVE_CONFIG: '/usr/local/kong/declarative/kong.yml' KONG_PROXY_LISTEN: '0.0.0.0:8080' KONG_ADMIN_LISTEN: '0.0.0.0:8081' KONG_LOG_LEVEL: 'info' KONG_PLUGINS: 'bundled,zone-transfer'
When the reverse proxy is run as part of an overall solution, Kong outputs log messages to show zones received in OAuth requests, so that the plugin's behavior can be visualized:
*** Found zone 'eu' in cookie, client: 172.19.0.1, server: , request: "POST /authn/authentication/UserName-Password HTTP/1.1" *** Found zone 'eu' in wrapped token, client: 172.19.0.1, server: , request: "POST /oauth/v2/oauth-token HTTP/1.1" *** Found zone 'eu' in cookie, client: 172.19.0.1, server: , request: "POST /authn/authentication/UserName HTTP/1.1" *** Found zone 'us' in cookie, client: 172.19.0.1, server: , request: "POST /authn/authentication/UserName-Password HTTP/1.1" *** Found zone 'us' in wrapped token, client: 172.19.0.1, server: , request: "POST /oauth/v2/oauth-token HTTP/1.1"
It is a standard job to implement dynamic routing in API gateways, and this is straightforward using Kong's extensibility features. When combined with the Curity Identity Server's multi zone features, a company can deploy a global IAM system with user data separated by region, and with good reliability.