CORS
Enforce client-site access controls with cross-origin resource sharing (CORS).For example, you can configure requests that are made on your behalf. At the same time, you can block requests that are made by attacks, such as Javascript code or malware. Consider the following request scenarios that you can configure with CORS.
- A different domain, such as
example.com
site callsapi.com
- A different subdomain, such as
example.com
callsapi.example.com
- A different port, such as
example.com
callsexample.com:3001
- A different protocol, such as
https://example.com
callshttp://example.com
For more information, see the following resources.
If you import or export resources across workspaces, your policies might not apply. For more information, see Import and export policies.
Before you begin
- Complete the multicluster getting started guide to set up the following testing environment.
- Three clusters along with environment variables for the clusters and their Kubernetes contexts.
- The Gloo Platform CLI,
meshctl
, along with other CLI tools such askubectl
andistioctl
. - The Gloo management server in the management cluster, and the Gloo agents in the workload clusters.
- Istio installed in the workload clusters.
- A simple Gloo workspace setup.
- Install Bookinfo and other sample apps.
Configure CORS policies
You can apply a CORS policy at the route level. For more information, see Applying policies.
Review the following sample configuration files.
apiVersion: security.policy.gloo.solo.io/v2
kind: CORSPolicy
metadata:
name: simple-cors
namespace: bookinfo
annotations:
cluster.solo.io/cluster: $REMOTE_CLUSTER1
spec:
applyToRoutes:
- route:
labels:
route: ratings
config:
maxAge: 1m
allowCredentials: true
allowHeaders:
- foo
- bar
allowMethods:
- GET
allowOrigins:
- exact: http://istio.io
Review the following table to understand this configuration. For more information, see the API docs.
Setting | Description |
---|---|
spec.applyToRoutes |
Configure which routes to apply the policy to, by using labels. The label matches the app and the route from the route table. If omitted, the policy applies to all routes in the workspace. |
maxAge |
Optionally specify how long the results of a preflight request are cached. This value translates to the Access-Control-Max-Age header. In this example, the value is 1m . |
allowCredential |
Optionally let the caller send the actualy request with credentials, not just the preflight request. This value translates to the Access-Control-Allow-Credentials header. In this example, the value is true . |
allowHeaders |
Specify a list of HTTP headers that can be used in requests. This value is serialized to the Access-Control-Allow-Headers header. In this example, the foo and bar headers can be used in the request. |
allowMethods |
Specify a list of HTTP methods that can be used in requests. This value is serialized to the Access-Control-Allow-Methods header. In this example, only the GET method can be used to access the resource. |
allowOrigins |
Enter a string pattern to use to decide if an origin is allowed. An origin is allowed if any of the string patterns match the origin in the header. You can set up exact , prefix , suffix , or regex matches. By default, matching is case-sensitive for exact, prefix, and suffix matching, unless you include ignoreCase: true . This value is serialized to the Access-Control-Allow-Origin header. In this example, the origin header must match exactly http://istio.io . |
Verify CORS policies
-
Apply the example CORS policy in the cluster with the Bookinfo workspace in your example setup.
kubectl apply --context ${REMOTE_CONTEXT1} -f - << EOF apiVersion: security.policy.gloo.solo.io/v2 kind: CORSPolicy metadata: name: simple-cors namespace: bookinfo annotations: cluster.solo.io/cluster: $REMOTE_CLUSTER1 spec: applyToRoutes: - route: labels: route: ratings config: maxAge: 1m allowCredentials: true allowHeaders: - foo - bar allowMethods: - GET allowOrigins: - exact: http://istio.io EOF
-
Create a route table for the ratings app. Because the policy applies at the route level, Gloo checks for the route in a route table resource.
kubectl apply --context ${REMOTE_CONTEXT1} -f - << EOF apiVersion: networking.gloo.solo.io/v2 kind: RouteTable metadata: name: ratings-rt namespace: bookinfo spec: hosts: - ratings http: - forwardTo: destinations: - ref: name: ratings namespace: bookinfo labels: route: ratings workloadSelectors: - {} EOF
Review the following table to understand this configuration. For more information, see the API docs.
Setting Description hosts
The host that the route table routes traffic for. In this example, the ratings
host matches the ratings service within the mesh.http.forwardTo.destinations
The destination to forward requests that come in along the host route. In this example, the ratings service is selected. http.labels
The label for the route. This label must match the label that the policy selects. workloadSelectors
The source workloads within the mesh that this route table routes traffic for. In the example, all workloads are selected. This way, the curl container that you create in subsequent steps can send a request along the ratings route. -
Send a request to the ratings app from within the mesh.
Create a temporary curl pod in the
bookinfo
namespace, so that you can test the app setup. You can also use this method in Kubernetes 1.23 or later, but an ephemeral container might be simpler, as shown in the other tab.- Create the curl pod.
kubectl run -it -n bookinfo --context $REMOTE_CONTEXT1 curl \ --image=curlimages/curl:7.73.0 --rm -- sh
- Send a request to the ratings app.
curl http://ratings:9080/ratings/1 -v -X OPTIONS -H "Origin: http://istio.io" -H "Access-Control-Request-Method: GET"
- Exit the temporary pod. The pod deletes itself.
exit
Use the
kubectl debug
command to create an ephemeral curl container in the deployment. This way, the curl container inherits any permissions from the app that you want to test. If you don't run Kubernetes 1.23 or later, you can deploy a separate curl pod or manually add the curl container as shown in the other tab.kubectl --context ${REMOTE_CONTEXT1} -n bookinfo debug -i pods/$(kubectl get pod --context ${REMOTE_CONTEXT1} -l app=reviews -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -v http://ratings:9080/ratings/1 -X OPTIONS -H "Origin: http://istio.io" -H "Access-Control-Request-Method: GET"
If the output has an error about
EphemeralContainers
, see Ephemeral containers don’t work when testing Bookinfo. - Create the curl pod.
-
Review the responses to verify your CORS policy. Depending on how you set up the policy, you might still expect to get a 200 response, but certain access control headers are missing, which let you know that the CORS is rejected.
- If CORS is enabled, you see an
Access-Control-Allow-Origin
header in the response. - If CORS is disabled, you do not see any
Access-Control-*
headers in the response. - If the preflight request is successful, the response includes the
Access-Control-Allow-Origin
,Access-Control-Allow-Methods
, andAccess-Control-Allow-Headers
. If the request was not successful, these headers are missing.
Example response:
HTTP/1.1 200 OK access-control-allow-origin: http://istio.io access-control-allow-credentials: true access-control-allow-methods: GET access-control-allow-headers: foo,bar access-control-max-age: 60
- If CORS is enabled, you see an
-
Optional: Clean up the resources that you created.
kubectl --context $REMOTE_CONTEXT1 -n bookinfo delete routetable ratings-rt kubectl --context $REMOTE_CONTEXT1 -n bookinfo delete CORSPolicy simple-cors