Envoy filters in Go

Overview

EGo-Demo

EGo Splash

This is a demo of how to build a Golang filter for Envoy, based on the Envoy Filter Example project, by using Go's CGo feature.

It is still a little rough on the edges, though probably good enough to help understand most of the challenges and benefits of this approach.

Design Principles

  • Provide a Go-native experience for Go developers
  • Support zero-copy data access where possible
  • Unopinionated interface: reflect the envoy C++ interface classes as close as possible

Successes

  • Parsing and passing filter configuration from C to Go with zero-copy and leveraging Envoy's protobuf validation mechanism
  • Communication between C and Go with zero-copy and strongly parallel interface design
  • Allow the use of goroutines with minimal discipline required (Pin/Post/Unpin)
  • Creating new filters without having to touch C code (except when "lifting up" new envoy interfaces)
  • Support interactions with SDS via Envoy's built-in client

Challenges

This project is still in its infancy, and there are a couple of technical challenges still waiting to be solved.

Dependency Cycles

In order to call Go functions from C, the go libraries need to be built first (as this produces the required C headers). However, the Go libraries also have a dependency on C libraries (to facilitate calls to envoy functions).

This can, in most cases, be mitigated by careful separation of the functions needed for the Go->C interactions from the functions implementing the envoy L7 filter interfaces.

When there are functions that are needed on both sides, this often requires creativity. A good example for this is the onPost handler, which needs to be passed as a callback on the Go->C side, but has an implementation that needs to make a C->Go call.

Since this obviously isn't a limitation of linkers or C, the situation could probably mitigated by extending the Bazel tooling (for example, by separating the generation of Go C headers and the Go library).

CGo limitations

Currently, there can only be one CGo library, and its main() function needs to reside in the same package containing the CGo code. This means that egofilters needs to be referenced from the ego package and can't contain CGo code. This is probably the most significant issue because it is in direct opposition to the desired architecture.

Replication of Envoy classes as Go interfaces

This is a work intensive process, and the only reason we chose to take on this challenge is that it typically is a one-off effort that will become less notable over time.

Keeping up with Envoy's changing C++ interface also requires regular effort, and in fact, this project currently doesn't account for different Envoy versions, although it probably should.

Prerequisites

Bazel

MacOS

To set up bazel on MacOS, install Homebrew and then

brew install bazelbuild/tap/bazelisk
brew install coreutils wget cmake libtool go bazel automake ninja clang-format autoconf aspell

(Don't worry if you get a warning for a symlink that couldn't be updated because bazelisk is sitting on it -- this is by design)

Building

git submodule update --init  # clone the linked envoy tag into /envoy
bazel build //:envoy         # build envoy with the new libraries added

Note: This version requires Envoy v1.14

Trying It Out

This demo includes two example filters:

  • getheader is a very basic filter that showcases using go-routines and the Go runtime library for HTTP requests. Its sole purpose is to retrieve response header hdr from a GET request for URL src, and inject it as request header key into the request to the upstream service.

  • security is a more elaborate piece outlining a framework to accomodate many different custom authentication methods, to show how things might pan out at scale. This is showcased via a simple pseudo-HMAC filter with the actual checksum calculations delegated to a separate REST service to once more demonstrate how to align Go-routines and envoy worker threads as well as to motivate the use of SDS based secrets.

To play with it, go to the repository root, and do

bazel run ego-demo

This spins up a simple echo service as upstream for all requests, an HMAC authentication provider sidecar, and the Envoy proxy configured to showcase the Go-based filters provided with this demo.

curl -iX GET 'http://127.0.0.1:8080/Hello,world!'

Note the X-Getheader-Result in the response which was injected into the request by the getheader filter

curl -iX GET 'http://127.0.0.1:8080/hmac/unsigned' -H 'Authorization: client_id:123'

This should produce a 401 Unauthorized response

curl -iX GET 'http://127.0.0.1:8080/hmac/unsigned' -H 'Authorization: client_id:17'

The expectation would be a 200 OK response.

curl -iX GET 'http://127.0.0.1:8080/hmac/signed' -H 'Authorization: client_id:15'

This should also result in a 200 OK response, and a x-custom-auth-signature-hmac-sha256: 200_nnn response header (which clearly isn't the SHA256 HMAC but just the status code with the length of the response body appended).

Tinkering

The Go code is integrated with bazel via rules_go. The bazel rules areautomatically generated by gazelle, and interfaces mocks are generated via mockery.

Running Tests

The EGo interface layer still deserves a bit more rigorous testing, only some mind-numbing permutation testing for the most critical pieces has already been implemented so far, please budget a couple of minutes for this to run.

bazel test //ego/...

Since the HMAC filter is a gutted version of a more elaborate thing, coverage is relatively good (although not very pretty to read).

bazel test //egofilters/...

Note for MacOS users

Please see Envoy issue #10478 if some of the C++ tests fail with this message:

dyld: Symbol not found: _program_invocation_name

This is most likely means XCode needs to be installed.

Updating the Go Rules

From the repository root, do

bazel run gazelle

Adding new Go Dependencies

From the repository root, do

bazel run //:gazelle -- update-repos -from_file=go.mod

Bazel and Go Interface Mocks

Make sure you have installed mockery. Then, from the repository root, do

tools/copy_pb_go.py  # update generated protobuf bindings

cd egofilters/mock
go generate

cd ../../ego/test/go/mock
go generate

cd ../../../..
bazel run gazelle    # just in case

Bazel integration of this is work in progress...

You might also like...
Go package implementing Bloom filters

Go package implementing Bloom filters

Go package implementing Bloom filters

Bloom filters A Bloom filter is a concise/compressed representation of a set, where the main requirement is to make membership queries; i.e., whether

Sync distributed sets using bloom filters

goSetReconciliation An implementation to sync distributed sets using bloom filters. Based on the paper "Low complexity set reconciliation using Bloom

The Hyperscale InputFilter library provides a simple inputfilter chaining mechanism by which multiple filters and validator may be applied to a single datum in a user-defined order.

Hyperscale InputFilter Branch Status Coverage master The Hyperscale InputFilter library provides a simple inputfilter chaining mechanism by which mult

Custom text condition for filters

Custom text condition for filters Install go get -v github.com/blins/condition Use func init() { condition.RegisterConditionFabric("prefix", cond

Small microservice to apply filters to images

Small microservice to apply filters to images How to use Send an image file as p

Lightweight, CRD based envoy control plane for kubernetes

Lighweight, CRD based Envoy control plane for Kubernetes: Implemented as a Kubernetes Operator Deploy and manage an Envoy xDS server using the Discove

Envoy file based dynamic routing using kubernetes config map

Envoy File Based Dynamic Routing Config mapを使用してEnvoy File Based Dynamic Routingを実現します。 概要 アーキテクチャとしては、 +----------+ +--------------+ +-----------

A WASM Filter for Envoy Proxy written in Golang

envoy-proxy-wasm-filter-golang A WASM Filter for Envoy Proxy written in Golang Build tinygo build -o optimized.wasm -scheduler=none -target=wasi ./mai

json to rpc example with envoy, go, grpc, redis

grpc-redis-envoy-example json to rpc example with envoy, go, grpc, redis Run Make sure you have docker installed locally Run the services docker-com

Json to rpc example with envoy, go, grpc, nats

grpc-nats-envoy json to rpc example with envoy, go, grpc, redis This repo is a mirror of https://github.com/charlesonunze/grpc-redis-envoy-example It

Envoy introspection for golang

Envoy introspection Demo Build the extension (.wasm file): make wasm Start the upstream service: docker run -d -p 3030:80 kennethreitz/httpbin Run th

Envoy Oauth2 Filter helloworld
Envoy Oauth2 Filter helloworld

Envoy Oauth2 Filter A simple sample demonstrating Envoy's Oauth2 Filter. Basically, this filter will handle all the details for OAuth 2.0 for Web Serv

Using Envoy Proxy to load-balance gRPC services on GKE with header value based Session Affinity

Using Envoy Proxy to load-balance gRPC services on GKE with header value based S

Sesame: an Ingress controller for Kubernetes that works by deploying the Envoy proxy as a reverse proxy and load balancer

Sesame Overview Sesame is an Ingress controller for Kubernetes that works by dep

Simple grpc web and grpc transcoding with Envoy
Simple grpc web and grpc transcoding with Envoy

gRPC Web and gRPC Transcoding with Envoy This is a simple stand-alone set of con

Let's implement some basic ZeroMQ publisher and subscriber in Golang. Utilize Envoy as a proxy.
Let's implement some basic ZeroMQ publisher and subscriber in Golang. Utilize Envoy as a proxy.

Envy proxy with ZeroMQ Solution tested on DigitalOcean Droplet. In case of re-creation VM follow this article. Introduction Let's implement some basic

Releases(v0.0.1-1.14)
Owner
Grab
Together with Grab, we can move forward together to make SEA a better place.
Grab
Json to rpc example with envoy, go, grpc, nats

grpc-nats-envoy json to rpc example with envoy, go, grpc, redis This repo is a mirror of https://github.com/charlesonunze/grpc-redis-envoy-example It

Charles Onunze 1 Dec 7, 2021
Envoy introspection for golang

Envoy introspection Demo Build the extension (.wasm file): make wasm Start the upstream service: docker run -d -p 3030:80 kennethreitz/httpbin Run th

Peter Jausovec 1 Nov 30, 2021
Using Envoy Proxy to load-balance gRPC services on GKE with header value based Session Affinity

Using Envoy Proxy to load-balance gRPC services on GKE with header value based S

Daniel William Clarke 2 Aug 24, 2022
Simple grpc web and grpc transcoding with Envoy

gRPC Web and gRPC Transcoding with Envoy This is a simple stand-alone set of con

null 0 Dec 25, 2021
Let's implement some basic ZeroMQ publisher and subscriber in Golang. Utilize Envoy as a proxy.

Envy proxy with ZeroMQ Solution tested on DigitalOcean Droplet. In case of re-creation VM follow this article. Introduction Let's implement some basic

Jakub Wołynko 0 Jan 25, 2022
CLI filters the contents of the csv file according to the filters from the another file.

filtercsv CLI filters the contents of the csv file according to the filters from the another file. Made to process big files by a lots of filters. By

null 0 Dec 2, 2021
Envoy filters in Go

EGo-Demo This is a demo of how to build a Golang filter for Envoy, based on the Envoy Filter Example project, by using Go's CGo feature. It is still a

Grab 35 Oct 7, 2022
Envoy utility to process envoy config for fast development and debugging.

envoyconf-tools Envoy is a proxy, really awesome and we are devs who often use it, face errors and struggle to debug it, when envoy config's source is

VOrishirne 3 Oct 31, 2021
Go package implementing Bloom filters

Bloom filters A Bloom filter is a representation of a set of n items, where the main requirement is to make membership queries; i.e., whether an item

Will Fitzgerald 1.8k Dec 30, 2022
Go library implementing xor filters

xorfilter: Go library implementing xor filters Bloom filters are used to quickly check whether an element is part of a set. Xor filters are a faster a

null 607 Dec 30, 2022