Forward requests to auth service within a Kubernetes microservices architecture

ALex Antonica
6 min readFeb 13, 2021

In your typical microservice architecture the main access point (and desirably the only one) to the inside of it should be the apigateway. So its job is to route the requests, as the first line of interaction between outside world and the inside, to the appropriate microservice based on different values that the request has. Some endpoints are private therefore they might require, let’s say, a specific token to be passed with the request (eg. JWT auhorization).

In this case, instead of computing (here I’m referring to decrypt the JWT signature) whether or not the caller is who they say they are, on every service that we have in our application, we delegate this job to a authorization/authentication service ( for the sake of the example let’s call this auth-service). So the flow would be like this.

  1. Request comes from user to our backend -> it reaches apigw
  2. Apigw sends it to auth-service
  3. The auth-service will do some logic on that request and it will return a response
  4. Based on that response, apigw will decide whether or not to route the request

Imagine we have a java application, composed mainly of other microservices that are java based.

TL; DR You need service discovery, service routing etc etc, which they can be done using spring cloud technologies like eureka, ribbon and so on. But all of these can be achieve with, you guessed it, Kubernetes. This is a topic for another article, but now I’m gonna show you how to do just that the request forwarding to auth-service.

FYI: I won’t be explaining how the deployments/services/ingresses work or explain all the fields inside the configuration files. I might mention a few that are important.

Prerequisites

Before we start, you’ll need:

  1. Docker
  2. You’ll need kubernetes installed on your machine. I’m gonna use the one provided by Docker.
  3. kubectl — kubernetes cli application
  4. An IDE — intellij/eclipse

1. Backend

For convenience I’ve created 2 backend services : auth-service and doc-service. You don’t need to be a Java developer, just to understand the REST Api concepts. Here is the github repo. So if you run these 2 services and interact with the endpoints you’ll get something like this

// Auth-service without TEST_HEADER
alexantonica$ curl localhost:9998/authorize/request
Can't touch this--------------// Auth-service with TEST_HEADER
aantonica$ curl-H "test-header: test" localhost:9998/authorize/request
Proceed milord---------------// Doc-serviceaantonica$ curl localhost:9999/doc/fetch{"test":"value"}

I’ve pushed these images to my docker hub repository so we can have them available for our kubernetes deployments.

2. Deployments

We’re gonna create 3 deployments. One for each of the services and one for the ingress controller.

Auth deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service-deployment
labels:
name: auth-service-deployment
spec:
replicas: 1
selector:
matchLabels:
name: auth-service
template:
metadata:
labels:
name: auth-service
spec:
containers:
- image: aantonica/forward-req:auth-service
name: auth-service
imagePullPolicy: Always
ports:
- containerPort: 9998
restartPolicy: Always

Doc Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: doc-service-deployment
labels:
name: doc-service-deployment
spec:
replicas: 1
selector:
matchLabels:
name: doc-service
template:
metadata:
labels:
name: doc-service
spec:
containers:
- image: aantonica/forward-req:doc-service
name: doc-service
imagePullPolicy: Always
ports:
- containerPort: 9999
restartPolicy: Always

They are pretty much the same, the only things that differ are the names/labels and the container exposed port.

Apply them

kubectl apply -f infrastructure/deployments/auth-deployment.yaml
kubectl apply -f infrastructure/deployments/doc-deployment.yaml

We’ll create the ingress controller deployment at the end.

Now that we have the pods in a running state. Let’s create the services.

3. Services

Auth Service

apiVersion: v1
kind: Service
metadata:
name: auth-service
spec:
type: NodePort
selector:
name: auth-service
ports:
- protocol: TCP
port: 9998
targetPort: 9998

Doc Service

apiVersion: v1
kind: Service
metadata:
name: doc-service
spec:
type: NodePort
selector:
name: doc-service
ports:
- protocol: TCP
port: 9999
targetPort: 9999

Apply them

kubectl apply -f infrastructure/services/auth-service.yaml
kubectl apply -f infrastructure/services/doc-service.yaml

You can quickly check if they’re working by running

kubectl get svc

Kubernetes services in default namespace

In the port column you’ll have <internal-port>:<external-exposed-port> , knowing this, testing is as easy as

3. Igresses

They are used to define the routings of the requests

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: doc-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/auth-url: http://auth-service.default.svc.cluster.local:9998/authorize/request
spec:
rules:
- http:
paths:
- path: /doc/fetch
pathType: Prefix
backend:
service:
name: doc-service
port:
number: 9999
  • kubernetes.io/ingress.class: nginx — This is used to determine which ingress controller should claim this ingress, in case you have multiple controllers. Read more here
  • nginx.ingress.kubernetes.io/auth-url — This is the part that interests us . This will determine where to first route the request in order to do the authentication/authorization (it depends how you use it). I guess you noticed that it’s not an ip, it’s actually a DNS entry. Well, kubernetes has an internal DNS and it can figure out which ip the host is mapped to, if you write it in a specific manner. <service-name>.<namespace>.<resource-type>.cluster.local
  • Then we define the endpoint path, this one should be self explanatory.

Now that we have the ingress, we need a controller for this. So let’s deploy the ingress controller.

4. Ingress Controller

The simplest way in which you can install this is by executing this

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.44.0/deploy/static/provider/cloud/deploy.yaml

This will create all the necessary resources that the ingress controller needs.

Once this is done, we’ll have to change one of the services that it was created because it’s of type LoadBalancer which we don’t need on our local machine.

For that execute

  1. kubectl get svc -n ingress-nginx
  2. kubectl edit svc ingress-nginx-controller -n ingress-nginx
  3. Change the type field to NodePort
  4. esc -> :wq

Now we’re ready to test the whole flow.

5. Testing

First we need to see which port is exposed on this ingress-nginx-controller service. Execute the same command as above and fetch the port.

Mine is exposed on 31992

Send a request to doc service without the testing header

Notice that we got a 403 on the request and nothing is printed in the console, at least nothing that we logged on our only endpoint from doc service.

Now check the logs from auth-server. Eventually you’ll see these lines

c.a.a.c.AuthorizeRequestController       : Started checking the request
c.a.a.c.AuthorizeRequestController : Request failed the check, throwing 403

So this means the request got stopped before reaching the doc service.

Now try again with -H "test-header: test" . You should get the good response back now.

Full flow testing

Conclusion

As you might have seen, it’s pretty easy to setup a not-so-complex routing system using kubernetes. Of course, there are a ton of things that you can do with (nginx) ingresses, like rate limiting, a more complex authetication mechanism, caching etc. but for me it seems more viable than having a bunch of backend services that talk to each other. I see all this routing mechanism as something that has to be done outside the context of microservices of the application. They should only focus solely on what they were meant to do, and that’s it.

If you have any questions or suggestions, you know what to do :D.

--

--

ALex Antonica

Software engineer in my 5th year now, passionate and curious about new technologies, practices and ways of thinking. Never stop growing.