"use_keyless": false
"use_go_plugin_auth": true
"use_keyless": false
use_keyless
and use_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.-trimpath
, CC
, CGO_ENABLED
, GOOS
, GOARCH
) and also works around known Go issues such as:
go.mod
)./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 checksumsrelease-5.3.6
branch, to match Tyk Gateway release 5.3.6. With newer git
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 issue git checkout tags/v5.3.6
.
.go
file containing the code for your plugin.go.mod
file for the plugin.go mod edit -json go.mod | jq -r .Go
(e.g. 1.22.7
)go
project using go mod
command.go.mod
to match the one set in the Gateway.go
code.go.work
file to set up your Go workspace, and include the tyk-release-5.3.6
and plugins
folders. Then, navigate to the plugins folder to fetch the Gateway dependency at the exact commit hash and run go mod tidy
to ensure dependencies are up to date.
Follow these commands:
go get
.
git rev-parse HEAD
go.work
) should look like this:
init
function has been successfully invoked.
cgo
and require libraries like libc
provided by the runtime environment. The following are some debugging steps for diagnosing issues arising from using plugins.
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.
-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:
-trimpath
, -race
need to match.CC
value for the build (CGO).CGO_ENABLED=1
, GOOS
, GOARCH
must match with runtime.go version -m
command for the Gateway (go version -m tyk
) and plugin (go version -m plugin.so
). Inspecting and comparing the output of build
tokens usually yields the difference that caused the compatibility issue.
Gateway
has a dependency without a go.mod
file, but the plugin needs to use it.Gateway
and the plugin share a dependency. In this case, the plugin must use the exact same version as the Gateway
.go.mod
Gateway
, which uses dependency A.go.mod
file, so a pseudo version is generated during the build.go.mod
file.
Case 2: Shared dependency with version matching
Gateway
share a dependency, and this dependency includes a go.mod
file.go.mod
.Gateway
.Gateway
share a dependency, but the plugin needs a different version./v4
), it’s treated as a separate package, allowing both versions to coexist.Gateway
dependencies use basic v1
semantic versioning, which doesn’t always enforce strict versioned import paths.
go version -m <file>
:
"main"
and this package cannot be importedfunc main() {}
.func init()
and it gets called only once (when Tyk loads this plugin for the first time for an API).
It is possible to create structures or open connections to 3d party services/storage and then share them within every call and export the function in your Golang plugin.
For example, here is an example of a Tyk Golang plugin with a simple hit counter:
MyProcessRequest
(the one we set in the API spec in the "custom_middleware"
section). The map hitCounter
is used to send internal state and count hits to different endpoints. Then our exported Golang plugin function sends an HTTP reply with endpoint hit statistics.
nil
.ctx.GetOASDefinition(r)
returns an OAS
object containing the Tyk OAS API definition.
The Go data structure can be found here.
ctx.GetDefinition(r)
returns an APIDefinition object containing the Tyk Classic API Definition.
The Go data structure can be found here.
ctx.GetSession(r)
returns an SessionState object.
The Go data structure can be found here.
Here is an example custom Go plugin that makes use of the session object.
"github.com/TykTechnologies/tyk/log"
and use the exported public method Get()
:
"GoPluginMiddleware:" + Path + ":" + SymbolName
, e.g., for our example, the event name will be:
.exec_time
suffix:
driver
field to goplugin
in the API definition when registering the plugin.
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.
plugin_name
: output root file name (for example plugin.so
)build_id
: [optional] provides build uniquenessGOOS
: [optional] override of GOOS (add -e GOOS=linux
)GOARCH
: [optional] override of GOARCH (add -e GOARCH=amd64
)build_id
is not provided, the gateway will not allow the plugin to be loaded twice. This is a restriction of the Go plugins standard library implementation. As long as the builds are made with a unique build_id
, the same plugin can be loaded multiple times.
When you provide a unique build_id
argument, that also enables hot-reload compatibility of your .so
plugin build, so that you would not need to restart the gateway, only reload it.
build_id
-trimpath
, to omit local filesystem path details and improve plugin compatibility, the plugin compiler relies on the Go module itself to ensure each plugin build is unique. It modifies the plugin build go.mod
file and imports to ensure a unique build.
plugin_name
argument:
{Gw-version}
: the Tyk Gateway version, for example, v5.3.0
{OS}
: the target operating system, for example linux
{arch}
: the target CPU architecture, for example, arm64
plugin_name
is set to plugin.so
then given these example values the output file will be: plugin_v5.3.0_linux_arm64.so
.
This enables you to have one directory with multiple versions of the same plugin targeting different Gateway versions.
GOOS
and GOARCH
arguments to the plugin compiler, for example:
linux/arm64
architecture. It will produce an output file named plugin_v5.2.1_linux_arm64.so
.
--platform=linux/amd64
is necessary. The plugin compiler is a cross-build environment implemented with linux/amd64
.DEBUG=1
: enables debug output from the plugin compiler process.GO_TIDY=1
: runs go mod tidy to resolve possible dependency issues.GO_GET=1
: invokes go get to retrieve the exact Tyk gateway dependency.custom_middleware
section and make it look similar to the snippet below. Tyk Dashboard users should use RAW API Editor to access this section.
driver
- Set this to goplugin
(no value created for this plugin) which says to Tyk that this custom middleware is a Golang native plugin.post
- This is the hook name. We use middleware with hook type post
because we want this custom middleware to process the request right before it is passed to the upstream target (we will look at other types later).post.name
- is your function name from the Go plugin project.post.path
- is the full or relative (to the Tyk binary) path to the built plugin file (.so
). Make sure Tyk has read access to this file."use_keyless": true
and "target_url": "http://httpbin.org/"
- for testing purposes. We will test what request arrives to our upstream target and httpbin.org
is a perfect fit for that.
The API needs to be reloaded after that change (this happens automatically when you save the updated API in the Dashboard).
Now your API with its Golang plugin is ready to process traffic:
"Foo": "Bar"
which was added by our custom middleware implemented as a native Golang plugin in Tyk.
.so
file with the plugin. You will need to update the API spec section "custom_middleware"
, specifying a new value for the "path"
field of the plugin you need to reload..zip
file name in the "custom_middleware_bundle"
field. Make sure the new .zip
file is uploaded and available via the bundle HTTP endpoint before you update your API spec.
tyk.conf
these two fields:
"enable_bundle_downloader": true
- enables the plugin bundles downloader"bundle_base_url": "http://mybundles:8000/abc"
- specifies the base URL with the HTTP server where you place your bundles with Golang plugins (this endpoint must be reachable by the gateway)"custom_middleware_bundle"
- here you place your filename with the bundle (.zip
archive) to be fetched from the HTTP endpoint you specified in your tyk.conf
parameter "bundle_base_url"
To load a plugin, your API spec should set this field like so:
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 loadermanifest.json
:
"custom_middleware"
with exactly the same structure we used to specify "custom_middleware"
in API spec without bundle"path"
in section "post"
now contains just a file name without any path. This field specifies .so
filename placed in a ZIP archive with the bundle (remember how we specified "custom_middleware_bundle": "FooBarBundle.zip"
).get_time=1
in the request query string:
get_time
is not set:
get_time=1
query string parameter:
"auth_check"
middleware. Ensure you set the two fields in Post Authentication Hook.
Let’s have a look at the code example. Imagine we need to implement a very trivial authentication method when only one key is supported (in the real world you would want to store your keys in some storage or have some more complex logic).
"github.com/TykTechnologies/tyk/ctx"
is used to set a session in the request context - this is something "auth_check"
-type custom middleware is responsible for."github.com/TykTechnologies/tyk/user"
is used to operate with Tyk’s key session structure."abc"
should work).
Authentication will fail with the wrong API key:
{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.