Securing APIs using sidecar pattern
We have an application exposing set of API and we want to make them secure. How do we approach this problem?
Solution
We essentially need to build an auth module to secure the APIs. Straight forward solution to this problem is to build an auth module with in the API app for access control.
But think about it again. Is there a better approach out there?
Going by true “single responsibility principle”, we probably don’t want to keep our auth logic with in the API app as authentication and authorisation are not the core functionality of the API application. We want to clearly separate out the auth logic from API logic.
In the micro-services world, a new approach is to use sidecar pattern. Idea here is to run your auth module in a sidecar container and let your application just focus on the API logic. All the API requests are routed through a sidecar proxy, which uses the auth module to do the access control and then route the requests to the API application only after successful authorisation.
For this example, we will use netflix-zuul as the reverse proxy. Spring cloud makes it easy to embed zuul proxy in your spring boot app. We have coded the auth module with in proxy itself for simplicity (but you have the choice to separate auth module in to a separate app of its own if you want). Auth module here does two things - it generates the access token for the clients and also does the token verification (access control) for the APIs (You can definitely move these into separate apps).
API application runs in a container and our reverse proxy runs as a sidecar in another container. API application ports are not exposed outside, so no one can directly access the APIs from outside. Request to the APIs are routed via the reverse proxy and it does the authorisation check. We use spring-security OAuth 2.0 based authorisation check in the proxy.
Here is the zuul configuration to route the api requests to API app. API app is running on port 8080 on the same machine (localhost/127.0.0.1).
zuul:
sensitiveHeaders: Cookie,Set-Cookie
routes:
api:
path: /api/**
url: http://127.0.0.1:8080/
Both API app and the proxy side car containers are deployed in a Kubernetes pod.
Here is the kubernetes deployment file
apiVersion: v1
kind: Namespace
metadata:
name: my-apps
---
apiVersion: v1
kind: Pod
metadata:
name: api
labels:
app: api
namespace: my-apps
spec:
containers:
- name: api
image: springio/api-app:latest
imagePullPolicy: Never
- name: proxy
image: springio/proxy:latest
imagePullPolicy: Never
ports:
- containerPort: 8081
If you want to see this in action on your local machine, do the below
Install minikube
Refer to https://kubernetes.io/docs/setup/learning-environment/minikube/#installation
start minikube
minikube start
Checkout the code
https://github.com/bibinss/sidecar
This has the API app under ‘api’ folder and proxy app under ‘proxy’ folder.
API app is a simple hello world spring boot app exposing only one ‘/hello’ API endpoint. We want to secure this endpoint using the proxy.
Proxy app is a spring boot application using netflix-zuul proxy.
Build docker image for api application
cd api
eval $(minikube docker-env)
mvn clean package
mvn dockerfile:build
Build docker image for proxy application
cd proxy
eval $(minikube docker-env)
mvn clean package
mvn dockerfile:build
Deploy the docker images in minikube
From the ‘sidecar’ folder run,
kubectl create -f sidecar-deploy.yml
Expose the proxy outside the pod via NodePort
kubectl expose pod api -n my-apps — type=NodePort — port 8081
Go to minikube console and ensure that application containers are deployed properly
minikube dashboard
Get the NodePort exposed IP and Port
minikube service api -n my-apps
This should open a browser window with Nodeport IP and Port
Get the access token
curl -X POST \
http://<IP>:<Port>/oauth/token \
-H ‘Authorization: Basic dGVzdC1jbGllbnQ6Y2xpZW50LXNlY3JldA==’ \
-d ‘grant_type=client_credentials’
dGVzdC1jbGllbnQ6Y2xpZW50LXNlY3JldA== is the base64 encoded client credentials configured in the Auth server (test-client:client-secret)
For example, below shows the how we receive an access token
Invoke the API using the access token
curl -X GET \
http://<IP>:<Port>/api/hello \
-H ‘Accept: application/json, text/plain, */*’ \
-H ‘Authorization: Bearer <Access token>’
Our API ‘api/hello’ returns a JSON response when invoked successfully. See it in action below for a successful invocation
Try to hit the same API without the access token or with a wrong token, you will get an Authorisation error.