Introduction
Golang plugins are a very flexible and powerful way to extend the functionality of Tyk by attaching custom logic written in Go to hooks in the Tyk middleware chain. The chain of middleware is specific to an API and gets created at API load time. When Tyk Gateway performs an API re-load it also loads any custom middleware and “injects” them into a chain to be called at different stages of the HTTP request life cycle. For a quick-start guide to working with Go plugins, start here. The Go plugin writing guide provides details of how to access dynamic data (such as the key session object) from your Go functions. Combining these resources provides you with a powerful set of tools for shaping and structuring inbound traffic to your API.Supported plugin types
All of Tyk’s custom middleware hooks support Go plugins. They represent different stages in the request and response middleware chain where custom functionality can be added.- Pre - supports an array of middleware that run before any others (i.e. before authentication)
-
Auth - this middleware performs custom authentication and adds API key session info into the request context and can be used only if the API definition has both:
"use_keyless": false
"use_go_plugin_auth": true
-
Post-Auth - supports an array of middleware to be run after authentication; at this point, we have authenticated the session API key for the given key (in the request context) so we can perform any extra checks. This can be used only if the API definition has both:
"use_keyless": false
- an authentication method specified
- Post - supports an array of middleware that run at the very end of the middleware chain, just before Tyk makes a round-trip to the upstream target
-
Response - run only at the point the response has returned from a service upstream of the API Gateway; note that the method signature for Response Go plugins is slightly different from the other hook types
The
use_keyless
anduse_go_plugin_auth
fields are populated automatically with the correct values if you add a plugin to the Auth or Post-Auth hooks when using the Tyk Dashboard.
Custom Go plugin development flow
Go Plugins need to be compiled to native shared object code, which can then be loaded by Tyk Gateway. We recommend that you familiarize yourself with the following official Go documentation to help you work effectively with Go plugins:- The official plugin package documentation - Warnings
-
Tutorial: Getting started with multi-module workspaces
Plugins are currently supported only on Linux, FreeBSD, and macOS, making them unsuitable for applications intended to be portable.
Tyk Plugin Compiler
We provide the Tyk Plugin Compiler docker image, which we strongly recommend is used to build plugins compatible with the official Gateway releases. That tool provides the cross compilation toolchain, Go version used to build the release, ensures that compatible flags are used when compiling plugins (such as-trimpath
, CC
, CGO_ENABLED
, GOOS
, GOARCH
) and also works around known Go issues such as:
- https://github.com/golang/go/issues/19004
- https://www.reddit.com/r/golang/comments/qxghjv/plugin_already_loaded_when_a_plugin_is_loaded/
Setting up your environment
It’s important to understand the need for plugins to be compiled using exactly the same environment and build flags as the Gateway. To simplify this and minimise the risk of compatibility problems, we recommend the use of Go workspaces, to provide a consistent environment. To develop plugins without using the Tyk Plugin Compiler, you’ll need:- Go (matching the version used in the Gateway, which you can determine using
go.mod
). - Git to check out Tyk Gateway source code.
- A folder with the code that you want to build into plugins.
/tyk-release-x.y.z
- the Tyk Gateway source code/plugins
- the plugins/go.work
- the Go workspace file/go.work.sum
- Go workspace package checksums
Steps for Configuration:
-
Checking out Tyk Gateway source code
This example uses a particular
release-5.3.6
branch, to match Tyk Gateway release 5.3.6. With newergit
versions, you may pass--branch v5.3.6
and it would use the tag. In case you want to use the tag it’s also possible to navigate into the folder and issuegit checkout tags/v5.3.6
. -
Preparing the Go workspace
Your Go workspace can be very simple:
- Create a
.go
file containing the code for your plugin. - Create a
go.mod
file for the plugin. - Ensure the correct Go version is in use.
The following snippet provides you with a way to get the exact Go version used by Gateway from it’s go.mod file:go mod edit -json go.mod | jq -r .Go
(e.g.1.22.7
)
- We created a plugins folder and initialzed a
go
project usinggo mod
command. - Set the Go version of
go.mod
to match the one set in the Gateway. - Initialzied the project with sample plugin
go
code.
- Create a
-
Creating the Go workspace
To set up the Go workspace, start in the directory that contains the Gateway and the Plugins folder. You’ll first, create the
go.work
file to set up your Go workspace, and include thetyk-release-5.3.6
andplugins
folders. Then, navigate to the plugins folder to fetch the Gateway dependency at the exact commit hash and rungo mod tidy
to ensure dependencies are up to date. Follow these commands:The following snippet provides you to get the commit hash exactly, so it can be used withgo get
.git rev-parse HEAD
go.work
) should look like this: -
Building and validating the plugin
Now that your Go workspace is ready, you can build your plugin as follows:
These steps build both the Gateway and the plugin. You can use the Gateway binary that you just built to test that your new plugin loads into the Gateway without having to configure and then make a request to an API using this command:You should see an output similar to:The log shows that the plugin has correctly loaded into the Gateway and that its
init
function has been successfully invoked. - Summary In the preceding steps we have put together an end-to-end build environment for both the Gateway and the plugin. Bear in mind that runtime environments may have additional restrictions beyond Go version and build flags to which the plugin developer must pay attention. Compatibility in general is a big concern when working with Go plugins: as the plugins are tightly coupled to the Gateway, consideration must always be made for the build restrictions enforced by environment and configuration options. Continue with Loading Go Plugins into Tyk.
Debugging Golang Plugins
Plugins are native Go code compiled to a binary shared object file. The code may depend oncgo
and require libraries like libc
provided by the runtime environment. The following are some debugging steps for diagnosing issues arising from using plugins.
Warnings
The Plugin package - Warnings section in the Go documentation outlines several requirements which can’t be ignored when working with plugins. The most important restriction is the following:Runtime crashes are likely to occur unless all parts of the program (the application and all its plugins) are compiled using exactly the same version of the toolchain, the same build tags, and the same values of certain flags and environment variables.
Using Incorrect Build Flags
When working with Go plugins, it’s easy to miss the restriction that the plugin at the very least must be built with the same Go version, and the same flags (notably-trimpath
) as the Tyk Gateway on which it is to be used.
If you miss an argument (for example -trimpath
) when building the plugin, the Gateway will report an error when your API attempts to load the plugin, for example:
-race
in the plugin but the gateway was built with -race
, the following error will be reported:
nm -gD testplugin.so | grep testplugin
00000000014db4b0 R go:link.pkghashbytes.testplugin 000000000170f7d0 D go:link.pkghash.testplugin 000000000130f5e0 T testplugin.AddFooBarHeader 000000000130f900 T testplugin.AddFooBarHeader.deferwrap1 000000000130f980 T testplugin.AuthCheck 0000000001310100 T testplugin.AuthCheck.deferwrap1 000000000130f540 T testplugin.init 0000000001310ce0 T testplugin.init.0 0000000001ce9580 D testplugin..inittask 0000000001310480 T testplugin.InjectConfigData 0000000001310180 T testplugin.InjectMetadata 0000000001d2a3e0 B testplugin.logger 0000000001310cc0 T testplugin.main 0000000001310820 T testplugin.MakeOutboundCall 0000000001310c40 T testplugin.MakeOutboundCall.deferwrap1ctx.GetOASDefinition(r)
returns an OAS
object containing the Tyk OAS API definition.
The Go data structure can be found here.
Working with Tyk Classic APIs
The API definition can be accessed as follows:ctx.GetDefinition(r)
returns an APIDefinition object containing the Tyk Classic API Definition.
The Go data structure can be found here.
Accessing the session object
When Tyk passes a request to your plugin, the key session object is made available as part of the request context. This can be accessed as follows:Monitoring instrumentation for custom plugins
All custom middleware implemented as Golang plugins support Tyk’s current built in instrumentation. The format for an event name with metadata is:"GoPluginMiddleware:" + Path + ":" + SymbolName
, e.g., for our example, the event name will be:
.exec_time
suffix:
Creating a custom response plugin
As explained here, you can register a custom Go plugin to be triggered in the response middleware chain. You must configure thedriver
field to goplugin
in the API definition when registering the plugin.
Response plugin method signature
To write a response plugin in Go you need it to have a method signature as in the example below i.e.func(http.ResponseWriter, *http.Response, *http.Request)
.
You can then access and modify any part of the request or response. User session and API definition data can be accessed as with other Go plugin hook types.
FooBarBundle.zip
contents. It is just a ZIP archive with two files archived inside:
AddFooBarHeader.so
- this is our Golang pluginmanifest.json
- this is a special file with meta information used by Tyk’s bundle loader
manifest.json
:
get_time
is not set:
get_time=1
query string parameter:
"abc"
should work).
Authentication will fail with the wrong API key:
Upgrading your Tyk Gateway
When upgrading your Tyk Gateway deployment, you need to re-compile your plugin with the new version. At the moment of loading a plugin, the Gateway will try to find a plugin with the name provided in the API definition. If none is found then it will fall back to search the plugin file with the name:{plugin-name}_{Gw-version}_{OS}_{arch}.so
.
Since Tyk v4.1.0, the compiler automatically creates plugin files following this convention so when you upgrade, say from Tyk v5.2.5 to v5.3.0 you only need to have the plugins compiled for v5.3.0 before performing the upgrade.
This diagram shows how every Tyk Gateway will search and load the plugin binary that it is compatible with.
