DEV Community

Cover image for Envoy External Authorization with Golang GRPC service
prakash chokalingam
prakash chokalingam

Posted on

Envoy External Authorization with Golang GRPC service

Envoy is a cloud native opensource proxy server. The Envoy proxy offers a variety of http filters to handle incoming requests.

Out of them External authorisation is a filter type that directs an incoming request to an external service and waits for its authorisation to continue the request.The external service, which has the ability to modify or suspend the incoming request, can be either a grpc_service or a http_service.

Working example: prakashchokalingam/envoy_ext_auth_grpc_go

envoy filter to go flow diagram

Let's look at connecting a GRPC-based Golang service using the Envoy external auth filter.

Create a golang grpc based server:

main.go
package main

import (
  "fmt"
  "net"
  "google.golang.org/grpc"
)

func main() {
  endPoint := fmt.Sprintf("localhost:%d", 3001)
  listen, err := net.Listen("tcp", endPoint)

  grpcServer := grpc.NewServer()
  grpcServer.Serve(listen)
}
Enter fullscreen mode Exit fullscreen mode

Extend with go-control-plane protobuf

The above server should be extended and registered with the envoyproxy go-control-plane protobuf server with a check function defined struct.

main.go
package main

import (
  "fmt"
  "net"

   auth_pb "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
  "google.golang.org/grpc"
)

func main() {
  // struct with check method
  type AuthServer struct {}

  func (server *AuthServer) Check(
    ctx context.Context, 
    request *auth_pb.CheckRequest,
  ) (*auth_pb.CheckResponse, error) {
    fmt.println("All request goes through me")

    // block if path is /private
    path = request.Attributes.Request.Http.Path[1:]
    if path == 'private' {
      return nil, fmt.Errorf('private request not allowed')
    }

    // allow all other requests
    return &auth_pb.CheckResponse{}, nil
  }


  endPoint := fmt.Sprintf("localhost:%d", 3001)
  listen, err := net.Listen("tcp", endPoint)

  grpcServer := grpc.NewServer()

  // register envoy proto server
  server := &AuthServer{}
  auth_pb.RegisterAuthorizationServer(grpcServer, server)

  grpcServer.Serve(listen)
}
Enter fullscreen mode Exit fullscreen mode

If nil is returned in the check method rather than &auth_pb.CheckResponse, the request will be suspended with a 403 error code.

Note: Envoy will also return 403 if the service is offline or unreachable.

Last but not least, set up your Envoy

In your envoy config add the filter envoy.filters.http.ext_authzand point it to the Go service cluster

envoy.yml
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 80 }
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                codec_type: auto
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                   ...
                http_filters:
                  - name: envoy.filters.http.ext_authz
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
                      transport_api_version: v3
                      grpc_service:
                        envoy_grpc:
                          cluster_name: go_grpc_cluster
                      include_peer_certificate: true
                        ...
  clusters:
    - name: go_grpc_cluster
      connect_timeout: 0.25s
      type: LOGICAL_DNS
      typed_extension_protocol_options:
        envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
          "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
          explicit_http_config:
            http2_protocol_options: {}
      load_assignment:
        cluster_name: go_grpc_cluster
        endpoints:
        - lb_endpoints:
          - endpoint:
              address:
                socket_address:
                  address: 127.0.0.1
                  port_value: 3001
Enter fullscreen mode Exit fullscreen mode

To see an example snippet for customising headers, click here

Check out this repository for a fully functional code sample

GitHub logo prakashchokalingam / envoy_ext_auth_grpc_go

Working example of envoy external auth filter with go lang grpc service

Screenshot 2023-06-20 at 10 16 03 PM

Example repo to demonstrate Envoy External Authorization with Golang GRPC service

This repository provides an envoy configuration file with an external auth filter activated for all incoming routes at port 8080

The envoy is configured with two clusters,

go_grpc_filter

The filter envoy.filters.http.ext_authz in envoy is pointed at this go grpc cluster. All incoming requests will be forwarded to this cluster.

The Check method will be called during a request; it then adds a custom header to all other requests and rejects requests with the path '/private'.

Request grpc_filter status http_server_response
/ x-custom-header = "Hello World" 200 Hello World
/ private 403 - -

go_simple_http

It is a straightforward Golang HTTP server that merely emits the custom header value x-custom-header added via the go_grpc_filter cluster.

To run this example

  1. Start the envoy server
envoy -c envoy.yml
Enter fullscreen mode Exit fullscreen mode
  1. start the go_grpc_filter & go_simple_http servers by navigating to the cluster root.
go run main.go
Enter fullscreen mode Exit fullscreen mode

Top comments (0)