External Auth providers can't set multiple headers of the same key w/ gRPC
#40,255 opened on 2025年7月16日
Repository metrics
- Stars
- (27,997 stars)
- PR merge metrics
- (平均マージ 8d) (30d で 378 merged PRs)
説明
I have a gRPC service implementing the externalAuth xDS service. It is returning headers to add to the request to the upstream service. This works perfectly if you just send one. If I want to send multiple copies of the header, only the last one get's sent.
Versions: envoy version: 8d14f6fc8fe9703ff17d4377d9053b3cbbe85dea/1.33.3-dev/Clean/RELEASE/BoringSSL from istio 1.25.2
I see in the docs here that the
Note that the [append field in HeaderValueOption](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#envoy-v3-api-field-config-core-v3-headervalueoption-append) defaults to false when used in this message. By setting the append field to true, the filter will append the correspondent header value to the matched request header. By leaving append as false, the filter will either add a new header, or override an existing one if there is a match.
That's a deprecated field. But I tried it either way. I even attempted to use the proper non-deprecated option(which happens to be the 0 option in enum, so it's identical to not setting)
Here is the OK auth response that my service sent back to envoy
status {
}
ok_response {
headers {
header {
key: "trial-1"
value: "a"
}
}
headers {
header {
key: "trial-1"
value: "b"
}
}
headers {
header {
key: "trial-2"
value: "a"
}
append {
value: true
}
}
headers {
header {
key: "trial-2"
value: "b"
}
append {
value: true
}
}
headers {
header {
key: "trial-3"
value: "a"
}
}
headers {
header {
key: "trial-3"
value: "b"
}
}
}
Only tiral-1 and trial-3 headers were added to the authenticated upstream request. And only b. The a value was overwritten.
It seems like append means only append if it was there first. and non-append means overwrite.
The code for setting all 3 of these response headers looked like
private fun trial1(string: String): HeaderValueOption? = HeaderValueOption.newBuilder()
.setAppendAction(HeaderValueOption.HeaderAppendAction.APPEND_IF_EXISTS_OR_ADD)
.setHeader(
HeaderValue.newBuilder()
.setKey("trial-1")
.setValue(string),
).build()
private fun trial2(string: String): HeaderValueOption? = HeaderValueOption.newBuilder()
.setAppend(BoolValue.of(true))
.setAppendAction(HeaderValueOption.HeaderAppendAction.APPEND_IF_EXISTS_OR_ADD)
.setHeader(
HeaderValue.newBuilder()
.setKey("trial-2")
.setValue(string),
).build()
private fun trial3(string: String): HeaderValueOption? = HeaderValueOption.newBuilder()
.setHeader(
HeaderValue.newBuilder()
.setKey("trial-3")
.setValue(string),
).build()
The ext-auth config in the istio envoy filter is
patch:
operation: INSERT_AFTER
value:
name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
grpc_service:
envoy_grpc:
cluster_name: outbound|50051||api-authz-xds.public-sandbox-ingress.svc.cluster.local
authority: api-authz-xds.public-sandbox-ingress.svc.cluster.local
timeout: 0.5s
include_peer_certificate: true
transport_api_version: V3
allowed_headers:
patterns:
- exact: "partner-cert-subject"
- exact: "content-type"
- exact: ":method"
- exact: ":path"
- exact: ":authority"
This is working, and we're currently using an added request header and CSVing the values, but the ideal way is to have the repeated headers.
I've seen a few other issues about this from a while back, I'll attempt to rediscover them and link them after.