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
An ASM instance of v1.15.3.25 or later with a cluster added. See Add a cluster to an ASM instance.
Automatic sidecar proxy injection is enabled. See Enable automatic sidecar proxy injection.
The HTTPBin application is deployed and accessible. See Deploy the HTTPBin application.
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 theX-Forwarded-Forheader.
Choose ipBlocks or remoteIpBlocks
The correct field depends on how the client IP reaches the proxy:
| Traffic path | Source of client IP | Field to use |
|---|---|---|
| Client connects directly to the ASM gateway (no Layer 7 proxy) | Packet source address | ipBlocks or remoteIpBlocks (both work) |
| A Layer 7 proxy sits between the client and the ASM gateway | X-Forwarded-For header | remoteIpBlocks |
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 point | downstream_remote_address value | ipBlocks 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
externalTrafficPolicyfield toLocal. IfexternalTrafficPolicyis set toCluster, 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/ -IGateway 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
Create a file named
gateway-test.yamlwith 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: ingressgatewayDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify the policy by sending a request: Expected output: A
403 Forbiddenresponse confirms that the IP is blocked.curl <gateway-external-ip>:80/ -IHTTP/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.
Create a file named
gateway-test.yamlwith 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: ingressgatewayDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify the policy: Expected output:
curl <gateway-external-ip>:80/ -IHTTP/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
Create a file named
gateway-test.yamlwith the following AuthorizationPolicy:NoteThe namespace is
defaultand the selector targets thehttpbinworkload, 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: httpbinDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify the policy: Expected output: The request succeeds with
200 OKbecause the sidecar proxy sees the gateway pod IP indownstream_remote_address, not the client IP. TheipBlocksfield matches against the direct TCP connection source, which is the gateway pod. To block traffic at the sidecar level usingipBlocks, specify the gateway pod IP instead of the client IP. For client IP filtering at the sidecar, useremoteIpBlocksinstead.curl <gateway-external-ip>:80/ -IHTTP/1.1 200 OK server: istio-envoy content-type: text/html; charset=utf-8
Deny an IP at the sidecar using remoteIpBlocks
Create a file named
gateway-test.yamlwith 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: httpbinDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify the policy: Expected output: The
remoteIpBlocksconfiguration blocks the request because it evaluates theX-Forwarded-Forheader, which contains the actual client IP.curl <gateway-external-ip>:80/ -IHTTP/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.
Create a file named
gateway-test.yamlwith 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: ingressgatewayDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify 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' -IHTTP/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:
| numTrustedProxies | Trusted proxies (from right) | IP evaluated by remoteIpBlocks |
|---|---|---|
| 0 (default) | None | 106.11.XX.X (last IP, appended by gateway) |
| 2 | 106.11.XX.X, 98.1.X.X | 72.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 } }'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' -IExpected output:
HTTP/1.1 200 OK
server: istio-envoy
content-type: text/html; charset=utf-8The 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:
Create a file named
gateway-test.yamlwith 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: ingressgatewayDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify 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' -IHTTP/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.
Create a file named
gateway-test.yamlwith 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: httpbinDeploy the authorization policy:
kubectl apply -f gateway-test.yamlVerify the policy: Expected output: The request succeeds because the sidecar's
numTrustedProxiesis0, soremoteIpBlocksevaluates the last IP in theX-Forwarded-Forchain (106.11.XX.X). Since72.9.X.Xis not the last IP, the deny rule does not trigger. To deny72.9.X.Xat the sidecar level, setnumTrustedProxieson the sidecar proxy as well, or change theremoteIpBlocksvalue to match the IP that the sidecar evaluates (the last IP in theX-Forwarded-Forheader).curl <gateway-external-ip>:80/ -H 'X-Forwarded-For: 56.5.X.X, 72.9.X.X, 98.1.X.X' -IHTTP/1.1 200 OK server: istio-envoy content-type: text/html; charset=utf-8
What's next
Create an ingress gateway -- Configure and manage ASM ingress gateways.
ASM security policies -- Explore additional access control options, including ASM-managed security policies that simplify AuthorizationPolicy configuration.