Configuring CORS in Kubernetes is not easy, let alone debugging issues with it. In a microservices architecture, leaving CORS to be managed by each backend could lead to non-standardized CORS rules throughout your APIs. So how can we solve these issues?
CORS is a mechanism implemented by browsers to ensure that requests are not sent to the server when the request is made by a domain the server does not expect to receive a request from (for example, malicious.com).
It’s a security feature implemented for browsers mainly because when using CORS acts on the
Origin header of a request (which an attacker can easily set if they’re running the request outside of the browser), it’s no good for the server to protect against these types of requests. But, if the request is made through a browser, the
Origin header cannot be overridden by an attacker because the browser controls this header. So CORS becomes a browser’s responsibility as it’s the browser that should block the malicious requests.
For this example, we will be using Kusk Gateway, an open-source OpenAPI-driven Ingress Controller. The reasons we want to use Kusk Gateway is that firstly it simplifies the configuration of Envoy, a very sophisticated proxy, and it allows users to configure it using an OpenAPI definition, so the developers don’t need to learn yet another Kubernetes annotations (the case with Nginx Ingress for example), and they can leverage the OpenAPI ecosystem for code-generation, API mocking, testing, and more.
Let’s see how that looks in practice. We will deploy a simple hello-world example.
First, let’s install the Kusk CLI:
brew install kubeshop/kusk/kusk
curl -sSLf https://raw.githubusercontent.com/kubeshop/kusk-gateway/main/cmd/kusk/scripts/install.sh | bash
# Windows (go binary needed)
go install -x github.com/kubeshop/kusk-gateway/cmd/kusk@latest
Once the CLI is installed, proceed to install Kusk in your cluster:
kusk cluster install
kubectl create deployment hello-world --image=kubeshop/kusk-hello-world:v1.0.0
And we now wrap it in a service:
kubectl expose deployment hello-world --type=ClusterIP --port=8080
Create a file
openapi.yaml with the following content:
As you can see this is a standard OpenAPI definition. This API exposes a single
We also have the
x-kusk extension, this is a standard way to make annotations in OpenAPI, we’ve added there the CORS rules for our API and also the upstream details of the application we deployed in the first step.
The following command will configure Kusk Gateway with the new rules:
kusk deploy -i openapi.yaml
One way of testing CORS rules is by trying to access the website from the browser through a different domain than the one we have defined in CORS. The other way of testing it is through the CLI with
curl. Let’s explore both.
But first, let’s get the External IP of our Ingress Controller:
As you’ve seen in the step earlier, we have configured CORS to expect requests from example.com and we will now make our requests from example.org to test that the requests are blocked.
.then(res => console.log(res))
Given that our CORS configuration expects requests from example.com and not example.org, the browser should fail the request with the following error:
What is happening under the hood is that the browser makes a preflight request (a request before the actual request is made) which essentially asks the server for the origins that it expects to be called from. This happens by making an HTTP request using the OPTIONS method.
The server checks the origin header that the browser has set and it then can decide between either adding the
Access-Control-Allow-Origin: https://example.com\ or not adding it. In case it’s not added (meaning the server does not recognize the origin it’s being called from), the browser blocks the actual request.
With Kusk, it’s really easy to configure CORS in your APIs from a single place and using a standard like OpenAPI, which is essential for any modern REST API developer.
Check out how to configure CORS, rate-limiting and authentication in your Ingress Controller in our guides and let us know about your experience in our Discord channel, and we are more than happy to see issues raised and PRs opened in our Github repository :)
Thank you for reading!