The Api Gateway is the entry point of a microservice infrastructure (see api gateway pattern). The main problem it solves is "How do the clients of a Microservices-based application access the individual services?". It handles the first layer of authentication and routes the incoming requests (like a reverse proxy) to the corresponding service, based on the mapping in its service registry.
This is a young but very active project and absolutely needs your help. Good ways to contribute include:
- Raising bugs and feature requests
- Fixing bugs
- Improving the performance
- Adding to the documentation
- Sbt 0.13.*
$ sbt run
By default this will start the gateway interface on http://localhost:8080/api
and the management interface on http://localhost:8081
Most of the configurable options are available as environment variables. Below is a list with default values:
API_PREFIX=api
AUTH_HOST=localhost
AUTH_PORT=7070
K8S_API_HOST=localhost
K8S_API_PORT=8001
K8S_API_TOKEN=
For a Kubernetes configuration example see the sample descriptor file.
$ docker pull jeroenrosenberg/api-gateway
This project is using the Docker plugin of the Sbt Native Packager to generate a docker image using:
$ sbt docker:publishLocal
Then you can simply publish your docker image to your favorite docker repository. For instance for the Google Container Registry:
$ docker tag com.github.jeroenr/api-gateway eu.gcr.io/my-docker-registry/api-gateway
$ gcloud docker push eu.gcr.io/my-docker-registry/api-gateway
Since the Api gateway currently relies on the Kubernetes API for service discovery the first thing we would need is an access token. The easiest way to do this would be through service accounts. After that you need descriptor files for the Kubernetes deployment and service. Below is a quick walkthrough the steps. I assume you're familiar with Kubernetes' terminology and the kubectl commandline tool.
The steps below will create a service account and associated secret to be used by the Api gateway pod.
$ kubectl create serviceaccount my-service-account
serviceaccount "my-service-account" created
Great, now a new service account has been created and under the hood also an associated secret which we can retrieve by:
$ kubectl get serviceaccounts my-service-account -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
# ...
secrets:
- name: my-service-account-token-1yvwg
As you can see in our example the generated secret is called "my-service-account-token-1yvwg". Depending on where you pull your docker image from you might also need to create another secret with credentials for the docker registry to be used as an image pull secret.
Now we can setup our K8s deployment and service descriptor. Here's an example config. Important to configure is:
- The container image path (e.g. eu.gcr.io/my-docker-registry/api-gateway)
- The image pull secret for the container registry (e.g. my-docker-registry-secret)
- The secretKeyRef.name for the
K8S_API_TOKEN
env variable (e.g. my-service-account-token-1yvwg) - The AUTH_HOST and AUTH_PORT env variables to point to your backing authentication service
As a final step we can now create the k8s deployment and service:
$ kubectl apply -f k8s-descriptor.yaml
deployment "api-gateway" created
service "api-gateway-svc" created
Cool, now we're running the Api gateway in Kubernetes!
Currently the Api gateway relies on the services endpoint from the Kubernetes API to poll for service updates (see Automatic Service discovery section). If you want to deploy the Api gateway on a different platform you would have to:
- Mock this endpoint and manually update the service registry (see (#manually-updating-the-service-registry). You could also rely on something like Consul and write a sync script.
- Make a PR to add a flag in the configuration to disable automatic service discovery
- Make a PR to support different pluggable service discovery modules
- The k8s namespaces to watch for service updates. This can be configured through JVM params (e.g.
-Dintegration.kubernetes.namespaces.0=my-env
to only handle service updates on the 'my-env' namespace)
As mentioned the Api gateway is the first layer of the authentication flow. If a request is made to a secured route (i.e. a route which maps to a service with the flag "secured" set) it will request a JWT access token from an authentication service (e.g. https://github.com/cupenya/auth-service which retrieves a user claim based on a reference token in a domain cookie and generates a JWT token for this claim). If a valid JWT token is returned the call is forwarded to the corresponding service and the JWT token is passed in the request header as an Oauth bearer token for further authorization to be done by the backing service.
If no valid JWT token is returned the error response will be forwarded to the caller. This could happen when for instance the user session has expired. The user could then "login" again using the "/auth/login" endpoint as follows:
$ curl -X POST \
http://localhost:8080/auth/login \
-H 'cache-control: no-cache' \
-H 'content-type: application/json'
-d '{
"username": "my-user",
"password": "my-pass"
}'
This call is just forwarded to the authentication service.
Here's a Postman collection with all available Api calls
The Api gateway supports service discovery through the Kubernetes API using the k8s-svc-discovery module. It's polling the /api/v1/services
endpoint for service and updates the routing / mapping based on the service metadata.
There's also a dashboard Api which can be used to manually update the service registry. For instance to add a service:
$ curl -X POST \
http://localhost:8081/services \
-H 'cache-control: no-cache' \
-H 'content-type: application/json'
-d '{
"name": "my-user-service",
"host": "localhost",
"resource": "users",
"port": 9090,
"secured": false
}'
This will add a reverse proxy mapping from http://{api-gateway-host}:{api-gateway-port}/{api-gateway-prefix}/users
to http://localhost:9090/users
. If no 'port' is specified it will default to 80. The flag 'secured' determines whether the Api gateway performs authentication checks and passes on the authentication info to the corresponding service. See Authentication section for more information.