The WSO2 API Microgateway is a Cloud Native API Gateway which can be used to expose one or many microservices as APIs.
- Why WSO2 API Microgateway
- Microgateway quick start
- Features
- Microgateway Components
- Architecture
- Running the microgateway
- WSO2 API Microgateway commands
- Project Structure
- How to run the microgateway distribution
- Invoke API exposed via microgateway
- Microgateway supported open API extensions
- Microgateway open API extension usages
- 1. Override endpoint per API resource
- 2. Add API/resource level request and response interceptors
- 3. Add API/resource level throttling policies
- 4. Add API level CORS configuration
- 5. Define backend security parameters
- 6. Override backend service connection URLS
- 7. Disable security for resources
- 8. Override API Authorization Header
- Microgateway securing APIs
- Import APIs from WSO2 API Manager
Microservices have become the norm for modern application architecture. Workloads of modern applications are spread across many groups of microservices, cloud services and legacy services. The characteristics and behaviors of such heterogeneous services have a massive diversity. Such as authentication mechanisms, message formats, high availability factors and so on. The WSO2 API Microgateway is designed to expose heterogeneous microservices as APIs to end consumers using a common API interface based on the Open API Specification. This helps expose microservices using a unified interface to external consumers, internal consumers and partners. It applies the common quality of service attributes on API requests such as security, rate limiting and analytics and also offers a wide range of features which helps organizations to deploy APIs microservice architectures efficiently.
Let's host our first API on a Microgateway. We will be exposing the publicly available petstore services via microgateway
-
First download the microgateway toolkit related to latest release from the product official page or github release page and extract it to a folder of your choice.
-
Using your command line client tool add the 'bin' directory of the extracted folder to your PATH variable.
export PATH=$PATH:<TOOLKIT_EXTRACTED_LOCATION>/bin
- We are now ready to execute the Microgateway toolkit commands to initialize and build our Microgateway. Let's create our first project with name "petstore" by adding the open API definition of the petstore . You can do that by executing the following command using your command line tool.
micro-gw init petstore -a https://petstore.swagger.io/v2/swagger.json
-
The project is now initialized. You should notice a directory with name "petstore" being created in the location where you executed the command.
-
Next, Lets build the project and create a microgateway docker image.
micro-gw build petstore --docker-image petstore:v1 --docker-base-image wso2/wso2micro-gw:3.1.0
Once the build is successful microgateway docker image will be created with name "petstore:v1"
- Now gateway can be started using the built docker image Execute the command below to run the Microgateway for our Petstore project.
docker run -d -p 9090:9090 -p 9095:9095 petstore:v1
The above will expose an https endpoint on port 9095. The context of the API will be "/v2".
- The next step would be to invoke the API using a REST tool. Since APIs on the Microgateway are by default secured. We need a valid token or key in order to invoke the API. Microgateway can issue API keys on its own. Execute the command below to get a API key from microgateway. This will set the api key into TOKEN variable.
TOKEN=$(curl -X get "https://localhost:9095/apikey" -H "Authorization:Basic YWRtaW46YWRtaW4=" -k)
- We can now invoke the API running on the microgateway using cURL as below.
curl -X GET "https://localhost:9095/v2/pet/1" -H "accept: application/json" -H "api_key:$TOKEN" -k
Here is a short summary of the features it hosts.
- Exposing one or more microservices as APIs using the Open API Specification.
- Authentication and Authorization of API requests based on OAuth2.0 (opaque tokens and JWTs), Basic Auth and Mutual TLS.
- Rate Limiting of API requests based on numerous policies.
- Business Insights through API Analytics.
- Service discovery features.
- Request and Response transformations.
- Load balancing, failover and circuit breaking capabilities of API requests.
- Seamless integration with Docker and Kubernetes.
- Integration with WSO2 API Manager to support design first APIs, API Analytics and shared rate limiting.
- Grouping APIs by labels.
It also has the following characteristics that makes it a perfect fit for microservice architectures
- Less than 1s startup time, allowing for faster scaling.
- Built on a stateless architecture, allowing for infinite scaling.
- Has an immutable runtime, making it heavily robust.
- Easy integration with CI/CD processes and tools.
- Runs in isolation with no dependencies to other components
-
Toolkit : The toolkit is used to initiate microgateway projects. Once the project is initialized API developer can add(copy) open API definitions of the APIs to the project or import APIs from WSO2 API Manager. Once all the APIs are added the toolkit can be used to build the project and create and executable file.
-
Runtime : The gateway run time can expose the APIS and serves the API requests. The executable output of the toolkit should be provided as an input when running the microgateway runtime. Then this run time will expose all the APIs which were included in the particular project which used to create the executable file
The following diagram illustrates how the WSO2 API Microgateway expose micro services using Open API definition as well as importing APIs from WSO2 API Manager.
- API developer creates a WSO2 API Microgateway project using a WSO2 API Microgateway controller(toolkit)
- Adds the open API definitions of microservices into the project
- Developer defines endpoints and interceptors for the api/resources using the definition.yaml inside the project
- Builds the project and generates executables, images and k8s artifacts
Running the WSO2 API Microgateway is a 3 step process. The first two steps are involved in building the executable using the toolkit and the last step is to run that executable file using the microgateway runtime component.
- Initializing a WSO2 API Microgateway project.
- Building the WSO2 API Microgateway project and creating an executable file
- Running the WSO2 API Microgateway distribution.
Initializing a WSO2 API Microgateway project creates the default directory structure at the location where the command is run.
Empty api_definitions
directory will be created inside the root project directory. API developer can add multiple open API definitions inside the
api_definitions file and define endpoints and interceptors for the resources by adding open API extensions.
API developer can specify the back end endpoint details, request and response interceptors, throttle policies, CORS config and etc using open API
vendor specific extensions.
Once the project has been created, the next step is to build the project sources. This output of this operation is a executable file(.balx) which later provided as an input to the runtime
The output(.balx file) of toolkit build process is used to run the microgateway runtime component.
Following are the set of commands included within the WSO2 API Microgateway.
Note: Before you execute any of the commands below you need to add the path to the /bin directory to the PATH environment variable. Ex: /home/dev/wso2am-micro-gw/bin
$ micro-gw init <project_name>
The "micro-gw init" command is used to initialize a project structure with artifacts required in generating a WSO2 API Microgateway distribution. This will create a api_definitions directory.
- api_defintions - API developer should copy all the open API definitions of microservices inside this directory
If the project already exists, a warning will be prompted requesting permission to override existing project.
Execute micro-gw help init
to get more detailed information regarding the setup command.
Example
$ micro-gw init petstore
Let's see how we can expose the petstore swagger using the micro-gw.
Let's add the basic microgateway Open API extension to the petstore OAS file.
x-wso2-basePath: /petstore/v1
x-wso2-production-endpoints:
urls:
- https://petstore.swagger.io/v2
Sample for petstore OAS file with two resources and extensions can be found here
$ micro-gw build <project_name>
Upon execution of this command, the WSO2 API Microgateway CLI tool will build the executable file for the specified project.
Execute micro-gw help build
to get more detailed information regarding the build command.
Example
$ micro-gw build petstore
Following is the structure of a project generated when running micro-gw init command.
petstore/
├── api_definitions
├── conf
│ └── deployment-config.toml
├── gen
│ ├── api_definitions
├── interceptors
├── policies.yaml
└── target
Once the init, build commands are executed, an executable file with extension .balx will be created under target directory inside the project.
../petstore/target$ ls
petstore.balx
Then use the microgateway runtime component to run this executable file.
- Go to the <MG_RUNTIME_HOME>/bin directory and execute the following command
$ bash gateway <path_to_the_excutable_file>
micro-gw-internal/bin$ bash gateway /home/user/petstore/target/petstore.balx
ballerina: initiating service(s) in '/home/user/petstore/target/petstore.balx'
ballerina: started HTTPS/WSS endpoint localhost:9095
ballerina: started HTTP/WS endpoint localhost:9090
ballerina: started HTTPS/WSS endpoint localhost:9096
Once APIs are exposed we can invoke API with a valid jwt token or an opaque access token. In order to use jwt tokens microgateway should be presented with a jwt signed by a trusted OAuth2 service. There are few ways we can get a jwt token
-
Any third party secure token service The public certificate of the token service which used to sign the token should be added to the trust store of the microgateway. The jwt should have the claims sub, aud, exp in order to validate with microgateway
-
Get jwt from WSO2 API Manager Please refer the documentation on how to get a valid jwt
The following sample command can be used to invoke the "/pet/findByStatus" resource of the petstore API
curl -X GET "https://localhost:9095/petstore/v1/pet/findByStatus?status=available" -H "accept: application/xml" -H "Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UQXhabU14TkRNeVpEZzNNVFUxWkdNME16RXpPREpoWldJNE5ETmxaRFUxT0dGa05qRmlNUSJ9.eyJhdWQiOiJodHRwOlwvXC9vcmcud3NvMi5hcGltZ3RcL2dhdGV3YXkiLCJzdWIiOiJhZG1pbiIsImFwcGxpY2F0aW9uIjp7ImlkIjoyLCJuYW1lIjoiSldUX0FQUCIsInRpZXIiOiJVbmxpbWl0ZWQiLCJvd25lciI6ImFkbWluIn0sInNjb3BlIjoiYW1fYXBwbGljYXRpb25fc2NvcGUgZGVmYXVsdCIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTQ0M1wvb2F1dGgyXC90b2tlbiIsImtleXR5cGUiOiJQUk9EVUNUSU9OIiwic3Vic2NyaWJlZEFQSXMiOltdLCJjb25zdW1lcktleSI6Ilg5TGJ1bm9oODNLcDhLUFAxbFNfcXF5QnRjY2EiLCJleHAiOjM3MDMzOTIzNTMsImlhdCI6MTU1NTkwODcwNjk2MSwianRpIjoiMjI0MTMxYzQtM2Q2MS00MjZkLTgyNzktOWYyYzg5MWI4MmEzIn0=.b_0E0ohoWpmX5C-M1fSYTkT9X4FN--_n7-bEdhC3YoEEk6v8So6gVsTe3gxC0VjdkwVyNPSFX6FFvJavsUvzTkq528mserS3ch-TFLYiquuzeaKAPrnsFMh0Hop6CFMOOiYGInWKSKPgI-VOBtKb1pJLEa3HvIxT-69X9CyAkwajJVssmo0rvn95IJLoiNiqzH8r7PRRgV_iu305WAT3cymtejVWH9dhaXqENwu879EVNFF9udMRlG4l57qa2AaeyrEguAyVtibAsO0Hd-DFy5MW14S6XSkZsis8aHHYBlcBhpy2RqcP51xRog12zOb-WcROy6uvhuCsv-hje_41WQ==" -k
Please note that the jwt provided in the command is a jwt token retrieved from WSO2 API Manager with higher expiry time which can be used with any API not protected with scopes. This token works with any API since by default the microgateway config uses the public certificate of WSO2 API Manager to validate the signature.
Extension | Description | Required/Not Required |
---|---|---|
x-wso2-basePath | Base path which gateway exposes the API | Required -> API level only |
x-wso2-production-endpoints | Specify the actual back end of the service | Required -> API level/ Not required -> Resource level |
x-wso2-sandbox-endpoints | Specify the sandbox endpoint of the service if available | Not Required -> API/Resource level |
x-wso2-throttling-tier | Specify the rate limiting for the API or resource | Not Required -> API/Resource level |
x-wso2-cors | Specify CORS configuration for the API | Not Required -> API level only |
x-wso2-endpoints | Define endpoint configs globally which can be then referred with x-wso2-production-endpoints or x-wso2-sandbox-endpoints extensions | Not Required |
x-wso2-disable-security | Resource can be invoked without any authentication | Not Required -> Resource level only |
x-wso2-request-interceptor | Custom ballerina functions can be written in order to do transformations before dispatching the request | Not Required -> API/Resource level |
x-wso2-response-interceptor | Custom ballerina functions can be written in order to do transformations before dispatching the response | Not Required -> API/Resource level |
x-wso2-auth-header | Specify the authorization header for the API in which either bearer or basic token is sent | Not Required -> API level only |
x-wso2-transports | Specify the transport security for the API(http, https and mutual ssl) | Not Required -> API level only |
x-wso2-application-security | Specify application security (basic_auth, api_key, oauth2) | Not Required -> API/Resource level |
API developer can specify endpoints per resource by adding the x-wso2-production-endpoints extension under the respective resource in open API definition. If a specific resource have an endpoint which requires different back end rather than the global back end defined for the API, then it can be overridden as below.
In following example /pet/findByStatus
resource endpoint is overridden with load balance endpoint and pet/{petId}
resource overridden with another http endpoint
"/pet/findByStatus":
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
explode: true
schema:
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
x-wso2-production-endpoints:
urls:
- http://www.mocky.io/v2/5cd28cd73100008628339802
- https://petstore.swagger.io/v2
"/pet/{petId}":
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
schema:
type: integer
format: int64
x-wso2-production-endpoints:
urls:
- http://www.mocky.io/v2/5cd28b9a310000bf293397f9
Complete sample can be found here
Interceptors can be used to do request and response transformations and mediation. Request interceptors are engaged before sending the request to the back end and response interceptors are engaged before responding to the client. API developer can write his own request and response interceptors using ballerina and add it to the project and define them in the open API definition using extensions
In the sample below user can write the validateRequest and validateResponse methods in ballerina and add it to the interceptors
directory inside the project. This interceptor will only be engaged for that particular resource only
paths:
"/pet/findByStatus":
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
x-wso2-request-interceptor: validateRequest
x-wso2-response-interceptor: validateResponse
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
explode: true
schema:
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
Sample validateRequest method can be implemented as below.
import ballerina/io;
import ballerina/http;
public function validateRequest (http:Caller outboundEp, http:Request req) {
io:println("Request is intercepted.");
}
Sample validateResponse method can be implemented as below.
import ballerina/io;
import ballerina/http;
public function validateResponse (http:Caller outboundEp, http:Response res) {
io:println("Response is intercepted.");
}
Similarly the API developer can add interceptors globally at the API level as well
openapi: 3.0.0
version: 1.0.0
title: Swagger Petstore New
termsOfService: http://swagger.io/terms/
x-wso2-basePath: /petstore/v1
x-wso2-request-interceptor: validateRequest
x-wso2-response-interceptor: validateResponse
x-wso2-production-endpoints:
urls:
- https://petstore.swagger.io/v2
Sample open API definition for interceptors can be found here.
API developer can specify the rate limiting policies for each resource or globally for the API. These policies should be defined in the policies.yaml file in the project directory A default set of policies are already available, but users can add more policies to the file and later refer them by name in the open API definition Following samples show how throttling policies can be added to an API
x-wso2-basePath: /petstore/v1
x-wso2-throttling-tier: 10kPerMin
x-wso2-production-endpoints:
urls:
- https://petstore.swagger.io/v2
The throttle policy "10kPerMin" is defined in the policies.yaml of the project as below.
- 10kPerMin:
count: 10000
unitTime: 1
timeUnit: min
Resource level throttle policies also can be defined as well.
paths:
"/pet/findByStatus":
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
x-wso2-throttling-tier: 20kPerMin
Complete sample can be found here
CORS configurations can be added to each API using the open API extension x-wso2-cors
x-wso2-basePath: /petstore/v1
x-wso2-production-endpoints:
urls:
- https://petstore.swagger.io/v2
x-wso2-cors:
access-control-allow-origins:
- test.com
- example.com
access-control-allow-headers:
- Authorization
- Content-Type
access-control-allow-methods:
- GET
- PUT
- POST
Complete sample can be found here
There might be occasions where the actual back end service of the API might be protected using basic authentication. In those scenarios we need to send the basic authentication parameters(username and password) to the back end service. We can specify the endpoint security parameters in the openAPI definition using extensions. The supported way to define endpoint security is to define endpoints under the x-wso2-endpoints parameter and then refer them in the API level or resource level endpoint. When we define the endpoint under extension "x-wso2-endpoints" then endpoint should have a name. This name(myEndpoint in below sample) is used to pass the password when running the microgateway Under the endpoint config we can define security parameters as below
securityConfig:
type: basic
username: rajith
x-wso2-basePath: /petstore/v1
x-wso2-production-endpoints: "#/x-wso2-endpoints/myEndpoint"
paths:
"/pet/findByStatus":
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
x-wso2-production-endpoints: "#/x-wso2-endpoints/myEndpoint3"
.
.
.
.
x-wso2-endpoints:
- myEndpoint:
urls:
- https://petstore.swagger.io/v2
- https://petstore.swagger.io/v5
securityConfig:
type: basic
username: roshan
- myEndpoint3:
urls:
- https://petstore.swagger.io/v3
- https://petstore.swagger.io/v4
securityConfig:
type: basic
username: rajith
Complete sample can be found here
When running the microgateway we can provide the password as an environment variable. The variable format is <epName>_<epType>_basic_password
- epName : Name specified in the open API definition under x-wso2-endpoints
- epType : either prod or sand So the complete command for the above sample is like
bash gateway -e myEndpoint3_prod_basic_password=123456 <path_to_the_excutable_file>
There can be use cases where we want to override the back end connection url provided in the open API definition during the run time. We can override endpoints that are used as references similar to previous topic. In order to override the endpoint url we need to define endpoint in x-wso2-endpoints extension and refer them in the API level or resource level. Let's use the same example we have used in previous topic. So we can override the myEndpoint3 url during the runtime as follows. The variable format is <epName>_<epType>_endpoint_<epIndex>
- epName : Name specified in the open API definition under x-wso2-endpoints
- epType : either prod or sand
- epIndex : Index starting from 0. If there are many URLS(load balanced or fail over) we can override them using indexes 1,2,3 and etc So the complete command for the above sample is like
bash gateway -e myEndpoint3_prod_endpoint_0=<new back end url> <path_to_the_excutable_file>
By default the APIs and resources are protected via oauth2 in microgateway. API consumer need a valid oauth2 access token(jwt or opaque) to invoke the APIs. But API developer can expose APIs without any authentication using the open API extension x-wso2-disable-security. This extension is only supported at resource level only
paths:
"/pet/findByStatus":
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
x-wso2-disable-security: true
By default the APIs exposed via microgateway requires a valid token sent with "Authorization" header. By specifying the "x-wso2-auth-header" users can set custom header name for this.
x-wso2-auth-header: Authx
The gateway supports the "securitySchemes" keyword in open API specifications. Currently microgateway supports oauth2 and basic authentication for APIs which can be defined via open API extensions. If none of the securitySchemes are defined the gateway by default applies oauth2 security.
- Define scopes for the resources and API using oauth2 security type.
securityDefinitions:
petstore_auth:
type: oauth2
authorizationUrl: 'https://petstore.swagger.io/oauth/authorize'
flow: implicit
scopes:
'write:pets': modify pets in your account
'read:pets': read your pets
We can define oauth2 security type, and add scopes and refer the security scheme in API level or resource level
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
- Define basic authentication for the API
securityDefinitions:
petstore_basic:
type: basic
Complete sample can be found here
The published apis from WSO2 API Manager can be exposed via microgateway as well. We can import API from WSO2 API Manager by specifying the API name and version. The import command of the toolkit can be used to fetch APIs.
First initialize the project using the command below.
micro-gw init pizza-api
Then import the API. The toolkit will prompt for API manager url, username and password of a valid user in API manager, trust store location and password of toolkit. If url, trust store location and password is not provided default values will be used
micro-gw import -a <API-NAME> -v <API_VERSION> <PROJECT_NAME>
ex: micro-gw import -a PizzaShackAPI -v 1.0.0 pizza-api
$ micro-gw import -a PizzaShackAPI -v 1.0.0 pizza-api
Enter Username:
admin
Enter Password for admin:
Enter APIM base URL [https://localhost:9443]:
You are using REST version - v1.1 and dynamic client registration version - v0.16 of API Manager.
(If you want to change this, go to <MGW-TK_HOME>/conf/toolkit-config.toml)
Enter Trust store location: [lib/platform/bre/security/ballerinaTruststore.p12]
Enter Trust store password: [ use default? ]
ID for API PizzaShackAPI : 48776504-9479-48c0-abd2-711ea0263ac9
Once imported the auto generated swagger will be inside the gen directory of the project. Project structure will be as follows.
pizza-api/
├── api_definitions
├── conf
│ └── deployment-config.toml
├── extensions
│ ├── extension_filter.bal
│ └── token_revocation_extension.bal
├── gen
│ └── api_definitions
│ └── 30e623704c5c5479b7c0d9ab78e965df02c1610401e37cbd557e6353e3191c76swagger.json
├── interceptors
├── policies.yaml
└── target
└── gen
└── internal.conf
This project can then be built and run using the same approaches we have discussed above.