はじめに
OSSのKongは世界で最も人気のあるAPI Gatewayで、認証認可や流量制御などプラグインを有効化するだけで用意することができます。
プラグインはこちらのPlugin Hubにリストアップされており、必要なものを選んで導入することができますが、必要な機能がサポートされていない、といったケースもあるかと思います。
その際にはKong提供のプラグインをベースにカスタマイズすることも可能です。
本記事ではリクエスト情報の編集を行うRequest Transformer Plugin
への機能追加を例に、Kong提供プラグインのカスタマイズ方法を記載します。
記事のサンプルコードはこちらになります。
また、OSSのAPI Gateway「Kong」の日本コミュニティ Kong Community, Japan として活動も行っています。興味のある方はslackにご参加いただければ幸いです。
プラグインのコードの入手
request transformer
のようなKong提供のプラグインはkongのイメージに予め含まれているのでコピーしてきます。
今回はDBレスモード(yamlファイルでの設定管理)でのKong利用とします。
まず、適当なkong.ymlを作成します
_format_version: "1.1"
services:
- name: example_service
url: http://httpbin.org/status/200
routes:
- name: example_route
paths:
- /example
次章以降のためにDockerfileを作っておきます
FROM kong:3.0.0-alpine
USER root
COPY kong.yml /kong/declarative/kong.yml
# DBレスモード
ENV KONG_DATABASE off
ENV KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml
ENV KONG_PROXY_ACCESS_LOG /dev/stdout
ENV KONG_ADMIN_ACCESS_LOG /dev/stdout
ENV KONG_PROXY_ERROR_LOG /dev/stderr
ENV KONG_ADMIN_ERROR_LOG /dev/stderr
以下でrequest-transformerのコードを入手します。
docker build -t custom-kong .
docker run -d --name kong-dbless -p 8000:8000 custom-kong
docker cp kong-dbless:/usr/local/share/lua/5.1/kong/plugins/request-transformer request-transformer
以下のフォルダ/ファイルがコピーしたrequest-transformerディレクトリに含まれていればokです
- migrations
- access.lua
- handler.lua
- schema.lua
Kongプラグインについて
KongのプラグインはLua
という言語で書かれており、主な構成ファイルはhandler.lua
とschema.lua
になります。
-
handler.lua
ライフサイクル(request, response, logなど)の各フェーズで実行するプラグインの処理を記載する。
-
schema.lua
ユーザーが動作を変更するための変数を定義する。
request-transformer
ではhandler.lua内で処理を記載したaccess.luaを呼び出す構成になっています。
(migrations配下はDBのマイグレーション用)
request-transformerのカスタマイズ
今回はカスタマイズ例として、リクエストボディに環境変数をマッピングできるように変更します。
UIでは保持できない機密情報などのパラメータをKongでマッピングし、後続のAPIに流すことができます。
必要なライブラリのインストール
環境変数を扱うことのできるlua-resty-envを導入します。
Dockerfile内でluarocks installします。
luarocksがない方はbrew install luarocks
でインストールしてください。
FROM kong:3.0.0-alpine
...
# 以下を追加
RUN luarocks install lua-resty-env
ENV KONG_DATABASE off
...
access.luaの書き換え
request-transformerではtemplate文字列($(...)
)でヘッダーやクエリ文字列、URI文字列を使用することができます。(Template as a Value)
ここでは$(env.~)
で環境変数を使用できるようにします
resty.env
を呼び出し、envという文字列に対してenv.list()で環境変数を返すように変更します
local multipart = require "multipart"
local cjson = require "cjson"
local pl_template = require "pl.template"
local pl_tablex = require "pl.tablex"
--- 以下を追加 ---
local resty_env = require "resty.env"
...
local __meta_environment = {
__index = function(self, key)
local lazy_loaders = {
headers = function(self)
return get_headers() or EMPTY
end,
query_params = function(self)
return get_uri_args() or EMPTY
end,
uri_captures = function(self)
return (ngx.ctx.router_matches or EMPTY).uri_captures or EMPTY
end,
shared = function(self)
return ((kong or EMPTY).ctx or EMPTY).shared or EMPTY
end,
--- 以下を追加 ---
env = function(self)
return resty_env.list() or EMPTY
end
}
...
}
template_environment = setmetatable({
-- here we can optionally add functions to expose to the sandbox, eg:
-- tostring = tostring, -- for example
-- because headers may contain array elements such as duplicated headers
-- type is a useful function in these cases. See issue #25.
type = type,
}, __meta_environment)
local function clear_environment(conf)
rawset(template_environment, "headers", nil)
rawset(template_environment, "query_params", nil)
rawset(template_environment, "uri_captures", nil)
rawset(template_environment, "shared", nil)
--- 以下を追加 ---
rawset(template_environment, "env", nil)
end
...
変更したプラグインの有効化
プラグインの追加
今回編集したプラグインを使えるようにDockerfileに記述を追加します
編集したrequest-transformerディレクトリを/usr/local/share/lua/5.1/kong/plugins/にコピーします。
また、以下の環境変数を設定します
Env Variable | Value | Description |
---|---|---|
KONG_UNTRUSTED_LUA | on | カスタムLuaコードを使えるようにする設定 |
FROM kong:3.0.0-alpine
...
# 以下を追加
ENV KONG_UNTRUSTED_LUA on
COPY request-transformer /usr/local/share/lua/5.1/kong/plugins/request-transformer
変更したプラグインを使用してみる
環境変数の設定
まずはリクエストボディにマッピングする環境変数を定義します。
コンテナに設定した環境変数をlua-resty-envで呼び出すには環境変数名をNginxで指定する必要があります。
以下のようにKONG_NGINX_MAIN_ENV
に環境変数を指定します。
複数指定したい場合はKONG_NGINX_MAIN_ENV=FOO; env BAR;
と指定します。
(詳しくはこちらのissueに記載があります)
version: "3"
services:
kong:
build: .
container_name: kong-dbless
environment:
- KONG_NGINX_MAIN_ENV=ENV; env SECRET
- ENV=dev
- SECRET=secret
command: >
/bin/bash -c "
kong start"
restart: always
ports:
- 8000:8000
routeへのプラグインの追加
kong.ymlにrequest-transformerで環境変数のSECRET
をリクエストボディにマッピングしてみます
_format_version: "1.1"
services:
- name: example_service
url: http://httpbin.org/post
routes:
- name: example_route
methods:
- POST
paths:
- /httpbin/post
plugins:
- name: request-transformer
config:
add:
body:
- secret:$(env.SECRET)
これでコンテナを起動します
docker-compose up -d --build
リクエスト
localhost:8000/httpbin/postにリクエストしてみます
今回upstreamに指定しているhttpbin.org/postはリクエストした内容をレスポンスとして返してくれます
curl -X POST -H "Content-Type: application/json" -d '{"test": "test"}' http://localhost:8000/httpbin/post
>>>
{
"args": {},
"data": "{\"secret\":\"secret\",\"test\":\"test\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Content-Length": "33",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/7.79.1",
"X-Forwarded-Host": "localhost",
"X-Forwarded-Path": "/httpbin/post",
"X-Forwarded-Prefix": "/httpbin/post"
},
"json": {
"secret": "secret",
"test": "test"
},
"url": "http://localhost/post"
}
ちゃんとSECRET
の内容がボディにマッピングされていることが確認できます。
上記を応用することで、UIでは保持できない機密情報などのパラメータをKongでマッピングし、後続のAPIに流すことができます。