CORS

Summary

Grey Matter supports the configuration of cross-origin resource sharing on a sidecar. CORS can be configured to allow an application to access resources at a different origin (domain, protocol, or port) than its own.

For more information on CORS, see this CORS reference.

Simple Requests

Simple requests are classified as requests that don't require a CORS preflight check. This distinction is made between requests that might be dangerous (i.e. modifies server resources) and those that are most likely benign. A request is considered simple when all of the following criteria is true:

  • The method is GET, HEAD, or POST

  • Only CORS safe-listed headers are present

  • The Content Type header is set to one of application/x-www-form-urlencoded or multipart/form-data or text/plain

A more comprehensive list and explanation can be found here.

As an example, say an app running at http://localhost:8080 is trying to call a backend service with a Grey Matter sidecar at http://localhost:10808. Without a CORS configuration, this request would fail, because the app localhost:8080 is trying to access resources from a server at a different origin, localhost:10808. To solve this, the following CORS config is set on its domain:

{
  "zone_key": "zone-default-zone",
  "domain_key": "domain-backend-service",
  "name": "*",
  "port": 10808,
  "cors_config": {
    "allowed_origins": [
      { "match_type": "exact", "value": "http://localhost:8080" }
    ],
    "allowed_headers": [],
    "allowed_methods": [],
    "exposed_headers": [],
    "max_age": 60
  }
}

With this configuration, if a simple request comes in to the sidecar from the app, it will have an Origin header value of http://localhost:8080, and this request will succeed. The server will attach a header Access-Control-Allow-Origin: http://localhost:8080 to the response, which signals to the browser that this request is allowed.

Preflight Requests

Preflight requests are initiated by the browser using the OPTIONS HTTP method before sending a request in order to determine if the real request is safe to send. The response to this kind of request contains information about what is allowed from a request, and the server determines whether or not to send the actual request based on this information.

This response information is in the form of three HTTP headers, access-control-request-method, access-control-request-headers, and the origin header. These correspond to fields of the cors_config - thus these configurations can be specified to determine how the Grey Matter sidecar will respond to preflight requests. If a preflight request comes in to the Grey Matter Sidecar that does not meet the specification for one of these configured fields, the sidecar will send back a response to not initiate the request.

Based on the same example from simple requests, say the app running at http://localhost:8080 wants to send requests to the backend service at http://localhost:10808 with a content-type of application/json;charset=UTF-8. This particular content-type is outside of those allowed by CORS for simple requests, and thus would result in sending a preflight request to determine if the request can be sent. In order for the CORS configuration to indicate that the request can be sent, it would need to allow the content-type header by configuring the allowed_headers field:

{
  "zone_key": "zone-default-zone",
  "domain_key": "domain-backend-service",
  "name": "*",
  "port": 10808,
  "cors_config": {
    "allowed_origins": [
      { "match_type": "exact", "value": "http://localhost:8080" }
    ],
    "allowed_headers": ["content-type"],
    "allowed_methods": [],
    "exposed_headers": [],
    "max_age": 60
  }
}

In the above configuration, CORS will allow requests with Origin header value http://localhost:8080 only and indicate that the content-type header can be set according to the request.

Configuration

To set up CORS, set the cors_config field on a domain object with the desired configuration, see the fields below.

For an existing domain, run

greymatter edit domain <domain-name>

and add the desired cors_config object.

Example object

  "cors_config": {
    "allowed_origins": [],
    "allowed_headers": [],
    "allowed_methods": [],
    "exposed_headers": [],
    "max_age": 0,
    "allow_credentials": true
  }

Fields

allowed_origins

This field specifies an array of string patterns that match allowed origins. The proxy will use these matchers to set the access-control-allow-origin header. This header will be set on any cross-origin response that matches one of the allowed_origins.

Available matchers include:

  • exact

  • prefix

  • suffix

  • regex

Example:

  "allowed_origins": [
      { "match_type": "exact", "value": "http://localhost:8080" }
    ]

A wildcard value * is allowed except when using the regex matcher.

allow_credentials

Specifies the content for the access-control-allow-credentials header that the proxy will set on any cross-origin request that matches one of the allowed_origins. This header specifies whether or not the upstream service allows credentials.

exposed_headers

Specifies the content for the access-control-expose-headers header that the proxy will set on any cross-origin request that matches one of the allowed_origins. This header specifies an array of headers that are allowed on the response.

max_age

Specifies the content for the access-control-max-age header that the proxy will set on the preflight response. This header is an integer value specifying how long a preflight request can be cached by the browser.

allowed_methods

Specifies the content for the access-control-allow-methods header that the proxy will set on the preflight response. This header specifies an array of methods allowed by the upstream service.

allowed_headers

Specifies the content for the access-control-allow-headers header that the proxy will set on the preflight response. This header specifies an array of headers allowed by the upstream service.

Notes

  • By default, the proxy will use the upstream service's CORS policy on the gateway and on the upstream service. The gateway policy is ignored.

  • Because CORS is a browser construct, curl can always make a request to the server, with or without CORS. However, it can be used to mimic a browser and verify how the proxy will react to CORS requests:

$ curl -v 'http://localhost:9080/services/catalog/latest/' \
    -X OPTIONS \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: content-type' \
    -H 'Origin: http://localhost:8080'
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 9080 (#0)
> OPTIONS /services/catalog/latest/ HTTP/1.1
> Host: localhost:9080
> User-Agent: curl/7.64.1
> Accept: */*
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: content-type
> Origin: http://localhost:8080
>
< HTTP/1.1 200 OK
< access-control-allow-origin: http://localhost:8080
< access-control-max-age: 60
< date: Tue, 12 May 2020 20:11:13 GMT
< server: envoy
< content-length: 0
<
* Connection #0 to host localhost left intact
* Closing connection 0

Last updated

Was this helpful?