All Products
Search
Document Center

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

Last Updated:Mar 11, 2026

Configure IP-based access control for applications running in a Service Mesh (ASM) instance. Use Istio AuthorizationPolicy resources to deny or allow traffic from specific IP addresses at the ingress gateway or the sidecar proxy level.

How it works

ASM uses Istio authorization policies to filter traffic by source IP address. Two fields control which IP addresses are evaluated:

FieldMatches againstUse when
ipBlocksThe source IP of the direct TCP connection (downstream_remote_address)No Layer 7 proxy sits between the client and the ASM gateway, and externalTrafficPolicy is set to Local
remoteIpBlocksThe original client IP derived from the X-Forwarded-For headerA Layer 7 proxy or HTTP load balancer sits in front of the ASM gateway

Prerequisites

Before you begin, make sure that you have:

Usage notes

  • All examples in this topic set the externalTrafficPolicy field of the gateway service to Local. If externalTrafficPolicy is set to Cluster, source IP addresses are not preserved, and IP-based access control does not work as expected.

Scenario 1: No Layer 7 proxy between the client and the ASM gateway

When no Layer 7 proxy sits between the client and the ASM gateway, the gateway receives traffic directly. Both ipBlocks and remoteIpBlocks identify the real client IP address.

To verify, access the httpbin application:

curl <gateway-ip>:80/ -I

Replace <gateway-ip> with the external IP address of your ASM ingress gateway.

Verify the client IP in access logs

Gateway access logs:

{
    "downstream_remote_address": "106.XX.XX.1:58656",
    "x_forwarded_for": "106.11.XX.X",
    "response_code": "200",
    ...
}

Both downstream_remote_address and x_forwarded_for reflect the real client IP (106.11.XX.X in this example), so both ipBlocks and remoteIpBlocks work correctly at the gateway level.

Sidecar proxy access logs:

{
    "downstream_remote_address": "106.11.XX.X:0",
    "x_forwarded_for": "106.11.XX.X",
    "response_code": "200",
    ...
}

The sidecar proxy receives the real client IP in x_forwarded_for. The port in downstream_remote_address is 0 because the address is inferred from headers, not from the physical TCP connection.

Example 1: Deny an IP address at the gateway

The following AuthorizationPolicy denies traffic from a specific IP address at the ingress gateway.

Test with ipBlocks

  1. Create a file named gateway-test.yaml with the following content: Replace 106.11.XX.X with the IP address to deny.

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

       kubectl apply -f gateway-test.yaml
  3. Access the httpbin application: Expected output: A 403 Forbidden response confirms the IP address is denied.

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

Test with remoteIpBlocks

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

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

       kubectl apply -f gateway-test.yaml
  3. Access the httpbin application: Expected output: Both ipBlocks and remoteIpBlocks produce the same result because no Layer 7 proxy is involved.

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

Example 2: Deny an IP address at the sidecar proxy

Apply the authorization policy to the sidecar proxy instead of the gateway.

Test with ipBlocks

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

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

       kubectl apply -f gateway-test.yaml
  3. Access the httpbin application: Expected output: The request succeeds because ipBlocks matches against downstream_remote_address -- the IP of the pod that established the TCP connection. At the sidecar proxy, this is always the gateway pod IP, not the original client IP. The downstream_remote_address field indicates the remote address of the downstream connection. It is not always the real physical address of the remote end -- the address may be inferred from the Proxy Protocol filter or an X-Forwarded-For request header.

    Note

    To block traffic with ipBlocks at the sidecar level, specify the gateway pod IP address instead of the client IP. The ipBlocks field matches the direct TCP connection source, which at the sidecar proxy is the gateway pod.

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

Test with remoteIpBlocks

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

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

       kubectl apply -f gateway-test.yaml
  3. Access the httpbin application: Expected output: remoteIpBlocks correctly blocks the client IP because it reads from the X-Forwarded-For header, which preserves the original client address.

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

Scenario 2: A Layer 7 proxy between the client and the ASM gateway

When a Layer 7 proxy (such as an HTTP load balancer) sits in front of the ASM gateway, the X-Forwarded-For header contains a chain of IP addresses from each proxy in the path. This scenario only tests remoteIpBlocks because ipBlocks matches the physical peer IP -- the proxy, not the original client.

Understand the X-Forwarded-For chain

Gateway access logs:

{
    "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",
    ...
}

The X-Forwarded-For header contains four IP addresses:

  • 56.5.X.X -- the original client IP

  • 72.9.X.X, 98.1.X.X -- intermediate proxies

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

Before the request reached the ASM gateway, the header value was 56.5.X.X, 72.9.X.X, 98.1.X.X. The gateway appended 106.11.XX.X (the downstream_remote_address of the direct connection).

Sidecar proxy access logs:

{
    "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",
    ...
}

At the sidecar level, downstream_remote_address is not the physical peer address -- it is the last IP from the X-Forwarded-For header. The port is 0 because the address is derived from header data, not from a TCP connection.

Example 1: Deny the last IP in the X-Forwarded-For chain at the gateway

By default, numTrustedProxies is 0, so remoteIpBlocks evaluates the last IP in the X-Forwarded-For header.

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

       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:
                     - 106.11.XX.X
         selector:
           matchLabels:
             istio: ingressgateway
  2. Deploy the authorization policy:

       kubectl apply -f gateway-test.yaml
  3. Simulate a request with an X-Forwarded-For header: Expected output: The gateway denies the last IP in X-Forwarded-For (106.11.XX.X, appended by the gateway from the direct connection).

       curl <gateway-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

Example 2: Change the trusted proxy count with numTrustedProxies

The numTrustedProxies setting controls how many proxies in the X-Forwarded-For chain the gateway considers trusted. This shifts which IP address remoteIpBlocks evaluates.

numTrustedProxiesTrusted proxies (from right)IP evaluated by remoteIpBlocks
0 (default)NoneLast IP in X-Forwarded-For
11 rightmost proxySecond-to-last IP
22 rightmost proxiesThird-to-last IP
  1. Add the following annotation to the spec field of the ingress gateway YAML file. For more information about editing the gateway configuration, see Manage the ingress gateway in the ASM console.

    Warning

    This change restarts the gateway pods.

       podAnnotations:
           proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 2 } }'
  2. After the gateway restarts, access the httpbin application: Expected output: The request succeeds. With numTrustedProxies set to 2, the gateway trusts the two rightmost proxies and evaluates the third-to-last IP (72.9.X.X) against remoteIpBlocks. Since the authorization policy denies 106.11.XX.X (the last IP), the request is no longer blocked.

       curl <gateway-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

Example 3: Deny the third-to-last IP in the X-Forwarded-For chain

With numTrustedProxies set to 2, create a policy that denies the IP address the gateway now evaluates.

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

       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. Access the httpbin application: Expected output: The third-to-last IP (72.9.X.X) is denied, confirming that numTrustedProxies: 2 shifts the evaluation target.

       curl <gateway-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

Example 4: Apply the same policy to the sidecar proxy

Change the authorization policy scope from the gateway to the httpbin sidecar proxy.

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

       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. Access the httpbin application: Expected output: The request succeeds because the numTrustedProxies setting was applied only to the gateway. The sidecar proxy uses the default value (0) and evaluates the last IP in the X-Forwarded-For header. Since the policy denies 72.9.X.X but the sidecar evaluates 106.11.XX.X, the request is not blocked.

    Note

    To deny traffic at the sidecar level, set remoteIpBlocks to the last IP in the X-Forwarded-For header as seen in the sidecar proxy logs. The numTrustedProxies setting on the gateway does not affect sidecar proxy behavior.

       curl <gateway-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

Troubleshooting

Authorization policy has no effect

If the policy does not block traffic as expected:

  1. Confirm externalTrafficPolicy is set to Local on the gateway service. If set to Cluster, the source IP is replaced with a node IP.

  2. Check gateway access logs to identify the actual values of downstream_remote_address and x_forwarded_for. Make sure the IP in your policy matches what appears in the logs.

ipBlocks does not work on the sidecar proxy

When you apply an ipBlocks-based policy to a sidecar proxy, the downstream_remote_address is the gateway pod IP, not the client IP. Use remoteIpBlocks instead, or specify the gateway pod IP in ipBlocks.

numTrustedProxies does not affect the sidecar proxy

The numTrustedProxies setting is a gateway-level configuration. Sidecar proxies always use the default value (0), evaluating the last IP in the X-Forwarded-For header. Adjust your remoteIpBlocks value accordingly when targeting sidecar proxies.

Clean up

After testing, remove the authorization policies to avoid blocking legitimate traffic:

kubectl delete authorizationpolicy gateway-test -n istio-system
kubectl delete authorizationpolicy gateway-test -n default

If you modified numTrustedProxies, remove the podAnnotations from the ingress gateway configuration and wait for the gateway pods to restart.

What's next