Skip to content

Security Lab📜

In this lab, we’ll explore the security features offered by the Istio service mesh.

Mutual TLS📜

Istio enables mutual TLS for any service deployed on the mesh by default:

  • The service identifies as a function of its associated service account and namespace.
  • Workloads receive an x.509 certificate that rotates regularly. This certificate serves as the identifier of the workload when making calls to other services.

    Info

    It is important to note this only happens if you workload has an envoy proxy, regardless if it’s being injected automatically or manually, the Envoy proxy is what enables mTLS in Istio. Workloads without an Envoy proxy injected will not get mTLS.

In the observability lab, we examined the Kiali dashboard. We observed lock icons that indicate the traffic is secure with mTLS.

Can a workload receive plain-text requests?📜

We can test whether a mesh workload, such as the customer’s service, will allow a plain-text request as follows:

  1. Create a separate namespace that is not configured with automatic injection.

    kubectl create ns otherns
    
  2. Deploy sleep to that namespace.

    kubectl apply -f sleep.yaml -n otherns
    
  3. Verify that the sleep pod has no sidecars:

    kubectl get pod -n otherns
    
  4. Call the customer service from that pod:

    SLEEP_POD=$(kubectl get pod -l app=sleep -n otherns -ojsonpath='{.items[0].metadata.name}')
    kubectl exec -n otherns $SLEEP_POD -it -- curl customers.default
    

The output should resemble a list of customers in JSON format.

Our analysis shows that Istio allows plain-text requests by default, also known as Permissive mode. This mode helps services participate without being fully onboarded.

Enable STRICT mode📜

Istio provides the PeerAuthentication resource to define peer authentication policy.

  1. Apply the following PeerAuthentication policy.

    mtls-strict.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ---
    apiVersion: security.istio.io/v1beta1
    kind: PeerAuthentication
    metadata:
      name: default
      namespace: default
    spec:
      mtls:
        mode: STRICT
    
    kubectl apply -f mtls-strict.yaml
    

    Info

    Strict mtls can be enabled globally by setting the namespace to the name of the Istio root namespace, which by default is istio-system

  2. Verify that PeerAuthentication is applied.

    kubectl get peerauthentication
    

Verify that plain-text requests are no longer permitted📜

kubectl exec -n otherns $SLEEP_POD -it -- curl customers.default

The console output should indicate that the connection was reset by peer.

Security in-depth📜

Another important layer of security is to define an AuthorizationPolicy, in which we allow only specific services to communicate with others.

Any container can call the customers service or the web-frontend service.

  1. Capture the name of the sleep pod running in the default namespace.

    SLEEP_POD=$(kubectl get pod -l app=sleep -ojsonpath='{.items[0].metadata.name}')
    
  2. Call the customers service.

    kubectl exec $SLEEP_POD -it -- curl customers
    
  3. Call the web-frontend service.

    kubectl exec $SLEEP_POD -it -- curl web-frontend | head
    

Both calls succeed.

We wish to apply a policy in which only web-frontend is allowed to call customers, and only the ingress gateway can call web-frontend.

Study the below authorization policy.

authz-policy-customers.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allowed-customers-clients
  namespace: default
spec:
  selector:
    matchLabels:
        app: customers
  action: ALLOW
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/default/sa/web-frontend"]
  • The selector section specifies that the policy applies to the customers service.
  • The rules have a from: source: section indicating who is allowed in.
  • The nomenclature for the value of the principals field comes from the SPIFFE standard. Notice how it captures the service account name and namespace associated with the web-frontend service. This identity is associated with the x.509 certificate used by each service when making secure mTLS calls to one another.

Tasks:

  • [ ] Apply the policy to your cluster.
hint
kubectl apply -f authz-policy-customers.yaml
  • [ ] Verify that you are no longer able to reach the customers pod from the sleep pod.
hint
kubectl exec $SLEEP_POD -it -- curl customers
Info
Configuration may take a minute to take place and while this happens there might be requests to the customers service that might get through.

Challenge📜

Can you come up with a similar authorization policy for web-frontend?

  • Use a copy of the customers AuthorizationPolicy as a starting point.
  • Give the resource an apt name.
  • Revise the selector to match the web-frontend service.
  • Revise the rule to match the principal of the ingress gateway.

Hint

The ingress gateway has its own identity.

Here is a command which can help you find the name of the service account associated with its identity:

kubectl get pod -n istio-system -l istio=ingressgateway -o yaml | grep serviceAccountName

Use this service account name together with the namespace that the ingress gateway is running in to specify the value for the principals field.

Test it📜

Remember to verify that the policy is enforced.

  • Call both services again from the sleep pod and ensure communication is no longer allowed.
hint
kubectl exec $SLEEP_POD -it -- curl customers
kubectl exec $SLEEP_POD -it -- curl web-frontend | head
  • The console output should contain the message RBAC: access denied.
  • Test if you can still reach the web-frontend service.
hint
curl $GATEWAY_IP | head

Next📜

Next, we will learn about utilizing the traffic management capabilities of Istio.