Skip to content

Instantly share code, notes, and snippets.

@electricjesus
Last active December 6, 2025 15:11
Show Gist options
  • Select an option

  • Save electricjesus/34651e4888c03a19c0a24a1be12b572b to your computer and use it in GitHub Desktop.

Select an option

Save electricjesus/34651e4888c03a19c0a24a1be12b572b to your computer and use it in GitHub Desktop.
Calico + Istio 1.28 App Layer Policy Setup Guide (IstioOperator approach)

Calico + Istio 1.28 App Layer Policy - Step-by-Step Guide

This guide shows how to set up Calico Application Layer Policy (ALP) with Istio 1.28 using the modern IstioOperator approach.

Prerequisites

  • Kubernetes cluster (1.29+)
  • Calico 3.31.1 installed
  • Istio 1.28.1 installed
  • kubectl and istioctl CLI tools

Step 1: Enable Calico Policy Sync API

The Policy Sync API allows Dikastes to communicate with Felix for policy decisions.

kubectl patch felixconfiguration default --type merge -p '{"spec":{"policySyncPathPrefix":"/var/run/nodeagent"}}'

Verify:

kubectl get felixconfiguration default -o yaml | grep policySyncPathPrefix

Step 2: Create IstioOperator Manifest with Dikastes Templates

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

  values:
    sidecarInjectorWebhook:
      templates:
        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
            # This allows Envoy to communicate with Dikastes via Unix socket
            initContainers:
            - name: istio-proxy
              volumeMounts:
              - mountPath: /var/run/dikastes
                name: dikastes-sock
            volumes:
            - name: dikastes-sock
              emptyDir:
                medium: Memory
            - name: felix-sync
              csi:
                driver: csi.tigera.io

        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
            # IMPORTANT: Patch istio-proxy to mount dikastes socket
            initContainers:
            - name: istio-proxy
              volumeMounts:
              - mountPath: /var/run/dikastes
                name: dikastes-sock
            volumes:
            - name: dikastes-sock
              emptyDir:
                medium: Memory
            - name: felix-sync
              csi:
                driver: csi.tigera.io

Step 3: Apply IstioOperator Configuration

Update your Istio installation with the Dikastes templates:

istioctl install -f istio-operator-dikastes.yaml -y

This will update the istio-sidecar-injector ConfigMap with the custom templates.

Step 4: Verify Dikastes Templates

Check that the templates were loaded:

kubectl get configmap -n istio-system istio-sidecar-injector -o yaml | grep "dikastes:" -A 5

You should see both dikastes and dikastes-gateway templates.

Step 5: Deploy Calico Authorization Service

Deploy the EnvoyFilter and ServiceEntry for Dikastes integration:

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.2/manifests/alp/istio-app-layer-policy-envoy-v3.yaml

This configures:

  • ServiceEntry: dikastes.calico.cluster.local for Unix socket communication
  • DestinationRule: Disables mTLS for local socket
  • EnvoyFilter: Configures Envoy's ext_authz filter to call Dikastes

Step 6: Create Test Namespace

kubectl create namespace bookinfo
kubectl label namespace bookinfo istio-injection=enabled

Step 7: Deploy Test Application with Dikastes

Create test-app.yaml:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
  namespace: bookinfo
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  namespace: bookinfo
  labels:
    app: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
      annotations:
        # KEY: Inject both Istio sidecar AND Dikastes sidecar
        inject.istio.io/templates: sidecar,dikastes
    spec:
      serviceAccountName: httpbin
      containers:
      - name: httpbin
        image: kennethreitz/httpbin
        ports:
        - containerPort: 80
---
# Client pod for testing
apiVersion: v1
kind: ServiceAccount
metadata:
  name: client
  namespace: bookinfo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: client
  namespace: bookinfo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: client
  template:
    metadata:
      labels:
        app: client
      annotations:
        inject.istio.io/templates: sidecar
    spec:
      serviceAccountName: client
      containers:
      - name: client
        image: curlimages/curl:latest
        command: ["/bin/sh"]
        args: ["-c", "while true; do sleep 30; done"]

Deploy:

kubectl apply -f test-app.yaml

Step 8: Verify Dikastes Injection

Wait for pods to be ready:

kubectl wait --for=condition=ready pod -l app=httpbin -n bookinfo --timeout=180s

Check that Dikastes container was injected:

kubectl get pod -l app=httpbin -n bookinfo -o jsonpath='{.items[0].spec.containers[*].name}'

Expected output: dikastes httpbin

Check pod status (should show 3/3 containers):

kubectl get pods -n bookinfo

The three containers are:

  1. dikastes - Calico L7 policy enforcer
  2. httpbin - Application
  3. istio-proxy - Envoy sidecar (as native Kubernetes sidecar)

Step 9: Verify Dikastes is Running

Check Dikastes logs:

kubectl logs -n bookinfo -l app=httpbin -c dikastes

You should see:

Successfully connected to Policy Sync server
Starting synchronization with Policy Sync server

Step 10: Test L7 Authorization

Test that Dikastes is enforcing authorization:

kubectl exec -n bookinfo deploy/client -- curl -s -o /dev/null -w "%{http_code}\n" http://httpbin:8000/get

Expected: 403 (Forbidden)

This confirms Dikastes is actively enforcing L7 authorization with default-deny behavior.

Understanding the Architecture

How It Works

  1. Istio Proxy (Envoy) intercepts all HTTP traffic
  2. ext_authz filter forwards authorization checks to Dikastes via Unix socket
  3. Dikastes queries Felix for policy decisions via CSI-mounted socket
  4. Felix returns allow/deny based on Calico policies
  5. Envoy allows or blocks the request

Container Communication

Envoy → /var/run/dikastes/dikastes.sock → Dikastes
Dikastes → /var/run/felix/nodeagent/socket → Felix (via CSI driver)

Kubernetes Native Sidecars

Istio 1.28 uses Kubernetes 1.29+ native sidecar support where istio-proxy is an init container with restartPolicy: Always. This provides:

  • Guaranteed startup ordering
  • Automatic restart on failure
  • Proper lifecycle management

Key Annotation

To inject Dikastes into your workloads, add this annotation:

metadata:
  annotations:
    inject.istio.io/templates: sidecar,dikastes

For gateway workloads, use:

metadata:
  annotations:
    inject.istio.io/templates: sidecar,dikastes-gateway

Troubleshooting

Dikastes not injected

Check that namespace has Istio injection enabled:

kubectl get namespace bookinfo -o yaml | grep istio-injection

Verify templates in ConfigMap:

kubectl get configmap -n istio-system istio-sidecar-injector -o yaml | grep dikastes

Pod stuck in PodInitializing

Check pod events:

kubectl describe pod -n bookinfo -l app=httpbin

Check Dikastes logs:

kubectl logs -n bookinfo -l app=httpbin -c dikastes

403 errors for all requests

This is expected behavior! Dikastes defaults to deny-all when no ApplicationLayerPolicy is configured. This is a secure-by-default posture.

To allow traffic, you need to create Calico policies (specific policy configuration depends on your Calico distribution).

IstioOperator Benefits

Compared to the old ConfigMap patching approach:

  • Declarative: Full YAML manifest in version control
  • Upgrade-safe: Survives Istio upgrades
  • GitOps-friendly: Use istioctl manifest generate to generate manifests
  • Maintainable: Easy to review and modify
  • Documented: Clear intent in IstioOperator spec

Next Steps

  1. Create ApplicationLayerPolicy resources to define allow rules
  2. Test HTTP method filtering (GET, POST, etc.)
  3. Test path-based authorization
  4. Implement identity-based access control

References


Version: Tested with Calico 3.31.2 and Istio 1.28.1 Date: 2025-12-06

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment