You can enforce Calico network policy for Istio application layer policy using the Dikastes sidecar. Dikastes enables Calico to integrate with Istio's Envoy proxy to enforce fine-grained Layer 7 (HTTP) policies.
- Pod traffic controls for Istio-enabled apps: Lets you restrict ingress traffic inside and outside pods and mitigate common threats to Istio-enabled apps.
- Security alignment with zero trust: Supports zero-trust network models through traffic encryption, multiple enforcement points, and multiple identity criteria for authentication.
- Familiar policy language: Apply Kubernetes network policies and Calico network policies that you already know.
Required
- Calico CNI is installed and configured
kubectlandistioctlCLI tools are installed- MutatingAdmissionWebhook admission controller is enabled
Supported Istio versions
- Istio v1.28.1 (tested and verified - recommended)
- Istio v1.18+ (should work with the IstioOperator approach)
- Legacy: Istio v1.15.2, v1.10.2 (use ConfigMap patching method - see Legacy Installation)
Note: Istio v1.9.x and lower are not supported.
Recommended Kubernetes versions
- Kubernetes v1.29+: Required for native sidecar support used by Istio 1.22+
This guide covers the modern IstioOperator approach which is declarative, version-controllable, and upgrade-safe.
For the legacy ConfigMap patching approach, see Legacy Installation.
Enable the Policy Sync API in Felix to allow Dikastes to query policy decisions.
Using kubectl:
kubectl patch felixconfiguration default --type merge -p '{"spec":{"policySyncPathPrefix":"/var/run/nodeagent"}}'Using calicoctl:
calicoctl patch FelixConfiguration default --patch \
'{"spec": {"policySyncPathPrefix": "/var/run/nodeagent"}}'Optional: If using the Calico operator, disable deprecated flexvolumes:
kubectl patch installation default --type=merge -p '{"spec": {"flexVolumePath": "None"}}'The CSI driver mounts the Felix Policy Sync socket into Dikastes containers.
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/csi-driver.yamlVerify the CSI driver is running:
kubectl get pods -n calico-system -l k8s-app=csi-node-driverFollow the upstream Istio installation documentation to install Istio if not already installed.
For testing, you can use:
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.28.1 sh -
cd istio-1.28.1
export PATH=$PWD/bin:$PATHNote: Do not run
istioctl installyet if you want to configure Dikastes templates during installation. See the next step.
Create a file named istio-operator-dikastes.yaml:
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-with-dikastes
namespace: istio-system
spec:
profile: minimal # or 'default' if you need gateways
values:
sidecarInjectorWebhook:
templates:
# Template for workload pods
dikastes: |
spec:
containers:
- name: dikastes
image: quay.io/calico/dikastes:v3.31.2
args:
- server
- -l
- /var/run/dikastes/dikastes.sock
- -d
- /var/run/felix/nodeagent/socket
securityContext:
allowPrivilegeEscalation: false
runAsGroup: 999
runAsNonRoot: true
runAsUser: 999
livenessProbe:
exec:
command:
- /healthz
- liveness
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
exec:
command:
- /healthz
- readiness
initialDelaySeconds: 3
periodSeconds: 3
volumeMounts:
- mountPath: /var/run/dikastes
name: dikastes-sock
- mountPath: /var/run/felix
name: felix-sync
volumes:
- name: dikastes-sock
emptyDir:
medium: Memory
- name: felix-sync
csi:
driver: csi.tigera.io
# Template for gateway pods (runs as root)
dikastes-gateway: |
spec:
containers:
- name: dikastes
image: quay.io/calico/dikastes:v3.31.2
args:
- server
- -l
- /var/run/dikastes/dikastes.sock
- -d
- /var/run/felix/nodeagent/socket
securityContext:
allowPrivilegeEscalation: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
livenessProbe:
exec:
command:
- /healthz
- liveness
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
exec:
command:
- /healthz
- readiness
initialDelaySeconds: 3
periodSeconds: 3
volumeMounts:
- mountPath: /var/run/dikastes
name: dikastes-sock
- mountPath: /var/run/felix
name: felix-sync
volumes:
- name: dikastes-sock
emptyDir:
medium: Memory
- name: felix-sync
csi:
driver: csi.tigera.ioInstall or update Istio with the Dikastes templates:
istioctl install -f istio-operator-dikastes.yaml -yThis will:
- Install Istio (if not already installed)
- Update the
istio-sidecar-injectorConfigMap with the Dikastes templates - Make the templates available for pod annotation-based injection
Verify the templates were loaded:
kubectl get configmap -n istio-system istio-sidecar-injector -o yaml | grep "dikastes:" -A 5You should see both dikastes and dikastes-gateway templates.
Benefits of the IstioOperator approach:
- ✅ Declarative: Full configuration in a version-controlled YAML file
- ✅ Upgrade-safe: Templates persist across Istio upgrades
- ✅ GitOps-friendly: Use
istioctl manifest generateto generate Kubernetes manifests - ✅ Maintainable: Easy to review, modify, and understand
If you prefer to generate Kubernetes manifests instead of applying directly:
istioctl manifest generate -f istio-operator-dikastes.yaml > istio-with-dikastes.yaml
kubectl apply -f istio-with-dikastes.yamlConfigure Istio's Envoy proxies to use Dikastes as an external authorization service.
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/alp/istio-app-layer-policy-envoy-v3.yamlThis manifest creates:
- ServiceEntry: Defines
dikastes.calico.cluster.localfor Unix socket communication - DestinationRule: Disables mTLS for local socket communication
- EnvoyFilter: Configures Envoy's
ext_authzfilter to call Dikastes
Label the namespace where you want to deploy Istio-enabled workloads:
kubectl label namespace <your-namespace> istio-injection=enabledTo inject the Dikastes sidecar into your pods, add the following annotation to your pod template:
For regular workloads:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
metadata:
annotations:
inject.istio.io/templates: sidecar,dikastes
spec:
# ... your pod specFor gateway workloads:
metadata:
annotations:
inject.istio.io/templates: sidecar,dikastes-gatewayThe key difference:
dikastes: Runs as non-root user (999) - for application workloadsdikastes-gateway: Runs as root (0) - for ingress/egress gateways
After deploying a workload with the annotation:
# Check that Dikastes container is present
kubectl get pod -l app=<your-app> -n <your-namespace> -o jsonpath='{.items[0].spec.containers[*].name}'You should see: dikastes <your-app-container> ...
Check pod status (should show all containers ready):
kubectl get pods -n <your-namespace>Check Dikastes logs:
kubectl logs -n <your-namespace> -l app=<your-app> -c dikastesYou should see:
Successfully connected to Policy Sync server
Starting synchronization with Policy Sync server
For enhanced security, enable strict mutual TLS between services:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default-strict-mode
namespace: istio-system
spec:
mtls:
mode: STRICTApply:
kubectl apply -f <filename>.yamlWhen a request flows through an Istio-enabled pod with Dikastes:
- Istio Proxy (Envoy) intercepts the HTTP request
- ext_authz filter sends an authorization check to Dikastes via Unix socket (
/var/run/dikastes/dikastes.sock) - Dikastes queries Felix for policy decisions via the CSI-mounted socket (
/var/run/felix/nodeagent/socket) - Felix evaluates Calico network policies and returns allow/deny
- Dikastes responds to Envoy with the authorization decision
- Envoy allows or blocks the request based on the decision
HTTP Request → Envoy → ext_authz → Dikastes → Felix Policy Sync API → Policy Decision
Istio 1.22+ (including 1.28) uses Kubernetes 1.29+ native sidecar support. The istio-proxy runs as an init container with restartPolicy: Always, which provides:
- ✅ Guaranteed startup ordering (sidecar starts before application)
- ✅ Automatic restart on failure
- ✅ Proper lifecycle management
This is why you'll see the istio-proxy in init containers but the pod shows 3/3 containers running.
With Dikastes deployed, you can now create Calico network policies to enforce Layer 7 access control.
Note: The specific policy CRDs available depend on your Calico distribution (OSS vs Enterprise). Consult your Calico documentation for policy syntax.
Example conceptual policy (syntax may vary):
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-http-get
namespace: default
spec:
selector: app == "myapp"
types:
- Ingress
ingress:
- action: Allow
protocol: TCP
destination:
ports:
- 80Without explicit allow policies, Dikastes enforces a default-deny posture for security. All HTTP requests will receive a 403 Forbidden response until you create policies that explicitly allow traffic.
Check namespace label:
kubectl get namespace <your-namespace> -o yaml | grep istio-injectionShould show: istio-injection: enabled
Check pod annotation:
kubectl get pod <pod-name> -n <your-namespace> -o yaml | grep inject.istio.io/templatesShould show: inject.istio.io/templates: sidecar,dikastes
Verify templates in ConfigMap:
kubectl get configmap -n istio-system istio-sidecar-injector -o yaml | grep -c "dikastes:"Should return 2 (one for each template).
Check pod events:
kubectl describe pod <pod-name> -n <your-namespace>Check Dikastes logs:
kubectl logs <pod-name> -n <your-namespace> -c dikastesCheck CSI driver:
kubectl get pods -n calico-system -l k8s-app=csi-node-driverAll CSI driver pods should be Running.
Verify Felix Policy Sync API:
kubectl get felixconfiguration default -o yaml | grep policySyncPathPrefixShould show: policySyncPathPrefix: /var/run/nodeagent
This is expected behavior when no allow policies are configured. Dikastes enforces default-deny for security.
To allow traffic, create Calico network policies that explicitly permit the desired traffic.
If you have egress policies applied to your pods, Envoy needs access to the Istio control plane.
Apply the allow policy:
kubectl apply -f https://docs.tigera.io/files/allow-istio-pilot.yamlIf you see this warning during installation:
detected Calico CNI with 'bpfConnectTimeLoadBalancing=TCP';
this must be set to 'bpfConnectTimeLoadBalancing=Disabled'
This may affect connection-level load balancing but does not prevent basic functionality. For production deployments, consider adjusting the Calico configuration as recommended.
Note: This method is supported but not recommended for new deployments. Use the IstioOperator approach instead.
If you need to use the legacy ConfigMap patching method for Istio 1.15 or 1.10:
curl https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/alp/istio-inject-configmap-1.15.yaml -o istio-inject-configmap.yaml
kubectl patch configmap -n istio-system istio-sidecar-injector --patch "$(cat istio-inject-configmap.yaml)"curl https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/alp/istio-inject-configmap-1.10.yaml -o istio-inject-configmap.yaml
kubectl patch configmap -n istio-system istio-sidecar-injector --patch "$(cat istio-inject-configmap.yaml)"Drawbacks of ConfigMap patching:
- ❌ Not declarative (imperative command)
- ❌ Not version-controlled
- ❌ Must be re-applied after Istio upgrades
- ❌ Difficult to audit and review
- Calico Application Layer Policy
- Istio Documentation
- IstioOperator API Reference
- Kubernetes Native Sidecars
Last Updated: 2025-12-06 Tested Versions: Calico v3.31.2, Istio v1.28.1, Kubernetes v1.33