Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go

Many developers and DevOps engineers have been deploying Kong Gateway in front of their microservices-based applications. While the extensive library of built-in plugins can add a lot of flexibility and power to your deployments, you might encounter th…

Many developers and DevOps engineers have been deploying Kong Gateway in front of their microservices-based applications. While the extensive library of built-in plugins can add a lot of flexibility and power to your deployments, you might encounter the occasional use case where you need a custom plugin that’s just not found in the library.

Fortunately, you can use Go to create and run plugins for Kong Gateway. This post will cover how to get set up for writing Kong plugins. We’ll look at the parts of the request/response lifecycle that you can tap into, and we’ll walk through an example of how to put it all together.

Custom plugins for Kong are achievable using a Go-specific plugin server that uses the sidecar model to load and run a plugin written in Go dynamically. Communication between the plugin and the plugin server is done through a Unix domain socket. Luckily, all of this is made simple through Kong’s Go Plugin Development Kit (PDK).

Why would you write your own plugin?

Ultimately, the ability to write your own Go plugin and integrate it with Kong Gateway provides nearly limitless flexibility during the request and response lifecycle. A classic example would be simple request and response enrichment: Imagine adding or removing headers to influence request routing or incorporating customized request validation or logging requests for auditing. Harnessing Kong Gateway’s power while building in custom gateway logic for your unique business case gives you the best of both worlds.

Enough talk. Let’s get to it.

Development setup

For this example, we’ll assume that you have already done the following:

  1. Installed Go
  2. Installed Kong locally on your machine.
  3. Installed decK to manage the service configuration, though you can also configure Kong using the Admin API.

The first step for environment setup is to create a directory and our Go mod file:

$ mkdir kong-go-plugin
$ cd kong-go-plugin/
~/kong-go-plugin$ go mod init kong-go-plugin
go: creating new go.mod: module kong-go-plugin

Next, we run go get and go build to build the plugin server:

~/kong-go-plugin$ go get -d -v github.com/Kong/go-pluginserver
go: downloading github.com/Kong/go-pluginserver v0.6.1
go: downloading github.com/Kong/go-pdk v0.6.0
go: downloading github.com/ugorji/go v1.2.1
go: downloading github.com/ugorji/go/codec v1.2.1
go get: added github.com/Kong/go-pluginserver v0.6.1
~/kong-go-plugin$ go build github.com/Kong/go-pluginserver

By default, Kong expectsgo-pluginserver to be in /usr/local/bin, but you can modify this behavior by changing the value for go_pluginserver_exe in your Kong configuration. For the sake of consistency, let’s move the newly built binary to the default location.

~/kong-go-plugin$ sudo mv go-pluginserver /usr/local/bin/

One other default that we’ll want to modify is go_plugins_dir which defaults to off. This is where Kong looks for our Go plugins. For this example, we’ll use $HOME/goplugins/, so if you’re following along, you’ll need to update your configuration to match.

Before diving into the code, one last thing is that consistency issues can occur. They normally manifest as error messages like the following:

failed to open plugin kong: plugin.Open("/path/go-plugins/myplugin"): plugin was built with a different version of package github.com/Kong/go-pdk/bridge

If you do run into that message, check out the documentation here for more info on possible root causes. Now that we set up our development environment, we can start coding our plugin!

Coding time!

Our next step is to write the plugin and verify it works as expected. We’ll start with a simple bit of code to add a header to a response. If you’re following along, you can copy this directly into a file called goplug.go:

package main

import (
    "github.com/Kong/go-pdk"
)

type Config struct {
    Attach bool
}

func New() interface{} {
    return &Config{}
}

func (c *Config) Access(kong *pdk.PDK) {
    if c.Attach {
        kong.ServiceRequest.SetHeader("x-goplug", "Set by custom go plugin")
    }
}

If you’ve worked with Go before, this should be pretty straightforward. That said, you can find more details in the Kong Go Plugin Documentation. The documentation explains the structs and functions, and it provides a full list of the phases you can tap into. We’re using the Access phase at this point, so we can attach the header before it gets proxied to the upstream service. This is useful in cases where you want to add data to the response based on some incoming criteria that the upstream service can use.

Next, we need to build our plugin and “install” it with go build -buildmode plugin goplug.go && mv goplug.so ~/goplugins/

After that, we restart Kong to ensure it has picked up our new plugin.

~/kong-go-plugin$ sudo kong restart

Now, our plugin is loaded and ready to use, but we still need a service to use it on. I’ve used decK and added the following to a kong.yaml file to test out the plugin. All you need to do is run deck sync once you’ve created the kong.yaml file.

_format_version: "1.1"
services:
- host: mockbin.org
  name: example_service
  port: 80
  protocol: http
  plugins:
  - name: goplug
    config:
      attach: true
  routes:
  - name: mocking
    paths:
    - /mock
    strip_path: true

With our plugin loaded and service created, we’re ready for a test. Simply make a curl request to the service and we’ll see the plugin in action!

~/kong-go-plugin$ curl -s  -i -X GET http://localhost:8000/mock/request \
| grep "x-goplug"
x-goplug: Set by custom go plugin

Success! For good measure, let’s tie into one more phase of the request and response lifecycle. In this next function, we’ll tap into the Response phase to add a debugging header in case our plugin runs into issues.

func (c *Config) Response(kong *pdk.PDK) {
    v := runtime.Version()
    kong.Response.AddHeader("x-goplug-go-version", v)
}

Add the above function to goplug.go, then repeat the process to rebuild the plugin. Next, restart Kong. When we rerun our curl command, we see that x-goplug is included with the request to the upstream service, but it is removed before sending a response back to the client.

$ curl -s -i -X GET http://localhost:8000/mock/request \
| grep "x-goplug-go-version"
x-goplug-go-version: go1.16.5

Let’s draw some parallels to real-world applications. We’ve shown here how you can take data from a request and turn that into actionable information, ready to be processed by an upstream service fronted by Kong Gateway. What’s even better is that all of this background work is transparent to the client. We’ve also shown how you can modify the response to the client. With this ability, you can ease troubleshooting or even pass back rate-limiting data (such as the number of remaining API requests per day or per hour) to keep your customers informed of their utilization.

Wrap up

In this post we’ve covered a lot, so let’s wrap it up with a brief review. Here’s what we’ve covered:

  • What you need to develop Go plugins for use with Kong Gateway
  • Why you might build a custom plugin rather than use a plugin that’s built-in with Kong Gateway
  • Set up a local development environment that supports creating Go-based Kong plugins
  • Used that local development environment to tap into the request/response lifecycle to modify the data on the fly

Now that the process is down, it’s easily repeatable for whatever use case you may have for Go-based Kong plugin development. You can find the Kong documentation for Go support on Kong’s website along with some other examples and walkthroughs.


Print Share Comment Cite Upload Translate
APA
DEV Community | Sciencx (2024-03-29T12:36:49+00:00) » Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go. Retrieved from https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/.
MLA
" » Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go." DEV Community | Sciencx - Tuesday March 8, 2022, https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/
HARVARD
DEV Community | Sciencx Tuesday March 8, 2022 » Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go., viewed 2024-03-29T12:36:49+00:00,<https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/>
VANCOUVER
DEV Community | Sciencx - » Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go. [Internet]. [Accessed 2024-03-29T12:36:49+00:00]. Available from: https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/
CHICAGO
" » Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go." DEV Community | Sciencx - Accessed 2024-03-29T12:36:49+00:00. https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/
IEEE
" » Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go." DEV Community | Sciencx [Online]. Available: https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/. [Accessed: 2024-03-29T12:36:49+00:00]
rf:citation
» Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go | DEV Community | Sciencx | https://www.scien.cx/2022/03/08/kong-plugins-as-microservices-writing-a-single-plugin-server-for-kong-in-go/ | 2024-03-29T12:36:49+00:00
https://github.com/addpipe/simple-recorderjs-demo