All Products
Search
Document Center

Alibaba Cloud Service Mesh:Restrict IP address access to applications in an ASM instance

Last Updated:Mar 11, 2026

Alibaba Cloud Service Mesh (ASM) supports IP-based access control through Istio AuthorizationPolicy resources. Use ipBlocks or remoteIpBlocks to deny traffic from specific client IP addresses at the ingress gateway or sidecar proxy level.

Prerequisites

How it works

An AuthorizationPolicy with the DENY action blocks requests that match specified IP addresses. Two source fields control which IP is evaluated:

  • ipBlocks: Matches against the IP address of the peer that directly establishes the TCP connection with the proxy (downstream_remote_address).

  • remoteIpBlocks: Matches against the original client IP, typically extracted from the X-Forwarded-For header.

Choose ipBlocks or remoteIpBlocks

The correct field depends on how the client IP reaches the proxy:

Traffic pathSource of client IPField to use
Client connects directly to the ASM gateway (no Layer 7 proxy)Packet source addressipBlocks or remoteIpBlocks (both work)
A Layer 7 proxy sits between the client and the ASM gatewayX-Forwarded-For headerremoteIpBlocks

Gateway vs. sidecar enforcement

Policies applied at the gateway and at the sidecar proxy behave differently because each proxy sees a different downstream_remote_address:

Enforcement pointdownstream_remote_address valueipBlocks behavior
Gateway (istio-system namespace, selector istio: ingressgateway)Actual client IP (with valid port)Matches the real client IP
Sidecar proxy (default namespace, selector app: <workload>)Client IP inferred from X-Forwarded-For (port is 0)Does not match the client IP because actual TCP source is the gateway pod IP

Because the sidecar proxy receives traffic from the gateway pod rather than directly from the client, ipBlocks on the sidecar matches the gateway pod IP. To filter by client IP at the sidecar level, use remoteIpBlocks.

Usage notes

  • The following examples set the gateway's externalTrafficPolicy field to Local. If externalTrafficPolicy is set to Cluster, source IP addresses are not preserved, and IP-based access control does not work as expected.

Deny an IP at the gateway (no Layer 7 proxy)

When the client connects directly to the ASM gateway without an intermediate Layer 7 proxy, downstream_remote_address and x_forwarded_for in the gateway logs both reflect the actual client IP. Both ipBlocks and remoteIpBlocks work for IP-based access control.

Run the following command to verify connectivity to the HTTPBin application:

curl <gateway-external-ip>:80/ -I

Gateway log example

{
    "downstream_remote_address": "106.XX.XX.1:58656",
    "x_forwarded_for": "106.11.XX.X",
    "response_code": "200",
    "upstream_cluster": "outbound|8000||httpbin.default.svc.cluster.local",
    "user_agent": "curl/7.88.1"
}

The downstream_remote_address and x_forwarded_for fields both contain the actual client IP (106.11.XX.X).

Sidecar proxy log example

{
    "downstream_remote_address": "106.11.XX.X:0",
    "x_forwarded_for": "106.11.XX.X",
    "response_code": "200",
    "upstream_cluster": "inbound|80||",
    "user_agent": "curl/7.88.1"
}

The x_forwarded_for field shows the actual client IP. The downstream_remote_address field also shows the client IP, but the port is 0 because port information is lost at the sidecar level.

Deny an IP using ipBlocks

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy: Replace <client-ip> with the IP address to block.

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test
          namespace: istio-system
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    ipBlocks:
                      - <client-ip>  # Example: 106.11.XX.X
          selector:
            matchLabels:
              istio: ingressgateway
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy by sending a request: Expected output: A 403 Forbidden response confirms that the IP is blocked.

        curl <gateway-external-ip>:80/ -I
        HTTP/1.1 403 Forbidden
        content-length: 19
        content-type: text/plain
        server: istio-envoy

Deny an IP using remoteIpBlocks

The only difference from the ipBlocks approach is the source field in the AuthorizationPolicy. The remoteIpBlocks field evaluates the X-Forwarded-For header instead of the direct TCP connection source.

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy:

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test
          namespace: istio-system
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    remoteIpBlocks:
                      - <client-ip>  # Example: 106.11.XX.X
          selector:
            matchLabels:
              istio: ingressgateway
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy: Expected output:

        curl <gateway-external-ip>:80/ -I
        HTTP/1.1 403 Forbidden
        content-length: 19
        content-type: text/plain
        server: istio-envoy

Deny an IP at the sidecar proxy

When the policy targets the sidecar proxy instead of the gateway, ipBlocks and remoteIpBlocks behave differently.

Why ipBlocks does not match the client IP at the sidecar

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy:

    Note

    The namespace is default and the selector targets the httpbin workload, not the ingress gateway.

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test
          namespace: default
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    ipBlocks:
                      - <client-ip>  # Example: 106.11.XX.X
          selector:
            matchLabels:
              app: httpbin
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy: Expected output: The request succeeds with 200 OK because the sidecar proxy sees the gateway pod IP in downstream_remote_address, not the client IP. The ipBlocks field matches against the direct TCP connection source, which is the gateway pod. To block traffic at the sidecar level using ipBlocks, specify the gateway pod IP instead of the client IP. For client IP filtering at the sidecar, use remoteIpBlocks instead.

        curl <gateway-external-ip>:80/ -I
        HTTP/1.1 200 OK
        server: istio-envoy
        content-type: text/html; charset=utf-8

Deny an IP at the sidecar using remoteIpBlocks

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy:

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test
          namespace: default
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    remoteIpBlocks:
                      - <client-ip>  # Example: 106.11.XX.X
          selector:
            matchLabels:
              app: httpbin
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy: Expected output: The remoteIpBlocks configuration blocks the request because it evaluates the X-Forwarded-For header, which contains the actual client IP.

        curl <gateway-external-ip>:80/ -I
        HTTP/1.1 403 Forbidden
        content-length: 19
        content-type: text/plain
        server: istio-envoy

Deny an IP when a Layer 7 proxy sits between the client and the gateway

When one or more Layer 7 proxies (such as a load balancer or CDN) sit between the client and the ASM gateway, the X-Forwarded-For header contains multiple IP addresses. Each proxy appends the IP of the previous hop.

In this scenario, only remoteIpBlocks is relevant for IP-based access control. The ipBlocks field matches the physical peer IP, which is the Layer 7 proxy, not the client.

Gateway log example

{
    "downstream_remote_address": "106.11.XX.X:62232",
    "x_forwarded_for": "56.5.X.X, 72.9.X.X, 98.1.X.X, 106.11.XX.X",
    "response_code": "200",
    "upstream_cluster": "outbound|8000||httpbin.default.svc.cluster.local"
}

The x_forwarded_for header contains four IP addresses:

  • 56.5.X.X, 72.9.X.X, 98.1.X.X -- set by the upstream Layer 7 proxies before reaching the ASM gateway.

  • 106.11.XX.X -- appended by the ASM gateway itself.

The downstream_remote_address (106.11.XX.X:62232) shows the IP of the peer that directly connects to the gateway (the last Layer 7 proxy).

Sidecar proxy log example

{
    "downstream_remote_address": "106.11.XX.X:0",
    "x_forwarded_for": "56.5.X.X, 72.9.X.X, 98.1.X.X, 106.11.XX.X",
    "response_code": "200",
    "upstream_cluster": "inbound|80||"
}

The x_forwarded_for value is the same as the gateway log. The downstream_remote_address is the last IP in the x_forwarded_for chain (port is 0 because port information is not preserved at the sidecar level).

Deny the last IP in X-Forwarded-For at the gateway

By default (numTrustedProxies = 0), remoteIpBlocks evaluates the last IP address appended to the X-Forwarded-For header by the gateway. This is the IP of the peer that directly connects to the gateway.

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy:

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test-ap-wg-gateway-test-istio-system-gateway-ingressgateway
          namespace: istio-system
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    remoteIpBlocks:
                      - <last-proxy-ip>  # Example: 106.11.XX.X
          selector:
            matchLabels:
              istio: ingressgateway
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy: Expected output:

        curl <gateway-external-ip>:80/ -H 'X-Forwarded-For: 56.5.X.X, 72.9.X.X, 98.1.X.X' -I
        HTTP/1.1 403 Forbidden
        content-length: 19
        content-type: text/plain
        server: istio-envoy

Use numTrustedProxies to target a specific IP in the X-Forwarded-For chain

The numTrustedProxies setting controls which IP address in the X-Forwarded-For chain remoteIpBlocks evaluates. When set to N, the gateway treats the N proxies closest to itself as trusted and evaluates the IP of the (N+1)th proxy from the right.

For example, with X-Forwarded-For: 56.5.X.X, 72.9.X.X, 98.1.X.X and the gateway appending 106.11.XX.X:

numTrustedProxiesTrusted proxies (from right)IP evaluated by remoteIpBlocks
0 (default)None106.11.XX.X (last IP, appended by gateway)
2106.11.XX.X, 98.1.X.X72.9.X.X (third from right)

Set numTrustedProxies on the ingress gateway

Add the following annotation to the spec field of the ingress gateway YAML file. For instructions on editing the ingress gateway configuration, see Manage the ingress gateway in the ASM console.

podAnnotations:
    proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 2 } }'
Warning

This configuration restarts the gateway pods.

After the gateway restarts, verify the effect:

curl <gateway-external-ip>:80/ -H 'X-Forwarded-For: 56.5.X.X, 72.9.X.X, 98.1.X.X' -I

Expected output:

HTTP/1.1 200 OK
server: istio-envoy
content-type: text/html; charset=utf-8

The request succeeds because numTrustedProxies: 2 causes remoteIpBlocks to evaluate 72.9.X.X (the third IP from the right) instead of 106.11.XX.X. Since 72.9.X.X is not in the deny list, the request is allowed.

Deny the evaluated IP after setting numTrustedProxies

With numTrustedProxies set to 2, create a policy that denies the IP evaluated by remoteIpBlocks:

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy:

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test-ap-wg-gateway-test-istio-system-gateway-ingressgateway
          namespace: istio-system
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    remoteIpBlocks:
                      - 72.9.X.X
          selector:
            matchLabels:
              istio: ingressgateway
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy: Expected output:

        curl <gateway-external-ip>:80/ -H 'X-Forwarded-For: 56.5.X.X, 72.9.X.X, 98.1.X.X' -I
        HTTP/1.1 403 Forbidden
        content-length: 19
        content-type: text/plain
        server: istio-envoy

Apply the policy to the sidecar proxy instead of the gateway

The numTrustedProxies setting configured on the gateway does not apply to sidecar proxies. Sidecar proxies use the default numTrustedProxies value of 0, which means remoteIpBlocks evaluates the last IP in the X-Forwarded-For chain received by the sidecar.

  1. Create a file named gateway-test.yaml with the following AuthorizationPolicy that targets the HTTPBin sidecar:

        apiVersion: security.istio.io/v1beta1
        kind: AuthorizationPolicy
        metadata:
          name: gateway-test
          namespace: default
        spec:
          action: DENY
          rules:
            - from:
                - source:
                    remoteIpBlocks:
                      - 72.9.X.X
          selector:
            matchLabels:
              app: httpbin
  2. Deploy the authorization policy:

        kubectl apply -f gateway-test.yaml
  3. Verify the policy: Expected output: The request succeeds because the sidecar's numTrustedProxies is 0, so remoteIpBlocks evaluates the last IP in the X-Forwarded-For chain (106.11.XX.X). Since 72.9.X.X is not the last IP, the deny rule does not trigger. To deny 72.9.X.X at the sidecar level, set numTrustedProxies on the sidecar proxy as well, or change the remoteIpBlocks value to match the IP that the sidecar evaluates (the last IP in the X-Forwarded-For header).

        curl <gateway-external-ip>:80/ -H 'X-Forwarded-For: 56.5.X.X, 72.9.X.X, 98.1.X.X' -I
        HTTP/1.1 200 OK
        server: istio-envoy
        content-type: text/html; charset=utf-8

What's next