Access

Control access for workloads in your service mesh.

For example, you might want to enforce a “zero trust” network model for all of your services. In your workspace settings, you can enable service isolation so that only services within the same workspace (or imported workspaces) can communicate with each other. For even more control, you can set up access policies. With access policies, you can deny all access to a specific destination except for the clients that you explicitly allow.

This guide walks you through how to set up Gloo access policies to restrict incoming or outgoing traffic on Layer 4 for an endpoint to a specific port and protocol. For example, you can allow incoming TCP traffic on port 3000 only. Note that if you use the Solo distribution of the Cilium CNI, Gloo access policies are automatically translated into Cilium network policies for you.

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

This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started. If you have different names, make sure to update the sample configuration files in this guide.
  1. 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 as kubectl and istioctl.
    • 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.
  2. Install Bookinfo and other sample apps.

Configure access policies

You can apply an access policy at the workload or destination level. For more information, see Applying policies.

Workload selectors are considered more secure than destination selectors and are therefore recommended to be used whenever possible. While workload selectors are applied to the translated Istio AuthorizationPolicy resource directly, destination selectors require the translation of the selected destination first before the access policy is enforced. This can lead to a window where traffic is unsecured if a new destination is added to the cluster. However, keep in mind that workloads selectors cannot be used when service isolation is enabled in your workspace. If service isolation is enabled, you must use destination selectors instead. Destination selectors can select only Kubernetes services. Gloo virtual destinations or Gloo external services are not supported.

The following example is for a simple access policy that allows only the productpage app to access the ratings app. Keep in mind that the applyToWorkloads selector in the example does not work if you enable service isolation in your workspace.

apiVersion: security.policy.gloo.solo.io/v2
kind: AccessPolicy
metadata:
  name: ratings-access
  namespace: bookinfo
spec:
  applyToWorkloads:
  - selector:
      labels:
        app: ratings
  config:
    authn:
      tlsMode: STRICT
    authzList:
    - allowedClients:
      - serviceAccountSelector:
          labels:
            account: productpage
      allowedPaths:
      - /ratings*

The following example is for a simple access policy that allows only the productpage app to access the ratings app. When comparing this access policy to the access policy that uses a workload selector, the destination selector also lets you specify the port number of the target app.

apiVersion: security.policy.gloo.solo.io/v2
kind: AccessPolicy
metadata:
  name: ratings-access
  namespace: bookinfo
spec:
  applyToDestinations:
  - port:
      number: 9080
    selector:
      labels:
        app: ratings
  config:
    authn:
      tlsMode: STRICT
    authzList:
    - allowedClients:
      - serviceAccountSelector:
          labels:
            account: productpage
      allowedPaths:
      - /ratings*

The following access policy specifies the request headers that must be sent to allow or deny the communication between the productpage and ratings apps. For example, if you send a request with the X-Test-Header: match header from the productpage app to the ratings app, the request is matched and the communication between productpage and ratings is allowed. If you send the same request without a header or with the X-Test-Header: noMatch header, the request is not matched and the communication between the apps is denied.

apiVersion: security.policy.gloo.solo.io/v2
kind: AccessPolicy
metadata:
  name: ratings-access
  namespace: bookinfo
spec:
  applyToDestinations:
  - port:
      number: 9080
    selector:
      labels:
        app: ratings
  config:
    authn:
      tlsMode: STRICT
    authzList:
    - allowedClients:
      - serviceAccountSelector:
          labels:
            account: productpage
      allowedPaths:
      - /ratings*
      match:
        request:
          headers:
            X-Test-Header:
              notValues:
              - noMatch
              - partial-blocked
              values:
              - match
              - partial*

The following example specifies the IP address that you want to allow access to the ratings app. When a client sends a request to ratings, the client's IP address is matched against the IP addresses that are defined in the access policy. If the IP address matches, the request is forwarded to the ratings app. If the IP does not match, access to ratings is denied.

You can specify single IP addresses (e.g. 111.123.2.0) or an IP address CIDR block (e.g. 123.222.167.4/24).

Keep in mind that the applyToWorkloads selector in the example does not work if you enable service isolation in your workspace.

apiVersion: security.policy.gloo.solo.io/v2
kind: AccessPolicy
metadata:
  name: ratings-access
  namespace: bookinfo
spec:
  applyToWorkloads:
  - selector:
      labels:
        app: ratings
  config:
    authn:
      tlsMode: STRICT
    authzList:
    - allowedIpBlocks:
      - 112.114.230.1
      allowedPaths:
      - /ratings*

You can have multiple authzList entries to configure access to workloads. A request is allowed when it matches at least one of the authzList entries (logically OR'd together).

For each entry, you can specify different requirements for allowed clients, paths, methods, IP blocks, and other configuration settings. Then, a request is allowed only when ALL of the requirements are met (logically AND'd together).

In the following example:

  • The product page app is allowed to send GET requests to the ratings app along the /ratings* wildcard path.
  • The product page app is allowed to send CREATE, DELETE, PATCH, and POST requests to the ratings app along the /ratings/1* wildcard path.
  • The product page app is allowed to send PATCH requests to the ratings app along the /ratings/2* wildcard path.
  • Keep in mind that the applyToWorkloads selector in the example does not work if you enable service isolation in your workspace.
apiVersion: security.policy.gloo.solo.io/v2
kind: AccessPolicy
metadata:
  name: ratings-access
  namespace: bookinfo
spec:
  applyToWorkloads:
  - selector:
      labels:
        app: ratings
  config:
    authn:
      tlsMode: STRICT
    authzList:
    - allowedClients:
      - serviceAccountSelector:
        labels:
          app: productpage
      allowedPaths:
      - /ratings*
      allowedMethods:
      - GET
    - allowedClients:
      - serviceAccountSelector:
          labels:
            app: productpage
      allowedPaths:
      - /ratings/1*
      allowedMethods:
      - CREATE
      - DELETE
      - PATCH
      - POST
    - allowedClients:
      - serviceAccountSelector:
          labels:
            app: productpage
      allowedPaths:
      - /ratings/2*
      allowedMethods:
      - PATCH

You can have multiple allowed clients in the same access policy. A request is allowed when it matches at least one of the authzList entries (logically OR'd together).

For each client, you can specify different requirements for allowed paths, methods, IP blocks, and other configuration settings. Then, a request is allowed only when ALL of the requirements are met (logically AND'd together).

In the following example:

  • The product page app is allowed to access the ratings app along the /ratings* wildcard path.
  • The reviews app is allowed to access the ratings app for requests that use the PATCH method.
  • Keep in mind that the applyToWorkloads selector in the example does not work if you enable service isolation in your workspace.
apiVersion: security.policy.gloo.solo.io/v2
kind: AccessPolicy
metadata:
  name: ratings-access
  namespace: bookinfo
spec:
  applyToWorkloads:
  - selector:
      labels:
        app: ratings
  config:
    authn:
      tlsMode: STRICT
    authzList:
    - allowedClients:
      - serviceAccountSelector:
          labels:
            app: productpage
      allowedPaths:
      - /ratings*
    - allowedClients:
      - serviceAccountSelector:
          labels:
            app: reviews
      allowedMethods:
      - PATCH

Review the following table to understand this configuration. For more information, see the API docs.

Setting Description
applyToDestinations or applyToWorkloads Configure which destinations to apply the policy to, by using labels. Destinations can be a Kubernetes service, VirtualDestination, or ExternalService. If you do not specify any destinations or routes, the policy applies to all destinations in the workspace by default. If you do not specify any destinations but you do specify a route, the policy applies to the route but to no destinations. Note that for security reasons, applyToWorkloads is preferred. However, the applyToWorkloads selector does not work if you enable service isolation in your workspace.
authn For authentication, set the type of TLS policy to enforce when connecting to the workload. STRICT secures connections to the upstream workload with mTLS by presenting client certificates for authentication. If you set up service isolation for the workspace, this setting is always set to STRICT. Other modes include PERMISSIVE to allow both TLS-secured and plain text connections to the upstream endpoint, or DISABLE to not set up a TLS connection to the upstream endpoint.
authzList Configure how clients are allowed to access the workload. You can have multiple allowed clients in the same access policy, as well as multiple configurations for the same client. A request is allowed when it matches at least one of the entries in this authzList field (logically OR'd together).
allowedClients Configure which clients are permitted to access the workload. In this example, the allowed clients are restricted to the product page app, as selected by the label of its Kubernetes service account.
allowedIpBlocks Specify the IP address or IP address CIDR that you want to allow access to the workload.
allowedPaths Optionally, you can restrict which path to allow. For HTTP paths, exact match, prefix match, and suffix match are supported. In this example, requests are restricted to the suffix match path /ratings*.
allowedMethods Optionally, you can restrict the HTTP methods to allow, such as GET or POST. If the request is made via gRPC, this value is ignored because the method is always POST. In this example, no method is set, so all methods are allowed by default.
match Define the requirements that must be met to match the request and enforce the policy.
request.headers Specify the request headers that must be sent to allow or deny communication between the allowedClients and the workload that you specified. If a header is sent that is specified in the values section, the header is matched and the request is allowed. Headers in the notValues section are not allowed, and requests that include this header are denied.

Verify access policies

  1. Create a temporary curl or debug pod and send a request to the ratings app from your local machine. The communication from your local machine to the ratings app is allowed as no access policy is applied yet.

    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.

    1. Create the curl pod.

      kubectl run -it -n bookinfo --context $REMOTE_CONTEXT1 curl \
        --image=curlimages/curl:7.73.0 --rm  -- sh
      
    2. Send a request to the ratings app.

      curl http://ratings:9080/ratings/1 -w "${http_code}"
      

      Example output:

      {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}200
      
    3. 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
    

    Example output:

    HTTP/1.1 200 OK
    ...
    {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}
    

    If the output has an error about EphemeralContainers, see Ephemeral containers don’t work when testing Bookinfo.

  2. Apply one of the access policies. The access policy allows access to the ratings app from the product page app. Requests from all other clients are denied.

    kubectl apply -f policy.yaml
    
  3. Verify that requests from a temporary curl or debug pod are now blocked. You get back a 403 Forbidden response.

    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.

    1. Create the curl pod.

      kubectl run -it -n bookinfo --context $REMOTE_CONTEXT1 curl \
        --image=curlimages/curl:7.73.0 --rm  -- sh
      
    2. Send a request to the ratings app.

      curl http://ratings:9080/ratings/1 -w "${http_code}"
      

      Example output:

      RBAC: access denied
      
    3. 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=productpage -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -v http://ratings:9080/ratings/1
    

    If the output has an error about EphemeralContainers, see Ephemeral containers don’t work when testing Bookinfo.

  4. Verify that the productpage app can access the ratings app. Note that if you created an access policy with request header matching, you must provide the X-Test-Header: <header-value> in your curl request.

    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=productpage -A -o jsonpath='{.items[0].metadata.name}') --image=curlimages/curl -- curl -v http://ratings:9080/ratings/1
    

    Example output:

    HTTP/1.1 200 OK
    ...
    {"id":1,"ratings":{"Reviewer1":5,"Reviewer2":4}}
    

    Add a curl container to your app deployment 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.

    1. Create the curl container.
      kubectl apply --context ${REMOTE_CONTEXT1} -f https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/main/policy-demo/productpage-with-curl.yaml
      
    2. Verify that requests now succeed. You get back a 200 response.
      kubectl exec $(kubectl get pod -l app=productpage -A --context ${REMOTE_CONTEXT1}  -o jsonpath='{.items[0].metadata.name}') -n bookinfo -c curl --context ${REMOTE_CONTEXT1} -- curl -v http://ratings:9080/ratings/1
      
    3. Exit the temporary pod. The pod deletes itself.
      exit
      

  5. Check the metrics to verify that the policy allowed or blocked requests.

    1. Open the Prometheus expression browser.

      For more information, see the CLI documentation.

      meshctl proxy prometheus
      

      Port-forward the prometheus-server deployment on 9091.

      kubectl -n gloo-mesh port-forward deploy/prometheus-server 9091
      
    2. Open your browser and connect to localhost:9091/.
    3. In the Expression search bar, enter rate(hubble_drop_total{destination_workload_id=~"ratings.+"}[5m]), then click Execute.
    4. Verify that you can see requests from the temporary curl pod to the ratings app that were dropped because of the access policy.

Cleanup

You can optionally remove the resources that you set up as part of this guide.
kubectl --context $REMOTE_CONTEXT1 -n bookinfo delete accesspolicy ratings-access