istio: Incorrect RemoteIP when Authorization Policy is applied to Injected Istio Proxy
Bug description

When AuthorizationPolicy is applied to injected istio proxy, remoteIpBlocks does not work as expected when istio gateway is behind another reverse proxy (Azure Front Door). RemoteIP seems to set to the IP of the reverse-proxy deployed in front of istio gateway. (see diagram above: requests in green, configuration in blue)
kind: AuthorizationPolicy
metadata:
name: test-auth-policy
spec:
action: ALLOW
selector:
matchLabels:
app: test
rules:
- from:
- source:
remoteIpBlocks:
- <clientIp>
Request from <clientIp> gets denied with the following entry in the logs:
2021-01-18T17:40:15.872025Z debug envoy rbac checking request: requestedServerName: outbound_.80_._.test-service.test-ns.svc.cluster.local, sourceIP: <IstioIngressGatewayIp>:37834, directRemoteIP: <IstioIngressGatewayIp>:37834, remoteIP: <AzureFrontDoorIP>:0,localAddress: 10.240.2.152:8080, ssl: uriSanPeerCertificate: spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-public-service-account, dnsSanPeerCertificate: , subjectPeerCertificate: , headers: ':authority', 'test.example.com'
...
'x-forwarded-for', '<clientIp>,<AzureFrontDoorIP>'
...
'x-envoy-external-address', '<clientIp>'
...
'x-forwarded-client-cert', 'By=spiffe://cluster.local/ns/test-ns/sa/default;Hash=2a3d7357260f29961fd26f74bf7e9d637eaa9c23c4d735488f0541027b4c48ed;Subject="";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-public-service-account'
, dynamicMetadata: filter_metadata {
key: "istio_authn"
value {
fields {
key: "request.auth.principal"
value {
string_value: "cluster.local/ns/istio-system/sa/istio-ingressgateway-public-service-account"
}
}
fields {
key: "source.namespace"
value {
string_value: "istio-system"
}
}
fields {
key: "source.principal"
value {
string_value: "cluster.local/ns/istio-system/sa/istio-ingressgateway-public-service-account"
}
}
fields {
key: "source.user"
value {
string_value: "cluster.local/ns/istio-system/sa/istio-ingressgateway-public-service-account"
}
}
}
}
2021-01-18T17:40:15.872065Z debug envoy rbac enforced denied, matched policy none
Affected product area (please put an X in all that apply)
[ ] Docs [ ] Installation [X] Networking [ ] Performance and Scalability [ ] Extensions and Telemetry [X] Security [ ] Test and Release [ ] User Experience [ ] Developer Infrastructure [ ] Upgrade
Affected features
[ ] Multi Cluster [ ] Virtual Machine [ ] Multi Control Plane
Expected behavior
AuthorizationPolicy allows requests from <clientIp>.
Steps to reproduce the bug
- Install istio using
istioctland configure with:ingressGateways: - enabled: false name: istio-ingressgateway k8s: replicaCount: 2 podAnnotations: proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 1 } }' service: externalTrafficPolicy: Local - Create
AuthorizationPolicykind: AuthorizationPolicy metadata: name: test-auth-policy spec: action: ALLOW selector: matchLabels: app: test rules: - from: - source: remoteIpBlocks: - <clientIp> - Deploy a dummy service with the labels matching istio
AuthorizationPolicy - Make a request from
<clientIp>through a reverse proxy (or simulate with XFF header) - Observe the request beeing blocked, but should be allowed
Version
istioctl version --remoteclient version: 1.8.2 control plane version: 1.8.2 data plane version: 1.8.2 (7 proxies)kubectl version --shortClient Version: v1.18.15 Server Version: v1.18.14
How was Istio installed?
istioctl install -f install.yaml
install.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
profile: default
tag: 1.8.2-distroless
meshConfig:
outboundTrafficPolicy:
mode: REGISTRY_ONLY
components:
cni:
enabled: true
ingressGateways:
- enabled: false
name: istio-ingressgateway
- enabled: true
name: istio-ingressgateway-public
k8s:
replicaCount: 2
podAnnotations:
proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 1 } }'
serviceAnnotations:
service.beta.kubernetes.io/azure-allowed-service-tags: AzureFrontDoor.Backend
service:
externalTrafficPolicy: Local
loadBalancerIP: 51.145.104.39
ports:
- name: http2
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
hpaSpec:
maxReplicas: 5
minReplicas: 2
resources:
limits:
cpu: 650m
memory: 400Mi
requests:
cpu: 100m
memory: 400Mi
overlays:
- kind: HorizontalPodAutoscaler
name: istio-ingressgateway-public
patches:
- path: metadata.labels.app
value: istio-ingressgateway-public
- path: metadata.labels.istio
value: public-ingressgateway
- path: spec.scaleTargetRef.name
value: istio-ingressgateway-public
- kind: Deployment
name: istio-ingressgateway-public
patches:
- path: metadata.labels.app
value: istio-ingressgateway-public # Change the label to istio-ingressgateway-public
- path: metadata.labels.istio
value: public-ingressgateway
- path: spec.selector.matchLabels.app
value: istio-ingressgateway-public
- path: spec.selector.matchLabels.istio
value: public-ingressgateway
- path: spec.template.metadata.labels.app
value: istio-ingressgateway-public
- path: spec.template.metadata.labels.istio
value: public-ingressgateway
- path: spec.template.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchExpressions[0].values[0]
value: istio-ingressgateway-public
- kind: Service
name: istio-ingressgateway-public
patches:
- path: metadata.labels.app
value: istio-ingressgateway-public
- path: metadata.labels.istio
value: public-ingressgateway
- path: spec.selector.app
value: istio-ingressgateway-public
- path: spec.selector.istio
value: public-ingressgateway
- kind: ServiceAccount
name: istio-ingressgateway-public-service-account
patches:
- path: metadata.labels.app
value: istio-ingressgateway-public
- path: metadata.labels.istio
value: public-ingressgateway
- kind: PodDisruptionBudget
name: istio-ingressgateway-public
patches:
- path: metadata.labels.app
value: istio-ingressgateway-public
- path: metadata.labels.istio
value: public-ingressgateway
- path: spec.selector.matchLabels.app
value: istio-ingressgateway-public
- path: spec.selector.matchLabels.istio
value: public-ingressgateway
pilot:
k8s:
replicaCount: 2
values:
global:
defaultNodeSelector:
beta.kubernetes.io/os: linux
proxy:
resources:
limits:
cpu: 500m
memory: 400Mi
requests:
cpu: 10m
memory: 80Mi
proxy_init:
resources:
limits:
cpu: 500m
memory: 400Mi
requests:
cpu: 10m
memory: 10Mi
gateways:
istio-ingressgateway:
podAntiAffinityLabelSelector:
- key: app
operator: In
values: istio-ingressgateway
topologyKey: kubernetes.io/hostname
cni:
excludeNamespaces:
- istio-system
- kube-system
- kube-public
- kube-node-lease
pilot:
autoscaleMin: 2
Environment where the bug was observed (cloud vendor, OS, etc)
- Cloud: Azure AKS
- Networking: Azure (advanced)
- OS:
Ubuntu 18.04.5 LTS - Kernel:
5.4.0-1035-azure - Container Runtime:
docker://19.3.14
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 10
- Comments: 27 (13 by maintainers)
I’m not sure if/when that might be supported, but you can approximate it a couple ways:
I think an AuthorizationPolicy like this might work:
That just supports basic wildcard matching right now I think, but full regex matching is on the horizon. If you need a full regex, you could also use the VirtualService to filter the traffic with something like this:
@rblaine95 Below one worked for me
I gave a few things a try there but no joy unfortunately. It seems this stuff is perhaps codified to the gateways? I think the problem with selecting on the IGWs with an
AuthorizationPolicyis multi-tenant platforms (like ours) tend to share gateways. Product engineering teams (the platform consumers) don’t get permission to put stuff in the namespace that the IGWs reside.Quick dump of things tried:
Firstly, @yangminzhu’s suggestion of hacking in the annotation at various levels (pod spec, deployment etc.) but didn’t have any luck.
Then I tried to get the annotations in place with our mesh deployment (which is
istioctlgeneration rather than in-cluster operator). I think the more modern Istio incantation here is something like (please correct me if wrong):This unfortunately doesn’t get accepted by our 1.8
istiod. It crash loops trying to parse the above from YAML to JSON. I tried a few variations but no luck. Perhaps someone else will have more joy.The
EnvoyFilterapproachThis got me the furthest. The
istioctl listenersobjective outlined by @kylebevans is met, but still seems to have no effect, and RBAC debug logging still shows the wrong IP, unlike at the Gateway. Maybe myEnvoyFilteris not sliding in at the right spot?Inspired by this suggestion from @anannaya, I tried the following, which worked perfectly:
The only other required change was adding the following annotation to the pod spec of my
istio-ingressgatewaydeployment, following the instructions from the documentation:proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 1 } }'If
numTrustedProxiesis set correctly for your topology, thenX-Envoy-External-Addresswill populate with the correct IP fromX-Forwarded-For(which can potentially be appended to by each layer 7 proxy along the request path), eliminating the need for a wildcard match.numTrustedProxiesis set to 1 because:Prior to getting this to work, I tried every conceivable configuration of the
fromandtofields with no success.Hope this helps someone.
@pokidov Below configuration at sidecar worked , I have some which routed via ALB So i preferred to apply at the app layer, If you have a all the apps goes via ALB then create Envoyfilter in istio-system namespace.
@jingslunt I have tried with 1 and 2 as well. Not worked for me.
Hi there, can this issue potentially be re-opened?
We’re experiencing a very similar issue (
Client -> AWS ALB -> IGW -> Sidecar -> Workload) whereremoteIPis the internal IP address of the AWS ALB, but not Client IP.Istio version:
1.13.1Setting this Istio config:
Doesn’t add the
"xffNumTrustedHops": 1,proxy config, even though the sidecar logs the correct config:Adding the annotation to the pods
proxy.istio.io/config: '{"gatewayTopology" : { "numTrustedProxies": 1 } }'also doesn’t add thexffNumTrustedHopsconfig when doingistioctl proxy-config listeners -o json -n $NS $POD | grep xffExample logs from sidecar:
After applying the EnvoyFilter detailed at https://github.com/istio/istio/issues/30166#issuecomment-796556188 that selects the workload pods, the
xffNumTrustedHopsconfig appears and theremoteIPis the correct Client IP:Side note: Also tried setting
gatewayTopology.numTrustedProxiesto higher values like2and5with no luck.If my understanding of the documentation is correct, this appears like it may be a bug.
FYI, the X-Envoy-External-Address isn’t calculated at the sidecar, and it is not what is used internally by RBAC. It’s calculated at the ingress gateway, and then the sidecars just pass it through among themselves.
Thanks for the very detailed report, that’s a very clear description.
I haven’t looked deep into this but just a quick check, have you tried setting the
numTrustedProxiesto the sidecar as well?