Advanced API Gateway: How to?
In this section, we will see how to configure the API Gateway in advanced mode to achieve specific goals.
To change all the advanced settings, you have to access to the Console and go to the Advanced Section from the menu in the main sidebar of the Design Area.
What is a Map?
The Map concept will often be mentioned on this page. In this context, we refer to the Nginx Map Module which allows you to compare the values of an Nginx variable with some conditions and assign it a new value depending on the match. In other words, it's a map between two values: A and B. The value of B will be set based on the value of A.
As first variable you can specify a simple string:
Map a Capital City to his Country.
"Rome" "Italy"
"Brussels" "Belgium"
"Copenhagen" "Denmark"
The first argument can be also a regular expression. To do so you need to prefix the expression with a ~
Map a string to UpperCase or LowerCase
"~^[A-Z]+$" "UpperCase"
"~^[a-z]+$" "LowerCase"
How to proxy a request through a service
Request from the frontend - port 8080
The maps-proxyName configuration map allows you to forward your request to a service, based on the sender user and the request done.
Action:
- Go to Section:
api-gateway/maps-proxyName.before.map
orapi-gateway/maps-proxyName.after.map
- Write here your map
The syntax is:
$secret_resolution-$is_allowed-$original_request_method-$original_request_uri $proxy_name
The variables are:
$secret_resolution
: it checks if there is aclient-key
associated to the request. Values: secreted or unsecreted$is_allowed
: 1 if the user has the required permission to access the resource (based on the nginx auth_request), 0 otherwise$original_request_method
: request method. Allowed values are: GET, DELETE, POST, PATH, PUT or \w+ to enable all methods$original_request_uri
: request uri prefix$proxy_name
: destination service hostname
Request from the Backoffice (CMS) - port 8081
Action:
- Go to Section:
api-gateway/maps-proxyBackofficeName.before.map
orapi-gateway/maps-proxyBackofficeName.after.map
- Write here your map
The syntax is:
$secret_resolution-$is_allowed-$request_method-$request_uri $proxy_backoffice_name
The variables are:
$secret_resolution
: it checks if there is aclient-key
associated to the request. Values: secreted or unsecreted$is_allowed
: 1 if the user has the required permission to access the resource (based on the nginx auth_request), 0 otherwise$original_request_method
: request method. Allowed values are: GET, DELETE, POST, PATH, PUT or \w+ to enable all methods$original_request_uri
: request uri prefix$proxy_name
: destination service hostname
If no request is matched by any regular expression, the default request is forwarded to not_found
.
I want that if it's done a GET
request to /login-site
with or without client-key (secreted|unsecreted)
from an authorized or not authorized user (0|1)
, this request is forwarded to the demo-login-site
service.
"~^(secreted|unsecreted)-(0|1)-GET-/login-site" "demo-login-site";
I want that if it's done a POST
request to /foo/bar
with an associated client-key from an authorized user 1
, it's forwarded to foo-bar-manager
.
"~^secreted-1-POST-/foo/bar" "foo-bar-manager";
How to forward a request to another url
Request from the Frontend (port: 8080)
maps-proxyUrl
It's a map between a request and the destination url.
Action:
- Go to Section:
api-gateway/maps-proxyUrl.before.map
orapi-gateway/maps-proxyUrl.after.map
- Write here your map
The syntax is:
$request_method-$request_uri $proxy_url
The variables are:
$request_method
: It's the request method. Values: GET, DELETE, POST, PATH, PUT$request_uri
: It's the request uri$proxy_url
: It's the url where you want to proxy through
Request from the Backoffice (CMS) (port: 8081)
maps-proxyBackofficeUrl
The syntax is:
$request_method-$request_uri $proxy_backoffice_url
Action:
- Go to Section:
api-gateway/maps-proxyBackofficeUrl.before.map
orapi-gateway/maps-proxyBackofficeUrl.after.map
- Write here your map
The variables are:
$request_method
: It's the request method. Values: GET, DELETE, POST, PATH, PUT$request_uri
: It's the request uri$proxy_backoffice_url
: It's the url where you want to proxy through
You want to forward all the GET requests /files/images/avatar/foo/ to /user/foo/avatar/
"~^GET-/files/images/avatar/(?<username>[\w]+)/$" "/user/$username/avatar/"
If no request is matched by any regular expression, the default proxy_url is set to request_uri (the request is forwarded to itself).
How to edit which map include
All these files will be included in maps.conf
file as default.
You can edit which of them include editing this file.
Action:
- Go to Section:
api-gateway/maps.conf
- Write here your include
What you write here will overwrite the default maps.conf file.
Include maps-proxyName and maps-groupExpression
include /etc/nginx/customization.d/maps-proxyName.conf;
include /etc/nginx/customization.d/maps-groupExpression.conf;
How to add a custom location
You can add a custom Nginx location:
- From the Advanced Configuration go to
api-gateway/server-extension.conf
- Write here your location
Example
location ~* /(process|create)|endpoint)/? {
proxy_pass http://auth.example.com;
}
How to proxy Web Socket connections
You can proxy Web Socket connections to a service adding a specific location:
- From the Advanced Configuration go to
api-gateway/server-extension.conf
- Write here your location:
location /api/websocket/ {
proxy_pass http://$proxy_name$proxy_url;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
}
The variables are:
$proxy_name
: destination service hostname$proxy_url
: the url where you want to proxy through$remote_addr
: the client address$host
: in this order of precedence, host name from the request line, or host name from the Host request header field, or the server name matching a request$proxy_add_x_forwarded_for
: the X-Forwarded-For client request header field with the$remote_addr
variable appended to it, separated by a comma$http_upgrade
: the upgrade client request header
How to manage authorization in a multi gateway architecture
Sometimes projects can get big and, for this reason, it's better do segregate different macro features inside different projects, each one with its own set of resources.
In these cases it may be useful to have a single "gateway project" that handles requests coming from API consumers and dispatches them to the right api-gateway
of other projects (let's call these "functional projects"). This configuration eases the API management, as all the endpoints that are exposed, for example, on the internet, are configured in a single project: the gateway project.
Another advantage of such a configuration is that the authorization process can be centralized at the gateway level, freeing all the sub projects from the burden of managing client authentication and authorization.
In this section we described how the authorization flow works for a single project. In few words, in order to activate the authorization flow for a project you need to install the Authorization Service.
The Authorization Service is the service that resolves if the caller is authorized to invoke a certain endpoint and defines a set of platform headers to inform the rest of the architecture about the authorization of the client.
For instance, the Authorization Service sets these headers in the response that it returns to the API Gateway:
Mia-Allowed
Mia-Groups
Mia-Userid
These headers are attached to the response that the API Gateway receives from the Authorization Service, and contain information about the logged user/client.
If authorization passes, the Api Gateway proxies the request to the designated client. In the context of a multi gateway architecture, where the first API Gateway is part of the gateway project, the client that will receive the request is the API Gateway of another project.
Normally, an API Gateway expects the incoming request to be from a client, not another API Gateway, and it doesn't consider incoming platform headers, as the client would be able to self-authorize or impersonate another user.
For this reason, if you want all the auth information passing through the second API Gateway, without having to setup another Authorization Service at the functional project level, you must inform the API Gateway, at the functional project level, about the legitimacy of the incoming platform headers.
In order to do so, an advanced configuration of the API Gateway is required. Here are described the steps to set such an advanced configuration:
- Open the Console and go the Design section of the functional project of interest.
- Go to the Advanced mode
- Open the advanced configuration file named
auth-usage.conf
under the sectionapi-gateway
, and write this piece of configuration
set $mia_userid $http_miauserid;
set $mia_groups $http_miagroups;
set $mia_allowed "1";
set $mia_userproperties $http_miauserproperties;
- Save and deploy
It's worth underlying the importance of the line set $mia_allowed "1";
, which causes the API Gateway to implicitly authorize all the incoming calls.
This could be quite of a problem if the API Gateway is directly exposed to the clients (through the Internet or an internal network).
For this reason, in a multi-gateway architecture, it is important to ensure that all the authorization phase, if necessary, is resolved at the gateway project level.