Istio’s authorization policies don’t just control who can access what, they control how and when, often in ways that fundamentally change how you think about network security.

Let’s see this in action. Imagine we have a frontend service that needs to talk to a backend service. By default, Istio allows all traffic between services within the mesh. We want to restrict this.

First, let’s set up a simple scenario with two services, frontend and backend, deployed in the default namespace.

# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: nginx # Replace with your actual frontend image
        ports:
        - containerPort: 80
---
# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: httpd # Replace with your actual backend image
        ports:
        - containerPort: 80

Apply these:

kubectl apply -f frontend-deployment.yaml
kubectl apply -f backend-deployment.yaml

Now, we need to ensure Istio’s sidecars are injected. If you’re running Istio, this usually happens automatically via namespace labeling:

kubectl label namespace default istio-injection=enabled

Wait a few moments for the pods to restart with the sidecars.

Without any authorization policy, frontend can talk to backend. We can verify this by exec-ing into the frontend pod and trying to curl the backend service:

kubectl exec -it $(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') -- curl http://backend.default.svc.cluster.local

This should return a successful response from the backend.

Now, let’s introduce an AuthorizationPolicy to deny all traffic to the backend service by default.

# deny-all-backend.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all-backend
  namespace: default
spec:
  selector:
    matchLabels:
      app: backend
  action: DENY
  rules:
  - {} # An empty rule denies everything

Apply this policy:

kubectl apply -f deny-all-backend.yaml

If you try the curl command again from the frontend pod, it will now fail with a 403 Forbidden error. This is Istio’s sidecar intercepting the request and enforcing the policy.

The mental model here is that Istio’s AuthorizationPolicy is applied to the destination workload (the backend service in this case), and the sidecar on the destination intercepts incoming requests. The policy then dictates whether to ALLOW, DENY, or AUDIT the request based on a set of rules.

The selector in the policy targets the workload(s) it applies to. action specifies what to do if a rule matches. rules are composed of from, to, and when clauses.

  • from: Specifies the source of the request (e.g., specific services, namespaces, principals).
  • to: Specifies the operation being performed (e.g., HTTP methods, paths, gRPC methods).
  • when: Adds more complex conditions, often based on request properties or JWT claims.

Crucially, if any AuthorizationPolicy selects a workload, then only requests that match a rule in an ALLOW policy (or are explicitly audited) will be permitted. If no ALLOW policy matches, and there’s a DENY policy, the request is denied. If there are multiple ALLOW policies, the request must match at least one. If there are multiple DENY policies, the request must not match any.

Let’s refine our policy to allow frontend to access backend only on GET requests to /api/v1/data.

# allow-frontend-get-data.yaml
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: allow-frontend-get-data
  namespace: default
spec:
  selector:
    matchLabels:
      app: backend
  action: ALLOW
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/default/sa/frontend"] # Assuming frontend pod has 'frontend' SA
    to:
    - operation:
        methods: ["GET"]
        paths: ["/api/v1/data"]

First, we need to ensure the frontend service account has a principal that Istio can recognize. If you created your frontend deployment with a specific serviceAccountName, make sure it’s frontend. If not, Istio uses the default service account for the namespace. For this example, let’s assume the frontend pod runs with the frontend service account. If you haven’t created one, you’ll need to:

kubectl create serviceaccount frontend -n default
# Then update your frontend deployment to use this SA

And then re-apply your frontend deployment.

Now, apply the ALLOW policy:

kubectl apply -f allow-frontend-get-data.yaml

With this policy in place, the deny-all-backend.yaml policy is still active. However, because we now have an ALLOW policy that matches specific conditions, Istio will evaluate it.

  • A GET request from frontend to /api/v1/data on backend will be allowed.
  • A POST request from frontend to /api/v1/data will be denied (because it doesn’t match the GET method in the ALLOW rule).
  • A GET request from frontend to /api/v1/other will be denied (because it doesn’t match /api/v1/data in the ALLOW rule).
  • A request from any other service to backend will also be denied by the deny-all-backend.yaml policy, as it doesn’t match the from clause of our allow-frontend-get-data.yaml policy.

The most surprising part is how principals work. They aren’t just strings; they are Istio’s representation of a workload’s identity, typically derived from its Kubernetes Service Account. The format cluster.local/ns/<namespace>/sa/<serviceaccountname> is the default. This means you’re not just authorizing based on IP addresses or network segments, but on the actual identity of the workload within the mesh, which is a much stronger security guarantee.

The next step is to explore how to use JWT validation within Istio authorization policies for even more robust authentication and authorization.

Want structured learning?

Take the full Istio course →