A really basic implementation of envoy External Processing Filter. This capability allows you to define an external gRPC server which can selectively process headers and payload/body of requests (see External Processing Filter PRD. Basically, your own unrestricted filter.
ext_proc
^
|
client -> envoy -> upstream
NOTE, this filter is really early and has a lot of features to implement!
you can find the source repo here
All we will demonstrate in this repo is the most basic functionality: manipulate headers and body-content on the request/response. I know, there are countless other ways to do this with envoy but just as a demonstration of writing the external gRPC server that this functionality uses. If interested, pls read on:
The scenario is like this
A) Manipulate outbound headers and body
ext_proc (delete specific header from client to upstream; append body content sent to upstream)
^
|
client -> envoy -> upstream
B) Manipulate response headers and body
ext_proc (delete specific header from upstream to client; append body content sent to client)
^
|
client <- envoy <- upstream
Specifically for (A), if a header key= “user” is sent by the client AND if the request is a POST, the external processing filter will
httpbin.org/post
If A is triggered, the couple of headers from httpbin are removed and the content type is set to text. Finally, the response body has the text qux
appended to it.
If the request type is GET or if the header ‘user’ is not present no modifications are made
First this code was just committed in PR 14385 so we will need envoy from the dev branch that was just committed
docker cp `docker create envoyproxy/envoy-dev:latest`:/usr/local/bin/envoy .
Now start the external gRPC server
go run grpc_server.go
This will start the gRPC server which will receive the requests from envoy.
I’m not sure if i’ve implemented the server correctly but the following does redact the user
header from upstream
As more features are implemented, you can handle new processing request types.
Now start envoy
./envoy -c server.yaml -l debug
Note, the external processing filter is by default configured to ONLY ask for the inbound request headers. What we’re going to do in code is first check if the header contains the specific value we’re interested in (i.,e header has a ‘user’ in it), if so, then we will ask for the request body, which will ask for the response headers which inturn will override and ask for the response body
http_filters:
- name: envoy.filters.http.ext_proc
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
failure_mode_allow: false
async_mode: false
request_attributes:
- user
response_attributes:
- server
processing_mode:
request_header_mode: "SEND"
response_header_mode: "SKIP"
request_body_mode: "NONE"
response_body_mode: "NONE"
request_trailer_mode: "SKIP"
response_trailer_mode: "SKIP"
grpc_service:
envoy_grpc:
cluster_name: ext_proc_cluster
Send in some requests
note, in each of tests, the upstream is
httpbin.org
which will just echo back the inbound request to the caller and display the headers and body it got back as the json response (i.,e the json response below is what httpbin saw)
In this case, we should not expect any modifications to take place.
$ curl -v -H "host: http.domain.com" --resolve http.domain.com:8080:127.0.0.1 http://http.domain.com:8080/get
> GET /get HTTP/1.1
> Host: http.domain.com
> User-Agent: curl/7.74.0
> Accept: */*
< HTTP/1.1 200 OK
< date: Wed, 31 Mar 2021 16:59:50 GMT
< content-type: application/json
< content-length: 311
< server: envoy
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 31
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "http.domain.com",
"User-Agent": "curl/7.74.0",
"X-Amzn-Trace-Id": "Root=1-6064aa86-1e8e99652e2c7ee003a2750f",
"X-Envoy-Expected-Rq-Timeout-Ms": "15000"
},
"origin": "108.51.98.171",
"url": "https://http.domain.com/get"
}
Here we’re also not expecting changes
$ curl -v -H "host: http.domain.com" --resolve http.domain.com:8080:127.0.0.1 -H "user: sal" http://http.domain.com:8080/get
> GET /get HTTP/1.1
> Host: http.domain.com
> User-Agent: curl/7.74.0
> Accept: */*
> user: sal
< HTTP/1.1 200 OK
< date: Wed, 31 Mar 2021 17:00:37 GMT
< content-type: application/json
< content-length: 331
< server: envoy
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 24
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "http.domain.com",
"User": "sal",
"User-Agent": "curl/7.74.0",
"X-Amzn-Trace-Id": "Root=1-6064aab5-1c5e1204091c69600d45b6ba",
"X-Envoy-Expected-Rq-Timeout-Ms": "15000"
},
"origin": "108.51.98.171",
"url": "https://http.domain.com/get"
}
In this case,we send in a POST but no user header so also no difference
$ curl -v -H "host: http.domain.com" -H "content-type: text/plain" --resolve http.domain.com:8080:127.0.0.1 -d 'foo' http://http.domain.com:8080/post
> POST /post HTTP/1.1
> Host: http.domain.com
> User-Agent: curl/7.74.0
> Accept: */*
> content-type: text/plain
> Content-Length: 3
< HTTP/1.1 200 OK
< date: Wed, 31 Mar 2021 17:03:06 GMT
< content-type: application/json
< content-length: 441
< server: envoy
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 8
{
"args": {},
"data": "foo",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "3",
"Content-Type": "text/plain",
"Host": "http.domain.com",
"User-Agent": "curl/7.74.0",
"X-Amzn-Trace-Id": "Root=1-6064ab4a-6df7e0d437a8ad2637c35fce",
"X-Envoy-Expected-Rq-Timeout-Ms": "15000"
},
"json": null,
"origin": "108.51.98.171",
"url": "https://http.domain.com/post"
}
We send a post request and the ‘user’ header below
What happens is that the external processing filter will
*pb.ProcessingRequest_RequestHeaders
,user
header*pb.ProcessingRequest_RequestBody
,bar
to the inbound request body*pb.ProcessingRequest_ResponseHeaders
,"access-control-allow-origin", "access-control-allow-credentials"
qux
)*pb.ProcessingRequest_ResponseBody
qux
to the response body sent by httpbin$ curl -v -H "host: http.domain.com" -H "content-type: text/plain" --resolve http.domain.com:8080:127.0.0.1 -H "user: sal" -d 'foo' http://http.domain.com:8080/post
> POST /post HTTP/1.1
> Host: http.domain.com
> User-Agent: curl/7.74.0
> Accept: */*
> content-type: text/plain
> user: sal
> Content-Length: 3
< HTTP/1.1 200 OK
< date: Wed, 31 Mar 2021 17:05:01 GMT
< server: envoy
< x-envoy-upstream-service-time: 24
< content-type: text/plain
< content-length: 453
{
"args": {},
"data": "foo baaar ",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "10",
"Content-Type": "text/plain",
"Host": "http.domain.com",
"User-Agent": "curl/7.74.0",
"X-Amzn-Trace-Id": "Root=1-6064abbd-1a54289105d3cec56cec7c9c",
"X-Envoy-Expected-Rq-Timeout-Ms": "15000"
},
"json": null,
"origin": "108.51.98.171",
"url": "https://http.domain.com/post"
}
qux
Thats it, i’ll be adding on more features as they become available to this repo.
Other reference envoy samples
This site supports webmentions. Send me a mention via this form.