When a client from one domain accesses a service in a different domain or a service that resides in the same domain but uses a different port from the client, the client initiates a cross-origin request. If the service does not allow cross-origin resource access, the client cannot access the service. In this case, you can implement cross-origin resource sharing (CORS) to allow web application servers to support cross-origin resource access. This topic describes how to configure a CORS policy in a virtual service of Alibaba Cloud Service Mesh (ASM) to implement CORS.

CORS overview

For security reasons, browsers restrict cross-origin HTTP requests that are initiated from scripts. To meet your requirements for cross-origin resource access, ASM allows you to implement CORS. CORS is a mechanism based on HTTP headers and allows a server to identify domains, schemes, or ports other than its own from which a browser permits loading resources.

The CORS mechanism supports two types of requests: simple requests and preflight requests.
  • Simple request mode:

    A browser sends a cross-origin request. The Origin header is specified in the request. This indicates that the request is a cross-origin request. After the destination server receives the cross-origin request, the server determines whether to allow the request based on configured CORS rules. In response, the server returns the Access-Control-Allow-Origin and Access-Control-Allow-Methods headers to indicate whether the request is allowed.

  • Preflight request mode:

    A browser sends a preflight request. The request is an HTTP OPTIONS request and is used to check whether the destination server allows cross-origin requests from the current domain. If the destination server allows cross-origin requests from the current domain, the browser sends an actual cross-origin request.

    The OPTIONS request contains the following headers: Origin, Access-Control-Request-Method, and Access-Control-Request-Headers. After the destination server receives the OPTIONS request, the server specifies the Access-Control-Allow-Origin, Access-Control-Allow-Method, Access-Control-Allow-Headers, and Access-Control-Max-Age headers in the response to indicate whether the request is allowed. If the preflight request is allowed, the browser sends an actual cross-origin request.

If a request meets the following three requirements, the CORS mechanism processes the request as a simple request. Otherwise, the CORS mechanism processes the request as a preflight request.
  • The request uses one of the following methods:

    GET, HEAD, and POST

  • The Content-Type header in the request is set to one of the following values:

    ext/plain, application/x-www-form-urlencoded, and multipart/form-data

  • The request uses one of the following CORS-safelisted headers that are defined by the Fetch standard:

    Accept, Accept-Language, Content-Language, and Content-Type. Note: The value of the Content-Type header must be set to one of the values that are listed in the second requirement.

Configure a CORS policy in a virtual service

Browsers automatically implement CORS communication. To allow cross-origin requests that are initiated to a service and implement CORS communication, you must set the corsPolicy field in the virtual service that is defined for the service.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings-route
spec:
  hosts:
  - ratings.prod.svc.cluster.local
  http:
  - route:
    - destination:
        host: ratings.prod.svc.cluster.local
        subset: v1
    corsPolicy:
      allowOrigins:
      - exact: https://example.com
      - regex: .*     # Regular expressions are supported to specify the addresses of the origins.
      allowMethods:
      - POST
      - GET
      allowCredentials: false
      allowHeaders:
      - X-Foo-Bar
      maxAge: "24h"
Parameter Description
allowOrigins The addresses of the origins that are allowed to access the service. Regular expressions are supported. For requests without credentials, the server can set this parameter to a wildcard (*) so that all origins are allowed to access the service.
allowMethods The HTTP methods that can be used to initiate cross-origin requests.
allowHeaders The headers that can be contained to initiate cross-origin requests. The headers are used to precheck the responses to requests.
exposeHeaders The allowlist of headers that the server allows browsers to access.
maxAge The maximum amount of time that browsers can cache the response to a preflight request.
allowCredentials Specifies whether credentials are required to initiate cross-origin requests. Only valid credentials can be used to initiate cross-origin requests.

Best practices for CORS

Prerequisites

Step 1: Deploy applications

  1. Deploy a backend application.
    1. Use kubectl to connect to the ACK cluster. For more information, see Connect to ACK clusters by using kubectl.
    2. Create a file named details.yaml and add the following content to the file:
      apiVersion: v1
      kind: Service
      metadata:
        name: details
        labels:
          app: details
          service: details
      spec:
        ports:
        - port: 9080
          name: http
        selector:
          app: details
      ---
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: bookinfo-details
        labels:
          account: details
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: details-v1
        labels:
          app: details
          version: v1
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: details
            version: v1
        template:
          metadata:
            labels:
              app: details
              version: v1
          spec:
            serviceAccountName: bookinfo-details
            containers:
            - name: details
              image: docker.io/istio/examples-bookinfo-details-v1:1.16.4
              imagePullPolicy: IfNotPresent
              ports:
              - containerPort: 9080
              securityContext:
                runAsUser: 1000
    3. Run the following command to deploy the details application in the default namespace:
      kubectl apply -f details.yaml -n default
  2. Deploy a frontend application.
    1. Create a file named istio-cors-demo.yaml and add the following content to the file:
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: istio-cors-demo
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: istio-cors-demo
        labels:
          app: istio-cors-demo
          service: istio-cors-demo
      spec:
        ports:
          - name: http
            port: 8000
            targetPort: 80
        selector:
          app: istio-cors-demo
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: istio-cors-demo
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: istio-cors-demo
            version: v1
        template:
          metadata:
            labels:
              app: istio-cors-demo
              version: v1
          spec:
            serviceAccountName: istio-cors-demo
            containers:
              - image: registry.cn-hangzhou.aliyuncs.com/build-test/istio-cors-demo:v1.0-g8e215f6-aliyun
                imagePullPolicy: IfNotPresent
                name: istio-cors-demo
                ports:
                  - containerPort: 80
    2. Run the following command to deploy the istio-cors-demo application in the foo namespace:
      kubectl apply -f istio-cors-demo.yaml -n foo 

Step 2: Deploy ingress gateway services

  1. Log on to the ASM console.
  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
  3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.
  4. On the details page of the ASM instance, click ASM Gateways in the left-side navigation pane. On the ASM Gateways page, click Create.
  5. On the Create page, set the Name parameter to ingressgateway, select a cluster in which you want to deploy the application from the Cluster drop-down list, select Internet Access as SLB Instance Type, and then select the specifications of the Server Load Balancer (SLB) instance from the Create SLB Instance drop-down list. Use the default values for other parameters and click Create.
  6. Repeat Step 4 and Step 5 to create an ingress gateway service named ingressgateway2.

Step 3: Create routing rules

  1. Create a routing rule for the backend application.
    1. Create an Istio gateway.

      Create an Istio gateway and associate the details application with the ingressgateway gateway service.

      1. Log on to the ASM console.
      2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
      3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.
      4. On the details page of the ASM instance, choose Traffic Management Center > Gateway in the left-side navigation pane. On the Gateway page, click Create from YAML.
      5. On the Create page, set the Namespace parameter to default, select a template from the Template drop-down list, replace the content in the YAML field with the following content, and then click Create.
        apiVersion: networking.istio.io/v1beta1
        kind: Gateway
        metadata:
          name: bookinfo-gateway
          namespace: default
        spec:
          selector:
            istio: ingressgateway
          servers:
            - hosts:
                - '*'
              port:
                name: http
                number: 80
                protocol: HTTP
                                           
    2. Create a virtual service.
      1. On the details page of the ASM instance, choose Traffic Management Center > VirtualService in the left-side navigation pane. On the VirtualService page, click Create from YAML.
      2. On the Create page, set the Namespace to default, select a template from the Template drop-down list, replace the content in the YAML field with the following content, and then click Create.
        apiVersion: networking.istio.io/v1beta1
        kind: VirtualService
        metadata:
          name: bookinfo
          namespace: default
        spec:
          gateways:
            - bookinfo-gateway
          hosts:
            - '*'
          http:
            - match:
                - uri:
                    prefix: /details
              route:
                - destination:
                    host: details
                    port:
                      number: 9080
                                                
    3. Access the backend application.
      1. Obtain the IP address of the ingressgateway gateway service. For more information, see Deploy an ingress gateway service.
      2. In the browser address bar, enter http://<IP address of the ingressgateway gateway service>/details/2. detail

        If the page shown in the preceding figure is displayed, the request to the details backend application is successful.

  2. Create a routing rule for the frontend application.
    1. Create an Istio gateway.

      Create an Istio gateway and associate the istio-cors-demo application with the ingressgateway2 gateway service.

      1. On the details page of the ASM instance, choose Traffic Management Center > Gateway in the left-side navigation pane. On the Gateway page, click Create from YAML.
      2. On the Create page, set the Namespace parameter to foo, select a template from the Template drop-down list, replace the content in the YAML field with the following content, and then click Create.
        apiVersion: networking.istio.io/v1beta1
        kind: Gateway
        metadata:
          name: istio-cors-demo-gateway
          namespace: foo
        spec:
          selector:
            istio: ingressgateway2
          servers:
            - hosts:
                - '*'
              port:
                name: http
                number: 80
                protocol: HTTP
    2. Create a virtual service.
      1. On the details page of the ASM instance, choose Traffic Management Center > VirtualService in the left-side navigation pane. On the VirtualService page, click Create from YAML.
      2. On the Create page, set the Namespace parameter to foo, select a template from the Template drop-down list, replace the content in the YAML field with the following content, and click Create.
        apiVersion: networking.istio.io/v1beta1
        kind: VirtualService
        metadata:
          name: istio-cors-demo
          namespace: foo
        spec:
          gateways:
            - istio-cors-demo-gateway
          hosts:
            - '*'
          http:
            - route:
                - destination:
                    host: istio-cors-demo
                    port:
                      number: 8000
  3. Access the backend application by using the frontend application.
    1. Obtain the IP address of the ingressgateway2 gateway service. For more information, see Deploy an ingress gateway service.
    2. In the address bar of Google Chrome, enter http://<IP address of the ingressgateway2 gateway service>.
    3. In the URL field, enter http://<IP address of the ingressgateway gateway>/details/2 and click Send.
      URL
    4. In the upper-right corner of Google Chrome, click the Settings icon icon and choose More Tools > Developer Tools.
      Error message

      The preceding error messages indicate that the request fails. This is because the istio-cors-demo frontend application sends a cross-domain request to access the details backend application.

Step 4: Configure a CORS policy

  1. Log on to the ASM console.
  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
  3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.
  4. On the details page of the ASM instance, choose Traffic Management Center > VirtualService in the left-side navigation pane.
  5. On the VirtualService page, click YAML in the Actions column that corresponds to the bookinfo service.
  6. In the Edit dialog box, add the following content to the http parameter and click OK.
    - corsPolicy:
        allowCredentials: false
        allowMethods:
          - POST
          - GET
        allowOrigins:
          - prefix: 'http://<IP address of the ingressgateway2 gateway service>'
        maxAge: 24h
    http parameter

Step 5: Verify that the CORS policy takes effect

  1. In the address bar of Google Chrome, enter http://<IP address of the ingressgateway2 gateway service>.
  2. In the URL field, enter http://<IP address of the ingressgateway gateway service>/details/2 and click Send.
    Success response

    If the message in the preceding page is returned, the istio-cors-demo frontend application accesses the details backend application as expected. This indicates that the CORS policy takes effect.