By Kasun Talwatta Translator | Luga Lee Planning | Luga Lee
Service grid projects such as Istio have introduced many functions and advantages into our architecture, including more secure management of traffic between cluster micro services, service discovery, request routing and reliable communication between services.
Although Istio is a neutral platform, it has become one of the more popular service grids used with Kubernetes. Despite this popularity, it may be complex for novices in service grid to understand Istio's network and core mechanisms, such as:
1. Envoy Sidecar proxy injection
2. How does Sidecar intercept and route traffic
3. Distribute traffic management configuration
4. How do traffic rules take effect on the data plane

In the first article in a series of blogs that explain these mechanisms by analyzing Istio's architecture and implementation mechanisms, we will introduce Istio's network basics, data plane and control plane, network and Sidecar injection using Envoy agent. Using the demo environment, we will be able to see how Istio injects Init and Sidecar containers and how these containers are configured in the Pod template.
Istio network foundation
Istio's overview has been widely introduced in the official documents, but before we go further, we will focus on the key components.

Istio consists of two main parts: data plane and control plane.
Data plane: the data plane or data layer consists of a set of proxy services, which are represented as side car containers in each Kubernetes Pod, using an extended Envoy proxy server. These side cars mediate and control all network communication between microservices, and also collect and report useful telemetry data.
Control plane: the control plane or control layer consists of a binary file named istiod, which is responsible for converting advanced routing rules and flow control behaviors into Envoy specific configurations, and then propagating them to Sidecar at run time. In addition, the control plane provides security measures, realizes powerful service to service and end-user authentication through built-in identity and credential management, and implements security policies according to service identity.
Istio environment demo
Before continuing, let's create a local sandbox environment. This will ensure that we have deployed the Istio service grid in Kubernetes and run a sample application in the grid.
The required tools are as follows:
- minikube
- istioctl (Installed with curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.11.4 sh)
Steps to deploy Istio Service Grid:
1. Use the hyperkit driver to create 1.22.0 locally Version 2 of Kubernetes cluster. If you are using a non Mac OS X machine, you need to install virtualbox.
[administrator@JavaLangOutOfMemory ~ ] % minikube start --memory=4096 --cpus=2 --disk-size='20gb' --kubernetes-version=1.22.2 --driver=hyperkit -p istio-demo
2. After the cluster is fully started, execute the following command to set Istio.
# Deploy Istio operator [administrator@JavaLangOutOfMemory ~ ] % istioctl operator init # Inject operator configuration [administrator@JavaLangOutOfMemory ~ ] % cat << EOF | kubectl apply -f - apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: istio-control-plane namespace: istio-system spec: profile: minimal meshConfig: accessLogFile: /dev/stdout enableAutoMtls: true defaultConfig: proxyMetadata: # Enable basic DNS proxying ISTIO_META_DNS_CAPTURE: 'true' # Enable automatic address allocation ISTIO_META_DNS_AUTO_ALLOCATE: 'true' EOF
3. Deploy the sample application.
# Create apps namespace [administrator@JavaLangOutOfMemory ~ ] % kubectl create ns apps # Label apps namespace for sidecar auto injection [administrator@JavaLangOutOfMemory ~ ] % kubectl label ns apps istio-injection=enabled # Deploy a unprivileged sleep application [administrator@JavaLangOutOfMemory ~ ] % cat << EOF | kubectl apply -n apps -f - apiVersion: v1 kind: ServiceAccount metadata: name: sleep --- apiVersion: v1 kind: Service metadata: name: sleep labels: app: sleep service: sleep spec: ports: - name: http port: 80 selector: app: sleep --- apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: replicas: 1 selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: terminationGracePeriodSeconds: 0 serviceAccountName: sleep containers: - name: sleep image: curlimages/curl command: ["/bin/sleep", "3650d"] imagePullPolicy: IfNotPresent volumeMounts: - name: secret-volume mountPath: /etc/sleep/tls volumes: - name: secret-volume secret: secretName: sleep-secret optional: true EOF
4. Verify that the istio init and istio proxy containers are ready and running.
[administrator@JavaLangOutOfMemory ~ ] % kubectl get po -l app=sleep -n apps -o jsonpath='{range .items[*]}{range @.status.containerStatuses[*]}{.name},{"ready="}{.ready},{"started="}{.started}{"\n"}{end}{range @.status.initContainerStatuses[*]}{.name},{"ready="}{.ready},{"terminated="}{.state.terminated.reason}{end}' | sort
The results are shown as follows:
istio-init,ready=true,terminated=Completed istio-proxy,ready=true,started=true
Istio Sidecar container and Envoy agent
Sidecar injection is one of the key functions in Istio, which simplifies the process of adding and running additional containers as part of the Pod template. As part of this injection process, two additional containers are also provided:
1. Istio Init – this container configures iptables in the application Pod so that the Envoy agent (running as a separate container) can intercept inbound and outbound traffic. Before any other container starts, Kubernetes runs it as an Init container to initialize the network in the Pod. Note that allowing istio Init to manipulate iptables in kernel space does require upgrading Kubernetes permissions. Once the task is successfully completed, the container will automatically terminate. The Pod will not be ready until then. Note that in order to eliminate any security issues and operational challenges when deploying this container, istio introduces the CNI plug-in, so it can integrate directly with the underlying Kubernetes CNI without operating iptables.
2. Istio proxy – packaged as an extended version of upstream Envoy proxy. For a list of supported extensions, see the official documentation.

In depth inspection of Sidecar list
Let's take a look at the YAML listings of these two containers in our previously deployed application Pod.
[administrator@JavaLangOutOfMemory ~ ] % kubectl get po -l app=sleep -n apps -o yaml
We'll look at excerpts from the istio init and istio proxy containers.
istio-init Container:
initContainers: - name: istio-init image: docker.io/istio/proxyv2:1.11.4 imagePullPolicy: IfNotPresent args: - istio-iptables - -p - "15001" - -z - "15006" - -u - "1337" - -m - REDIRECT - -i - '*' - -x - "" - -b - '*' - -d - 15090,15021,15020 env: - name: ISTIO_META_DNS_AUTO_ALLOCATE value: "true" - name: ISTIO_META_DNS_CAPTURE value: "true" resources: limits: cpu: "2" memory: 1Gi requests: cpu: 100m memory: 128Mi securityContext: allowPrivilegeEscalation: false capabilities: add: - NET_ADMIN - NET_RAW drop: - ALL privileged: false readOnlyRootFilesystem: false runAsGroup: 0 runAsNonRoot: false runAsUser: 0
istio-proxy Container:
containers: - name: istio-proxy image: docker.io/istio/proxyv2:1.11.4 imagePullPolicy: IfNotPresent args: - proxy - sidecar - --domain - $(POD_NAMESPACE).svc.cluster.local - --proxyLogLevel=warning - --proxyComponentLogLevel=misc:error - --log_output_level=default:info - --concurrency - "2" ports: - name: http-envoy-prom containerPort: 15090 protocol: TCP readinessProbe: httpGet: path: /healthz/ready port: 15021 scheme: HTTP failureThreshold: 30 initialDelaySeconds: 1 periodSeconds: 2 successThreshold: 1 timeoutSeconds: 3 securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL privileged: false readOnlyRootFilesystem: true runAsGroup: 1337 runAsNonRoot: true runAsUser: 1337 env: - name: PROXY_CONFIG value: | {"proxyMetadata":{"ISTIO_META_DNS_AUTO_ALLOCATE":"true","ISTIO_META_DNS_CAPTURE":"true"}} - name: ISTIO_META_DNS_AUTO_ALLOCATE value: "true" - name: ISTIO_META_DNS_CAPTURE value: "true" ...
There are some interesting key points to note in these additional parameters:
Both containers are served by the same image: docker io/istio/proxyv2:1.11. What does this mean and how does it work? Istio iptables and proxy (under args) commands are loaded into the pilot agent binary in the image. Therefore, if we run the pilot agent binary in the istio proxy container, we will see the following:
kubectl exec $(kubectl get po -l app=sleep -n apps -o jsonpath="{.items[0]. metadata.name}") -n apps -c istio-proxy --pilot-agent
Istio Pilot agent runs in the sidecar or gateway container and bootstraps Envoy. Usage: pilot-agent [command] Available Commands: completion generate the autocompletion script for the specified shell help Help about any command istio-clean-iptables Clean up iptables rules for Istio Sidecar istio-iptables Set up iptables rules for Istio Sidecar proxy XDS proxy agent request Makes an HTTP request to the Envoy admin API version Prints out build version information wait Waits until the Envoy proxy is ready
To minimize the attack surface, the securityContext section in the Istio init container (which is part of the PodSecurityContext object) indicates that the container runs with root permission (runAsUser: 0), but except NET_ADMIN and NET_RAW function. These capabilities provide runtime permissions for the Istio init init container to override the iptables of the application Pod. This is described in more detail in the Istio documentation.
allowPrivilegeEscalation: false capabilities: add: - NET_ADMIN - NET_RAW drop: - ALL privileged: false readOnlyRootFilesystem: false runAsGroup: 0 runAsNonRoot: false runAsUser: 0
On the other hand, the istio proxy container runs with the restricted privileges of 1337 users. Since this is reserved, the UID (user ID) of the application workload must be different and must not conflict with 1337. 1337 UID has been arbitrarily selected. Istio team bypasses traffic and redirects to istio proxy container. We can also see that 1337 is used as a parameter of istio iptables when initializing iptables. Since this container runs actively with the application workload, istio also ensures that if it is threatened, it can only have read-only access to the root file system.
allowPrivilegeEscalation: false capabilities: drop: - ALL privileged: false readOnlyRootFilesystem: true runAsGroup: 1337 runAsNonRoot: true runAsUser: 1337
The istio proxy container runs with the ready probe shown below. Kubelet in Kubernetes uses this ready probe to determine whether istio proxy is ready to accept traffic. Kubelet will recognize that the Pod is ready only when the istio proxy container and all corresponding application containers are running and the health probe has been successfully executed. If the / health / ready handler of the server path (defined in the pilot agent source code) returns a successful return code, kubelet will assume that the container is active and healthy. The failureThreshold configuration specifies the number of consecutive times this ready probe can fail before the container is marked as not ready.
readinessProbe: httpGet: path: /healthz/ready port: 15021 scheme: HTTP initialDelaySeconds: 1 failureThreshold: 30 periodSeconds: 2 successThreshold: 1 timeoutSeconds: 3
Sidecar injection analysis
Istio injects the Sidecar agent into the application workload in two different ways: manual and automatic. Both methods follow the same injection principle. Given "some" application workloads (which can be defined as higher-level Kubernetes resources, such as deployment, Statefulset, daemon set and even Pod), Kubernetes is allowed to inject Sidecar container templates and configuration parameters (isto Sidecar injector configmap) using Sidecar.
Manual side car injection in Istio
Of the two methods, this is the easiest to understand. Manual injection is done by the istioctl command using the Kube inject parameter. We can use any of the following formats for injection:
[administrator@JavaLangOutOfMemory ~ ] % istioctl kube-inject -f application.yaml | kubectl apply -f -
or
[administrator@JavaLangOutOfMemory ~ ] % kubectl apply -f <(istioctl kube-inject -f application.yaml)
When istioctl Kube inject is used to inject Sidecar, it will use the intra cluster configuration written as istio Sidecar injector kubernetes configmap by default. There are many flags that we can specify to customize this behavior:
--injectConfigFile string Injection configuration filename. Cannot be used with --injectConfigMapName --meshConfigFile string Mesh configuration filename. Takes precedence over --meshConfigMapName if set --meshConfigMapName string ConfigMap name for Istio mesh configuration, key should be "mesh" (default "istio") --injectConfigMapNam string ConfigMap name for Istio sidecar injection, key should be "config" (default "istio-sidecar-injector")
Please note that, - injectConfigMapNam is a hidden flag in istioctl Kube inject, which allows us to override Sidecar injection configuration in the cluster. Alternatively, you can use the configured local copy and the above flag to complete the injection:
[administrator@JavaLangOutOfMemory ~ ] % kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml [administrator@JavaLangOutOfMemory ~ ] % kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml [administrator@JavaLangOutOfMemory ~ ] % kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml [administrator@JavaLangOutOfMemory ~ ] % istioctl kube-inject \ --injectConfigFile inject-config.yaml \ --meshConfigFile mesh-config.yaml \ --valuesFile inject-values.yaml \ --filename application.yaml \ | kubectl apply -f -
During manual injection, care must be taken not to destroy Sidecar, especially when using custom configuration.
Automatic side car injection in Istio
This is considered to be the de facto method of injecting side cars into Istio. This involves fewer configuration steps than the manual method; However, it depends on whether the underlying Kubernetes distribution enables support for admission controllers. To this end, Istio uses a variant webhook admission controller.

The following is the process of Kubernetes mutation admission controller in Sidecar injection:
1. First, the Istio sidecar injector mutating configuration injected during Istio installation (as shown below) sends a webhook request with all Pod information to the Istio controller.
2. Next, the controller modifies the Pod specification at runtime and introduces Init and Sidecar container agent into the actual Pod specification.
3. Then, the controller returns the modified object to Webhook for object verification.
4. Finally, after verification, the modified Pod specification is deployed with all Sidecar containers.
For the complete configuration, see kubectl get mutatingwebhookconfiguration istio sidecar injector - O yaml. For brevity, only two of the four webhook configurations are given in the following excerpt:
apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: istio-sidecar-injector webhooks: - admissionReviewVersions: - v1beta1 - v1 clientConfig: caBundle: cert service: name: istiod namespace: istio-system path: /inject port: 443 failurePolicy: Fail matchPolicy: Equivalent name: namespace.sidecar-injector.istio.io namespaceSelector: matchExpressions: - key: istio-injection operator: In values: - enabled objectSelector: matchExpressions: - key: sidecar.istio.io/inject operator: NotIn values: - "false" reinvocationPolicy: Never rules: - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods scope: '*' sideEffects: None timeoutSeconds: 10 - admissionReviewVersions: - v1beta1 - v1 clientConfig: caBundle: cert service: name: istiod namespace: istio-system path: /inject port: 443 failurePolicy: Fail matchPolicy: Equivalent name: namespace.sidecar-injector.istio.io namespaceSelector: matchExpressions: - key: istio-injection operator: In values: - enabled objectSelector: matchExpressions: - key: sidecar.istio.io/inject operator: NotIn values: - "false" reinvocationPolicy: Never rules: - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods scope: '*' sideEffects: None timeoutSeconds: 10 - admissionReviewVersions: - v1beta1 - v1 clientConfig: caBundle: cert service: name: istiod namespace: istio-system path: /inject port: 443 failurePolicy: Fail matchPolicy: Equivalent name: object.sidecar-injector.istio.io namespaceSelector: matchExpressions: - key: istio-injection operator: DoesNotExist - key: istio.io/rev operator: DoesNotExist objectSelector: matchExpressions: - key: sidecar.istio.io/inject operator: In values: - "true" - key: istio.io/rev operator: DoesNotExist reinvocationPolicy: Never rules: - apiGroups: - "" apiVersions: - v1 operations: - CREATE resources: - pods scope: '*' sideEffects: None timeoutSeconds: 10
This configuration tells the Kubernetes mutation controller to safely send requests to the / inject endpoint of the istiod service on the HTTPS port. Before calling mutating webhook, Kubernetes checks whether the requesting user has the right to make the request. In Istio, webhook is implemented as part of the istiod binary.
Injection can be triggered using namespace level tags (istio injection = enabled) or as annotations at the object level (sidecar.istio.io/inject="true"). Each webhook configuration defines the matching rules for these triggers in the namespaceSelector and objectSelector. When injecting tags defined based on the namespace level, any Deployment objects (Deployment, StatefulSet, DaemonSet) created in the namespace will be mutated using the Sidecar agent. The following is a summary of the matching rules:
aop namespace tag | Object annotation | Sidecar injection (Y or N) |
---|---|---|
istio-injection=enabled | ||
sidecar.istio.io/inject="true" | ||
istio-injection=enabled | sidecar.istio.io/inject="true" | |
istio-injection=enabled | sidecar.istio.io/inject="false" | |
istio-injection=disabled | sidecar.istio.io/inject="true" | |
istio-injection=disabled | sidecar.istio.io/inject="false" |
When the pod list is injected, the pod object can also be changed directly (if the namespace has no label). Pod list must have a label sidecar istio. io/inject="true". For example:
apiVersion: v1 kind: Pod metadata: name: sleep namespace: apps labels: app: sleep sidecar.istio.io/inject: "true" ...
So far, we have learned about Istio's basic network knowledge, data plane and control plane, network and Sidecar injection using Envoy agent, how Istio uses demonstration to inject Init and Sidecar containers, and the configuration environment of these containers in Pod template. In the next blog, we will analyze how iptables are configured and managed.
Solo on Slack Contact us on Istio for more information about Istio and our products. We also offer many webinars and seminars about Istio, so please feel free to register for more information.
Original text: https://www.solo.io/blog/istios-networking-in-depth/?utm_source=thenewstack&utm_medium=website&utm_campaign=platform