Implementing Zero Trust with Istio Authorization Policies
Posted on November 4, 2025 by Matthew McLeod
Service Interaction Diagram
The concept of a secure network perimeter is becoming obsolete. In a modern, cloud-native world, we must adopt a Zero Trust security model, which assumes that threats can exist both inside and outside the network.
The core principle is simple: never trust, always verify.
Istio, a powerful open-source service mesh, provides the tools to implement Zero Trust principles at the service level. Using Istio’s AuthorizationPolicy, you can define fine-grained access control between workloads deciding who can talk to whom, which methods are allowed, and under what identity.
Think of it like the compartmentalized hull of a ship: if one section is breached, watertight doors seal it off to prevent the entire vessel from flooding. In Istio, each service becomes its own compartment, or micro-perimeter.
Scenario Overview
In this walkthrough, you’ll:
- Start a Minikube cluster with Istio installed,
- Deploy two BusyBox pods starboard and portside,
- Apply a default-deny policy,
- Then explicitly allow only one-way communication from starboard to portside.
This setup models a Zero Trust ship where only one compartment can talk to another through a sealed hatch.
Step 0: Start Minikube
First, start a local Kubernetes cluster with enough resources for Istio:
Check that it’s running:
You should see your Minikube node in Ready state.
Step 1: Install Istio
Download and install Istio (v1.22+ recommended):
Install the Istio control plane using the demo profile:
Label the default namespace for automatic sidecar injection:
Step 2: Deploy Two BusyBox Pods
Deploy your “ship compartments” starboard and portside:
Expose portside as a service so it has a stable address:
Verify both are running:
You should see starboard and portside pods up and running.
Step 3: Enforce a Namespace-Wide Default Deny Policy
By default, Istio allows all traffic between workloads. To enforce Zero Trust, we start by denying everything.
Create a file called deny-all.yaml:
Apply it:
What This Does
- Applies to all workloads in the default namespace.
- Since there are no rules, it denies every request by default.
- This sets your Zero Trust baseline. Nothing talks until explicitly allowed.
You can test it:
Expected output: wget: error getting response: Connection refused
Step 4: Allow Starboard to Communicate with Portside
Now, let’s open one “watertight door”: only the starboard service account can make GET requests to portside.
Create allow-starboard-to-portside.yaml:
Apply it:
Step 5: Test Connectivity
Run a request from starboard to portside:
You should now see a 200 OK response (or an empty connection, but no error).
Now test in the opposite direction (portside to starboard):
Expected output: wget: error getting response: Connection refused
Only one-way access is allowed our “ship compartment” logic is working.
Step 6: Verify the Active Policies
Check which AuthorizationPolicies are active:
Step 7: Clean Up
When you’re done experimenting:
Key Takeaways
This pattern deny everything, allow by necessity is the foundation of Zero Trust in Istio.
In summary:
- Start with a namespace-wide default-deny baseline.
- Add explicit ALLOW policies for legitimate paths.
- Treat each service as a compartment strong boundaries, minimal trust.
This not only enforces Zero Trust but also builds a clear audit trail of every permitted communication path. Configuration, not hope, becomes your security model.