Kong Plugins as Microservices: Writing a Single-Plugin Server for Kong in Go
Tags: microservices, architecture
Published: 2022-03-08 15:14:28

Many developers and DevOps engineers have been deploying [Kong Gateway](https://konghq.com/kong/?utm_source=guest&utm_medium=devspotlight&utm_campaign=community) in front of their [microservices](https://konghq.com/learning-center/microservices/?utm_source=guest&utm_medium=devspotlight&utm_campaign=community)-based applications. While the extensive library of [built-in plugins](https://docs.konghq.com/hub/) 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](https://pkg.go.dev/github.com/Kong/go-pdk) (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](https://go.dev/dl/)
2. [Installed Kong](https://konghq.com/install/#kong-community) locally on your machine.
3. [Installed decK](https://docs.konghq.com/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 expects` go-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](https://docs.konghq.com/gateway-oss/2.1.x/go/#environment-consistency-constraints) 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](https://docs.konghq.com/gateway-oss/2.1.x/go/#development). 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](https://docs.konghq.com/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](https://docs.konghq.com/gateway-oss/2.1.x/go/) along with some other [examples and walkthroughs](https://konghq.com/blog/kong-gateway-go-plugin/?utm_source=guest&utm_medium=devspotlight&utm_campaign=community).