Extending the APISIX Plugin
Goal
Understand the Keymate APISIX access plugin — its dual implementation model, configuration parameters, request flow, and extension points — so you can customize its behavior for your deployment.
Audience
Developers integrating Keymate authorization into Apache APISIX routes, or customizing the plugin behavior for specific use cases.
Prerequisites
- A running Apache APISIX instance (version 3.10.0 or later)
- Access to a running Access Gateway instance
- Familiarity with Apache APISIX route and plugin configuration
- For WASM modifications: Go and TinyGo 0.33.0
- For Lua modifications: basic Lua and OpenResty knowledge
Before You Start
The Keymate APISIX plugin ships in two implementations:
| Implementation | Language | Runtime | Best For |
|---|---|---|---|
| Lua | Lua (native APISIX plugin) | OpenResty | Standard deployments, fast iteration, no compilation step |
| WASM | Go (compiled with TinyGo) | WASM VM via proxy-wasm | High-performance isolation, language-neutral execution |
Both implementations perform the same function: intercept incoming HTTP requests during the APISIX access phase, forward them to the Access Gateway for authorization, and allow or deny the request based on the response.
The Lua plugin is registered as keymate-access-gateway in APISIX with priority 2000. The WASM plugin uses the proxy-wasm Go SDK (github.com/tetratelabs/proxy-wasm-go-sdk).
Choose the implementation that fits your operational requirements. Both can run simultaneously on different APISIX routes.
Worked Example
In this guide, you configure the plugin on a route, understand how the request flows through the authorization check, and learn where to extend the plugin behavior.
Steps
1. Understand the request flow
When a client request hits an APISIX route with the Keymate plugin enabled, the following sequence occurs:
The plugin:
- Captures the original request headers, method, URI (path + query string), and body
- Removes protected headers to prevent client-side spoofing (
Keymate-Enforcer,Keymate-Target-URI,Keymate-Target-Method) - Adds Keymate-specific headers identifying the gateway enforcer, target URI, and HTTP method
- Sends a POST request to the Access Gateway with the original request body
- Interprets the Access Gateway response: 2xx means allow, anything else means deny
2. Configure the Lua plugin
The Lua plugin is configured in the APISIX route definition:
plugins:
keymate-access-gateway:
gateway_url: "http://access-gateway.example.com:8080"
endpoint: "/gateway/api/v1/access/plugins/check-permission"
timeout: 5000
Configuration parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
gateway_url | String | Yes | — | Base URL of the Access Gateway instance |
endpoint | String | No | /auth | Authorization endpoint path appended to gateway_url |
timeout | Integer | No | 5000 | Request timeout in milliseconds |
The Lua plugin uses APISIX's resty.http client with connection keepalive (60s timeout, pool size 100).
3. Configure the WASM plugin
The WASM plugin accepts configuration as a JSON string in the conf parameter:
plugins:
keymate-access-gateway-wasm:
conf: |
{
"gateway_url": "http://access-gateway.example.com:8080",
"endpoint": "/gateway/api/v1/access/plugins/check-permission",
"timeout": 5000
}
The WASM plugin accepts the same parameters as the Lua plugin (gateway_url, endpoint, timeout) with the same defaults.
4. Understand the headers
Headers set by the plugin (sent to Access Gateway):
| Header | Value | Description |
|---|---|---|
Keymate-Enforcer | layer="gateway", tech="apisix", version="<plugin-version>" | Identifies the enforcer type, technology, and version |
Keymate-Target-URI | Original request path and query string | The target URI for access rule matching |
Keymate-Target-Method | Original HTTP method | The HTTP method for resource resolution |
Headers filtered (not forwarded to Access Gateway):
The plugin removes these headers from the forwarded request to prevent client-side spoofing:
Keymate-Enforcer— always set by the pluginKeymate-Target-URI— always set by the pluginKeymate-Target-Method— always set by the pluginContent-Length— recalculated for the gateway request
All other original request headers (including Authorization and Keymate-Client-Id) are forwarded as-is.
Headers added to the client response:
| Header | Source | Description |
|---|---|---|
Keymate-Decision | Access Gateway | Authorization decision result and authority source |
Keymate-Decision-Latency | Access Gateway | Total decision processing time in milliseconds |
Keymate-Decision-Authority-Latency | Access Gateway | Authority call latency in milliseconds |
Keymate-Decision-Cache | Access Gateway | Cache hit or miss indicator |
Keymate-Gateway | Access Gateway | Access Gateway version |
Keymate-Gateway-Auth-Duration | Plugin | Total plugin execution time in milliseconds |
Keymate-Gateway-Auth-Status | Plugin | Plugin outcome: GRANTED, DENIED, or GATEWAY_FAILED |
Keymate-Decision-* and Keymate-Gateway headers are forwarded from the Access Gateway response. Keymate-Gateway-Auth-Duration and Keymate-Gateway-Auth-Status are set by the plugin itself to track total plugin execution time and the final authorization outcome.
5. Understand error handling
| Condition | HTTP Status | Response Body | Keymate-Gateway-Auth-Status |
|---|---|---|---|
| Access Gateway unreachable or timeout | 502 | {"error": "Authorization Service Unavailable"} | GATEWAY_FAILED |
| Access Gateway returns 2xx | — | Request forwarded to upstream | GRANTED |
| Access Gateway returns 4xx | Same as gateway | Gateway response body forwarded to client | DENIED |
| Access Gateway returns 5xx | Same as gateway | Gateway response body forwarded to client | GATEWAY_FAILED |
The plugin does not retry failed gateway requests. If the Access Gateway is unavailable, the request is rejected with a 502 status.
The WASM plugin also includes backward compatibility with legacy Access Gateway header names (Elapsed-Time-Ms → Keymate-Decision-Latency, Permission-Gateway-Duration-Ms → Keymate-Decision-Authority-Latency). New headers take priority when both are present.
6. Customize per-route behavior
Configure different plugin parameters per APISIX route. This allows different services behind the same APISIX instance to use different Access Gateway endpoints or timeouts:
routes:
- uri: /api/payments/*
plugins:
keymate-access-gateway:
gateway_url: "http://access-gateway.example.com:8080"
endpoint: "/gateway/api/v1/access/plugins/check-permission"
timeout: 3000
- uri: /api/reports/*
plugins:
keymate-access-gateway:
gateway_url: "http://access-gateway.example.com:8080"
endpoint: "/gateway/api/v1/access/plugins/check-permission"
timeout: 10000
7. Extend the plugin
To add custom behavior, modify the plugin source code:
Adding custom headers: Both implementations construct the gateway request headers in a central location. Add new headers alongside the existing Keymate-* headers to pass additional context to the Access Gateway.
Adding protected headers: Expand the protected_headers table (Lua) or the header filter conditions (WASM) to prevent additional client-supplied headers from reaching the Access Gateway.
Custom error responses: Modify the error handling section to return custom error formats or status codes when the Access Gateway is unavailable.
Custom logging: Both implementations include log points for debugging. The Lua plugin uses core.log.info/core.log.warn/core.log.error. The WASM plugin uses proxywasm.LogInfof/proxywasm.LogWarnf/proxywasm.LogErrorf.
After modifying the Lua plugin, reload the APISIX configuration. After modifying the WASM plugin, recompile with TinyGo (make build) and redeploy the WASM binary.
Validation Scenario
Scenario
You configure the Lua plugin on a route and send a request with a valid access token. The Access Gateway grants access and the request reaches the upstream service.
Expected Result
- The upstream service receives the request
- The client response includes
Keymate-DecisionandKeymate-Gateway-Auth-Durationheaders Keymate-Gateway-Auth-StatusisGRANTED
How to Verify
- API evidence: Send a request with a valid Bearer token and check the response headers for
Keymate-Decision,Keymate-Decision-Cache, andKeymate-Gateway-Auth-Status - Logs: Check APISIX error logs for
[KEYMATE-ACCESS-GATEWAY]plugin execution entries - Audit evidence: Verify the Access Gateway audit log contains the authorization check event
Troubleshooting
- 502 "Authorization Service Unavailable" — The plugin cannot reach the Access Gateway. Verify the
gateway_urlis correct and the Access Gateway is running. Check network connectivity between APISIX and the Access Gateway. - Timeout errors — Increase the
timeoutvalue in the plugin configuration. The default is 5000ms. For latency-sensitive deployments, ensure the Access Gateway is co-located in the same network. - Client-supplied Keymate headers are ignored — This is by design. The plugin filters
Keymate-Enforcer,Keymate-Target-URI, andKeymate-Target-Methodfrom client requests to prevent spoofing. - WASM plugin not loading — Verify the WASM binary is compiled and accessible by APISIX. Rebuild with
make buildand ensure the binary path matches the APISIX plugin configuration. - Missing decision headers in response — The Access Gateway may not be returning decision headers. Verify the Access Gateway version supports the expected response headers. Check for the backward-compatible legacy header names.
Next Steps
Set up a local development environment to test plugin changes — see Plugin Local Development & Test Setup.
For contributing changes back to the plugin repository, see Contributing to Enforcement Plugins.
Related Docs
Access Gateway Overview
Endpoints, headers, and system role of the Access Gateway.
Plugin Local Development & Test Setup
Set up the local container environment for plugin testing.
Extending the Istio Plugin
Istio WASM plugin for authorization enforcement.
Enforcement Pipeline
How the Access Gateway processes permission check requests.