Setup Ingress Controller with CORS configuration
Tags: cors, openapi, api, kubernetes
Published: 2022-10-04 16:43:48

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?

## First things first, what is CORS?

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akaiztasu5pmltqj8ucx.png)


## How to configure your Ingress Controller for CORS?

For this example, we will be using [Kusk Gateway](https://kusk.io/), 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](https://www.openapis.org/), 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](https://thenewstack.io/what-it-means-to-be-openapi-first-in-kubernetes/) for code-generation, API mocking, testing, [and more](https://openapi.tools/).

Let’s see how that looks in practice. We will deploy a simple hello-world example.

### 1- Install Kusk in your Kubernetes cluster

First, let’s install the Kusk CLI:

```sh
# MacOS
brew install kubeshop/kusk/kusk

# Linux
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:
```sh
kusk cluster install
```
### 2- Deploy a sample application to your clusterYou can deploy this example application:

```sh
kubectl create deployment hello-world --image=kubeshop/kusk-hello-world:v1.0.0
```
And we now wrap it in a service: 

```sh
kubectl expose deployment hello-world --type=ClusterIP --port=8080
```
### 3- Configure your OpenAPI definition for Kusk

Create a file `openapi.yaml` with the following content: 

{% embed https://gist.github.com/kubeshop-gists/a7fbf5d0443c3ef6fc393af21a31fe2e %}

As you can see this is a standard OpenAPI definition. This API exposes a single `/hello` path. 

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.

### 4- Apply the changes to the cluster

The following command will configure Kusk Gateway with the new rules: 

```sh
kusk deploy -i openapi.yaml
```

### 5- Connect to Kusk Gateway

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:

```sh
kusk ip
```

### 6- How to test CORS?

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.  

Open https://example.org in your browser, and access the developer console, then run the following Javascript snippet (make sure your change the EXTERNAL-IP):

```js
(function() {
  fetch("http://EXTERNAL-IP/hello")
    .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](https://developer.mozilla.org/en-US/docs/Glossary/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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/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. 

## CORS through your Ingress Controller made easy

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](http://docs.kusk.io) and let us know about your experience in our [Discord channel](https://discord.gg/uNuhy6GDyn), and we are more than happy to see issues raised and PRs opened in our [Github repository](https://github.com/kubeshop/kusk-gateway) :)

Thank you for reading!