Linux Kernel Compatibility Notice

For nodes running Linux kernel versions earlier than 4.11 (e.g., CentOS 7), additional configuration is required prior to gateway installation. There are two scenarios depending on whether you need to listen on ports below 1024:

  • Scenario 1: Kernel versions earlier than 4.11 only — The gateway listens on ports 1024 or above. You need to set sysctls: [] to remove the default net.ipv4.ip_unprivileged_port_start sysctl, which is not available on kernels earlier than 4.11.
  • Scenario 2: Kernel versions earlier than 4.11 + privileged ports — The gateway needs to listen on ports below 1024. In addition to the sysctls: [] change from Scenario 1, you must also add the NET_BIND_SERVICE capability to allow binding to privileged ports.
INFO

Skip this section if your kernel version is 4.11 or later.

Istio Gateway

Prerequisites

  • Install jq locally to process JSON in these steps.

Procedure

Scenario 1: Kernel versions earlier than 4.11 only (ports ≥ 1024)

  1. Create a YAML file named gateway-injection-template.txt that contains the default injection template for gateways.

    Click to expand
    gateway-injection-template.txt
    {{- $containers := list }}
    {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}}
    metadata:
      labels:
        service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name  | quote }}
        service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest"  | quote }}
      annotations:
        istio.io/rev: {{ .Revision | default "default" | quote }}
        {{- if ge (len $containers) 1 }}
        {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }}
        kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}"
        {{- end }}
        {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }}
        kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}"
        {{- end }}
        {{- end }}
    spec:
      securityContext:
      {{- if .Values.gateways.securityContext }}
        {{- toYaml .Values.gateways.securityContext | nindent 4 }}
      {{- else }}
        sysctls: []  
      {{- end }}
      containers:
      - name: istio-proxy
      {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }}
        image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}"
      {{- else }}
        image: "{{ .ProxyImage }}"
      {{- end }}
        ports:
        - containerPort: 15090
          protocol: TCP
          name: http-envoy-prom
        args:
        - proxy
        - router
        - --domain
        - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }}
        - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }}
        - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }}
        - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }}
      {{- if .Values.global.sts.servicePort }}
        - --stsPort={{ .Values.global.sts.servicePort }}
      {{- end }}
      {{- if .Values.global.logAsJson }}
        - --log_as_json
      {{- end }}
      {{- if .Values.global.proxy.lifecycle }}
        lifecycle:
          {{ toYaml .Values.global.proxy.lifecycle | indent 6 }}
      {{- end }}
        securityContext:
          runAsUser: {{ .ProxyUID | default "1337" }}
          runAsGroup: {{ .ProxyGID | default "1337" }}
        env:
        - name: PILOT_CERT_PROVIDER
          value: {{ .Values.global.pilotCertProvider }}
        - name: CA_ADDR
        {{- if .Values.global.caAddress }}
          value: {{ .Values.global.caAddress }}
        {{- else }}
          value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012
        {{- end }}
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: INSTANCE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
        - name: HOST_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: ISTIO_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        - name: PROXY_CONFIG
          value: |
                {{ protoToJSON .ProxyConfig }}
        - name: ISTIO_META_POD_PORTS
          value: |-
            [
            {{- $first := true }}
            {{- range $index1, $c := .Spec.Containers }}
              {{- range $index2, $p := $c.Ports }}
                {{- if (structToJSON $p) }}
                {{if not $first}},{{end}}{{ structToJSON $p }}
                {{- $first = false }}
                {{- end }}
              {{- end}}
            {{- end}}
            ]
        - name: GOMEMLIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
        - name: GOMAXPROCS
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        {{- if .CompliancePolicy }}
        - name: COMPLIANCE_POLICY
          value: "{{ .CompliancePolicy }}"
        {{- end }}
        - name: ISTIO_META_APP_CONTAINERS
          value: "{{ $containers | join "," }}"
        - name: ISTIO_META_CLUSTER_ID
          value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}"
        - name: ISTIO_META_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: ISTIO_META_INTERCEPTION_MODE
          value: "{{ .ProxyConfig.InterceptionMode.String }}"
        {{- if .Values.global.network }}
        - name: ISTIO_META_NETWORK
          value: "{{ .Values.global.network }}"
        {{- end }}
        {{- if .DeploymentMeta.Name }}
        - name: ISTIO_META_WORKLOAD_NAME
          value: "{{ .DeploymentMeta.Name }}"
        {{ end }}
        {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }}
        - name: ISTIO_META_OWNER
          value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }}
        {{- end}}
        {{- if .Values.global.meshID }}
        - name: ISTIO_META_MESH_ID
          value: "{{ .Values.global.meshID }}"
        {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}
        - name: ISTIO_META_MESH_ID
          value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}"
        {{- end }}
        {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain)  }}
        - name: TRUST_DOMAIN
          value: "{{ . }}"
        {{- end }}
        {{- range $key, $value := .ProxyConfig.ProxyMetadata }}
        - name: {{ $key }}
          value: "{{ $value }}"
        {{- end }}
        {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}}
        readinessProbe:
          httpGet:
            path: /healthz/ready
            port: 15021
          initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }}
          periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }}
          timeoutSeconds: 3
          failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }}
        volumeMounts:
        - name: workload-socket
          mountPath: /var/run/secrets/workload-spiffe-uds
        - name: credential-socket
          mountPath: /var/run/secrets/credential-uds
        {{- if eq .Values.global.caName "GkeWorkloadCertificate" }}
        - name: gke-workload-certificate
          mountPath: /var/run/secrets/workload-spiffe-credentials
          readOnly: true
        {{- else }}
        - name: workload-certs
          mountPath: /var/run/secrets/workload-spiffe-credentials
        {{- end }}
        {{- if eq .Values.global.pilotCertProvider "istiod" }}
        - mountPath: /var/run/secrets/istio
          name: istiod-ca-cert
        {{- end }}
        - mountPath: /var/lib/istio/data
          name: istio-data
        # SDS channel between istioagent and Envoy
        - mountPath: /etc/istio/proxy
          name: istio-envoy
        - mountPath: /var/run/secrets/tokens
          name: istio-token
        {{- if .Values.global.mountMtlsCerts }}
        # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications.
        - mountPath: /etc/certs/
          name: istio-certs
          readOnly: true
        {{- end }}
        - name: istio-podinfo
          mountPath: /etc/istio/pod
      volumes:
      - emptyDir: {}
        name: workload-socket
      - emptyDir: {}
        name: credential-socket
      {{- if eq .Values.global.caName "GkeWorkloadCertificate" }}
      - name: gke-workload-certificate
        csi:
          driver: workloadcertificates.security.cloud.google.com
      {{- else}}
      - emptyDir: {}
        name: workload-certs
      {{- end }}
      # SDS channel between istioagent and Envoy
      - emptyDir:
          medium: Memory
        name: istio-envoy
      - name: istio-data
        emptyDir: {}
      - name: istio-podinfo
        downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "annotations"
              fieldRef:
                fieldPath: metadata.annotations
      - name: istio-token
        projected:
          sources:
          - serviceAccountToken:
              path: istio-token
              expirationSeconds: 43200
              audience: {{ .Values.global.sds.token.aud }}
      {{- if eq .Values.global.pilotCertProvider "istiod" }}
      - name: istiod-ca-cert
      {{- if eq (.Values.pilot.env).ENABLE_CLUSTER_TRUST_BUNDLE_API true }}
        projected:
          sources:
          - clusterTrustBundle:
            name: istio.io:istiod-ca:root-cert
            path: root-cert.pem
      {{- else }}
        configMap:
          name: istio-ca-root-cert
      {{- end }}
      {{- end }}
      {{- if .Values.global.mountMtlsCerts }}
      # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications.
      - name: istio-certs
        secret:
          optional: true
          {{ if eq .Spec.ServiceAccountName "" }}
          secretName: istio.default
          {{ else -}}
          secretName: {{  printf "istio.%s" .Spec.ServiceAccountName }}
          {{  end -}}
      {{- end }}
      {{- if .Values.global.imagePullSecrets }}
      imagePullSecrets:
        {{- range .Values.global.imagePullSecrets }}
        - name: {{ . }}
        {{- end }}
      {{- end }}
    1. Remove sysctls because net.ipv4.ip_unprivileged_port_start is not available on Linux kernels earlier than 4.11.

Scenario 2: Kernel versions earlier than 4.11 + privileged ports (ports < 1024)

If your gateway needs to listen on ports below 1024, you must also add the NET_BIND_SERVICE capability in addition to the sysctls: [] change. Modify the securityContext section of the istio-proxy container in gateway-injection-template.txt:

    securityContext:
      capabilities:
        add:
        - NET_BIND_SERVICE
      runAsUser: {{ .ProxyUID | default "1337" }}
      runAsGroup: {{ .ProxyGID | default "1337" }}
      # Uncomment the following fields to run the gateway as root if necessary.
      # runAsUser: 0
      # runAsGroup: 0
      # runAsNonRoot: false
WARNING

If the gateway still cannot listen on ports below 1024 after adding the NET_BIND_SERVICE capability, consider the following alternatives:

  1. Use ports 1024 or above (recommended) — Reconfigure your gateway listeners to use ports ≥ 1024 to avoid privilege issues entirely.
  2. Run the gateway as root — Set runAsUser: 0, runAsGroup: 0, and runAsNonRoot: false in the container's securityContext. This grants full privileges to the gateway process. Evaluate the security implications carefully before using this approach.

Apply the injection template

  1. Patch the default gateway injection template for Istio resource:

    TEMPLATE_CONTENT=$(cat gateway-injection-template.txt)
    PATCH_DATA=$(jq -n \
      --arg template "${TEMPLATE_CONTENT}" \
      '{
        "spec": {
          "values": {
            "sidecarInjectorWebhook": {
              "templates": {
                "gateway": $template
              }
            }
          }
        }
      }')
    # Finally apply the patch to the Istio resource named `default`:
    kubectl patch istio default --type=merge -p "${PATCH_DATA}"
  2. Wait for the control plane to return the Ready status condition by running the following command:

    kubectl wait --for condition=Ready istio/default --timeout=3m

Kubernetes Gateway API

Prerequisites

  • Alauda Container Platform 4.2.0 or later, or upgrade Gateway API CRDs to the latest version.

Procedure

Scenario 1: Kernel versions earlier than 4.11 only (ports ≥ 1024)

  1. Create a ConfigMap named asm-kube-gateway-options in the same namespace where you plan to deploy your Gateway:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: asm-kube-gateway-options
      # Replace `<your-gateway-namespace>` with the actual namespace where your Gateway will be deployed. 
      namespace: <your-gateway-namespace>
    data:
      deployment: |
        spec:
          template:
            spec:
              securityContext:
                sysctls: []
    1. Remove sysctls because net.ipv4.ip_unprivileged_port_start is not available on Linux kernels earlier than 4.11.

Scenario 2: Kernel versions earlier than 4.11 + privileged ports (ports < 1024)

If your gateway needs to listen on ports below 1024, add the NET_BIND_SERVICE capability in the ConfigMap. Modify the data.deployment field to include container-level security context:

apiVersion: v1
kind: ConfigMap
metadata:
  name: asm-kube-gateway-options
  namespace: <your-gateway-namespace>
data:
  deployment: |
    spec:
      template:
        spec:
          securityContext:
            sysctls: []
          # Add the following container-level security context to grant the NET_BIND_SERVICE capability.
          containers:
          - name: istio-proxy
            securityContext:
              capabilities:
                add:
                - NET_BIND_SERVICE
              # Uncomment the following fields to run the gateway as root if necessary.
              # runAsUser: 0
              # runAsGroup: 0
              # runAsNonRoot: false
WARNING

If the gateway still cannot listen on ports below 1024 after adding the NET_BIND_SERVICE capability, consider the following alternatives:

  1. Use ports 1024 or above (recommended) — Reconfigure your gateway listeners to use ports ≥ 1024 to avoid privilege issues entirely.
  2. Run the gateway as root — Set runAsUser: 0, runAsGroup: 0, and runAsNonRoot: false in the container's securityContext. This grants full privileges to the gateway process. Evaluate the security implications carefully before using this approach.

Apply the ConfigMap

  1. Reference the ConfigMap in your Gateway resource by adding the infrastructure.parametersRef field:

    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: <your-gateway-name>
      namespace: <your-gateway-namespace>
    spec:
      # Add the following infrastructure configuration to your Gateway CR
      infrastructure:
        parametersRef:
          group: ""
          kind: ConfigMap
          name: asm-kube-gateway-options
      # ... rest of your Gateway configuration

    This configuration ensures that the gateway deployment uses the custom security context settings defined in the ConfigMap.