Edited at

KONGことはじめ

More than 1 year has passed since last update.


KONGとは

公式読めってのはおいといて

噛み砕いて説明すると


  • マイクロサービスを構築する時のAPI Gatewayとなるもの

  • リバプロの役割をしてリクエストを各APIに振り分けるよ

  • pluginで認証や流量制限、ログ取りもできるよ

  • kong本体もクラスタ化できるし、APIもアグリゲーションできるよ

  • API GatewayみたいのがないとたくさんAPIサービス作るとわけわかんなくなっちゃうよ

  • 実際はnginxの拡張モジュールのようなものでAPIを経由してnginxの設定に反映してくれる感じ

つまり色々な言語で実装されたAPIサービスの総合窓口。

KONG配下にAPIサービス群を置いてあとはfrontendでもcurlでも好きに叩けば?

くらいの構成にできそう

KONG以外だとcloud系でAWS API GatewayとかAzureのAPI ManagementとかGoogle Cloud Endpointsがあるみたい

あとは自力でApacheとかNginxとかでリバプロするとか

(これだと認証を別途自作or用意する必要があるけど)

API発行あたりの従量課金の仕組みとかを考えててAPI Gatewayの必要性にたどり着いたので試してみる


インストール

公式のinstallガイドにいろいろあるのだけれど今回はEC2のAmazon Linuxに入れる

KONG用の管理DBはCassandraかPostgresが選択できる

馴染みのPostgresでやってみる


EC2用意

Amazon Linuxをとにかく用意した

elastic IPでIPを固定

セキュリティグループで以下のportをインバウンド許可した

* 5432 - postgres

* 80 - ユーザ用api(http)*1

* 443 - ユーザ用api(https)*1

* 8001 - admin用api(http) *2

* 8443 - ユーザ用api(https) *2

*1 kongのデフォルトユーザAPIは8000と8443だが後述するkongのconf設定で80と443に変更するので。

*2 通常はadmin用APIを公開しちゃいけない。今回試す用に空けた

sshで接続して以下を操作


root password設定

sudo su -

passwd


yum 設定

# yum -y install yum-cron

# vi /etc/yum/yum-cron.conf

apply_updates = yes ← ダウンロード&アップデートを自動で行うようにする

# service yum-cron start
# chkconfig --add yum-cron
# chkconfig yum-cron on
# yum install -y git


root宛メールを転送設定

# sed -i '/^root:/d' /etc/aliases

# echo "root: xxxx@xxx.xx.xx" >> /etc/aliases
# newaliases


Postgres用意

過去の遺産を使ってソースからインストール

もちろんそんなことしないでもpostgresさえ動いていればKONGはOK

(もっと言えばRDSでもいい)


依存ライブラリのインストール

# yum install -y readline-devel zlib-devel gcc uuid uuid-devel


インストール先フォルダの作成

# mkdir -p /usr/local/postgresql_kong/data


OSユーザにpostgresを追加

# useradd postgres


インストール先フォルダのownerをpostgresに

# chown postgres:postgres -R /usr/local/postgresql_kong


パッケージダウンロードと配置

# wget "https://ftp.postgresql.org/pub/source/v9.6.2/postgresql-9.6.2.tar.gz"

# mkdir /usr/local/src/postgresql
# mv postgresql-9.6.2.tar.gz /usr/local/src/postgresql
# cd /usr/local/src/postgresql/
# tar xvfz postgresql-9.6.2.tar.gz

# cp -pr postgresql-9.6.2 postgresql_kong


postgresqlのmake

過去の遺産でuuidやらcontribやらも入れちゃってる

KONGでは使わないと思うけど面倒なのでそのまま

→KONGをpostgresで動かしたらuuidを使っていたので

postgresをソースからインストールする場合はこの手順通り

uuidを入れてやる必要がある

# cd /usr/local/src/postgresql/postgresql_kong

# ./configure -prefix=/usr/local/postgresql_kong -with-pgport=5432 --with-uuid=ossp
# make && make install
# cd /usr/local/src/postgresql/postgresql_kong/contrib/pgcrypto
# make && make install
# cd /usr/local/src/postgresql/postgresql_kong/contrib/uuid-ossp
# make && make install


switch to postgres

# su - postgres


initdb

$ cd /usr/local/postgresql_kong/bin/

$ ./initdb -D /usr/local/postgresql_kong/data/


exit postgres

$ exit


postgresql.conf

# cp -p /usr/local/postgresql_kong/data/postgresql.conf /usr/local/postgresql_kong/data/postgresql.conf.org

# vi /usr/local/postgresql_kong/data/postgresql.conf

listen_addresses = '*'

port=5432とか、コメントアウトするだけ
log_line_prefix = '<%t %u %d>'


pg_hba.conf

# cp -p /usr/local/postgresql_kong/data/pg_hba.conf /usr/local/postgresql_kong/data/pg_hba.conf.org

# vi /usr/local/postgresql_kong/data/pg_hba.conf

host kong kong 0.0.0.0/0 md5


起動スクリプト作成

# cd /usr/local/src/postgresql

# cp -p postgresql_kong/contrib/start-scripts/linux postgresql_kong.sh

# vi postgresql_kong.sh

prefix=/usr/local/pgsql


prefix=/usr/local/postgresql_kong

PGDATA="/usr/local/pgsql/data"

PGDATA="${prefix}/data"


起動スクリプトに実行権をつける

# chmod +x postgresql_kong.sh


起動スクリプトをサービスに登録

# ln -s /usr/local/src/postgresql/postgresql_kong.sh /etc/init.d/postgresql_kong

# service postgresql_kong start
# chkconfig --add /etc/init.d/postgresql_kong
# chkconfig postgresql_kong on


KONG用のPostgresデータベースの作成

# ln -s /usr/local/postgresql_kong/lib/libpq.so.5 /usr/lib64/libpq.so.5

# /usr/local/postgresql_kong/bin/psql -U postgres

CREATE ROLE kong WITH LOGIN SUPERUSER PASSWORD 'kong';

CREATE DATABASE "kong" WITH OWNER = kong ENCODING = 'UTF8';


KONGのインストール

wget https://github.com/Mashape/kong/releases/download/0.10.0/kong-0.10.0.aws.rpm

yumから簡単に。

yum install epel-release

yum install kong-0.10.0.aws.rpm --nogpgcheck


KONGの起動

kong start

Kong startedと出ればOK

postmanから動作していることを確認

Kobito.eYO3pk.png


KONGの設定


Configファイルの編集

kongのconfigは/etc/kong/kong.confで変更できる

nginxあたりの設定もここで。


configファイルの作成

インストール時に用意される/etc/kong/kong.conf.defaultをコピーして使う

cp -p /etc/kong/kong.conf.default /etc/kong/kong.conf


ユーザ利用ポートの変更

デフォルトだと8000/8443になっているのを80/443に変更する

#proxy_listen = 0.0.0.0:8000


proxy_listen = 0.0.0.0:80

#proxy_listen_ssl = 0.0.0.0:8443


proxy_listen_ssl = 0.0.0.0:443


SSL関連

certを設定するのは以下の箇所(今回は飛ばす)

#ssl_cert =

#ssl_cert_key =
#admin_ssl_cert =
#admin_ssl_cert_key =


postgres関連

以下の箇所で設定できる(今回変更なし)

#database = postgres

#pg_host = 127.0.0.1
#pg_port = 5432
#pg_user = kong
#pg_password = kong
#pg_database = kong
#pg_ssl = off
#pg_ssl_verify = off

KONGに反映

kong reload


APIと認証


簡単なAPIの追加

とりあえずyahooにproxyする例

apiはport8001の/apiに対してpostすることで追加できる

渡せるパラメータは公式docを参照

postmanだとこんな感じ

Kobito.Qtvp1L.png

これでブラウザから http://[kong host]/demo/yahoo にアクセスするとyahooにproxysされることが確認できる


api key認証が必要なAPIの追加

kongにはいくつかの認証pluginがある

これを登録するAPI名毎に割り当てることができる


APIを作成

まずmock apiをmockbinを利用して作成

次にkongにAPIを作成

uriは /key-auth/mockbin にした

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=mockbin&upstream_url=http://mockbin.org/bin/e3edb608-40fd-465f-bc2a-854559e55dde&uris=/key-auth/mockbin' "http://[kong host name]:8001/apis"

続いてAPIを実行

curl -X GET -d '' "http://[kong host name]/key-auth/mockbin"

これでJSONが返ってきていることを確認する


作成したAPIにkey-authを付与

認証を付与したいAPIに向けて以下のようにPOSTする

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=key-auth' "http://[kong host name]:8001/apis/mockbin/plugins/"

POST先のURLは /apis/[対象のAPI name or id]/plugins/

パラメタに name=key-authを入れることで

key-authプラグインが該当API(今回はmockbin)に対して適用される

この状態でもう一度APIに対してGETすると401になる


ユーザの作成

key-authを通過させる為のユーザを作成

KONGではconsumersと呼んでいる

ユーザ作成は以下のように実行する

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=testuser&custom_id=A01001' "http://[kong host name]:8001/consumers/"


custom_idは任意設定。後で自力でRDBなどとの紐付けに使うkeyを入れる用途。



ユーザにAPI KEYを発行

key-authに必要なAPI KEYをユーザに対して発行する

URIは/consumers/[対象ユーザのname or id]/key-auth/

BODYにkey=[任意のAPI KEY名]

を設定してPOSTする

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'key=SAMPLE-KEY' "http://[kong host name]:8001/consumers/testuser/key-auth/"


API KEYを使ってAPIを発行する

http headerに apikey という名前で発行したKEY値を設定しAPIを発行することで実行できる

curl -X GET -H "apikey: SAMPLE-KEY" "http://[kong host name]/key-auth/mockbin"


key値が間違っている場合403

key値が設定されていない場合401



oauth2.0認証が必要なAPIの追加

oauth2.0の色々なflowでいってみる


SSL適用

KONGのOAuth2.0を試すにはhttps通信が必要

certbotを使ってcertを取得する

(事前にpublic DNS解決が必要)

cert取得までは本題から外れるのでメモだけ残す

# kong stop

# yum install httpd
# service httpd start

# yum install python27-devel git
# easy_install pip
# pip install --upgrade pip
# pip install --upgrade virtualenv
# git clone https://github.com/certbot/certbot
# cd certbot
# ./certbot-auto certonly --webroot -w /var/www/html -d xxxx.example.co.jp --agree-tos -m xxxxx@example.co.jp --debug

# service httpd stop
# kong start

/etc/letsencrypt/live/[host name]の配下にcertが作成されるので

これをkong.confに設定する

vi /etc/kong/kong.conf

ssl_cert = /etc/letsencrypt/live/[host name]/cert.pem

ssl_cert_key = /etc/letsencrypt/live/[host name]/privkey.pem

設定をkongに反映

kong reload

これでhttpsでアクセスできることを確認


apiの作成

mockbinでapi mockを作成

Kobito.HJNDlp.png

APIをKONGに登録

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=mockbin-oauth&upstream_url=http://mockbin.org/bin/2e884bd8-5b08-49c7-93bf-69a513f87739&uris=/test-oauth2/mockbin' "http://[kong host name]:8001/apis"


apiにoauth2.0を適用

code flow, password, client credentials, implicit flowを有効にした

scopeもemail phone addressと登録してみた

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=oauth2&config.scopes=email%2C phone%2C address&config.enable_authorization_code=true&config.mandatory_scope=true&config.token_expiration=7200&config.enable_client_credentials=true&config.enable_implicit_grant=true&config.enable_password_grant=true&config.hide_credentials=false' "http://[kong host name]:8001/apis/mockbin-oauth/plugins/"

適用した時のresponse内にprovision_key が含まれている。

code flowの場合、provision_key が必要になるのでメモっておく


もしくは[kong host]:8001/apis/[api-name]/plugins をGETして確認


作成されるoauth2.0 endpointは以下の2つ

(両方ともPOSTのみ)


  • /oauth2/authorize

  • /oauth2/token


ユーザの作成

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=oauthuser&custom_id=A01002' "http://[kong host name]:8001/consumers"


oauth applicationの作成

作成したconsumersに紐付ける形になる

postmanで確認したかったのでredirect_uriはpostman用のcallbackを登録した

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=oauth-trial-application&client_id=oauth-trial-application&client_secret=secret&redirect_uri=https://www.getpostman.com/oauth2/callback' "http://[kong host name]:8001/consumers/oauthuser/oauth2"


Access Tokenの取得

Auth Url: https://[your kong host]/test-oauth2/mockbin/oauth2/authorize

Access Token Url: https://[your kong host]/test-oauth2/mockbin/oauth2/token

にリクエストを投げる。

URL上、endpointとなる/oauth2/authorize/oauth2/token の前に各APIのPATHが入ることに注意


client credentials

いつもどおりに投げるだけでOK

Kobito.dXlAAc.png

直接投げるならこんな感じ

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&client_secret=secret&grant_type=client_credentials&scope=email' "https://[your kong host]/test-oauth2/mockbin/oauth2/token"

Kobito.EQ9NrD.png


code flow

KONGのpluginにあるcode flowはprovision_keyが必要になったりするので特殊かも?


codeの取得

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&client_secret=secret&response_type=code&scope=email&provision_key=[oauthをapiにセットした時のprovision_key]&authenticated_userid=oauthuser' "https://[your kong host]/test-oauth2/mockbin/oauth2/authorize"

Kobito.RnWDnJ.png

上記のとおり、provision_key が必要になる。

それとauthenticated_userid というkeyがありここに認証済みの個人IDを放り込むとのこと。

イメージとしては事前にログインID/パスワードで認証を行い、次にAPI認可として

認証済みIDを入れてcodeを取得しに行く感じか。

ちなみに成功時のレスポンスはこんな感じ

{

"redirect_uri": "https://www.getpostman.com/oauth2/callback?code=0b621f05c59b474191394040f4444267"
}

APIにOauthを設定した時、redirect_urlに指定したとこにcode付きで戻っていく。


codeをtokenにexchange

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&client_secret=secret&grant_type=authorization_code&code=0b621f05c59b474191394040f4444267' "https://[your kong host]/test-oauth2/mockbin/oauth2/token"

Kobito.wl8lH9.png

成功時のレスポンスはこんな感じ

{

"refresh_token": "95a8bc4570f94ecf90dbe0fe451a3ed3",
"token_type": "bearer",
"access_token": "0e76c1fd21164f289c8f74daf49d5bf5",
"expires_in": 7200
}


implicit flow

code flowができてれば簡単

response_type = tokenの場合

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&response_type=token&scope=email&provision_key=96b48acff71a4fe8a198a1771a7ffc9c&authenticated_userid=oauthuser&grunt_type=implicit' "https://[your kong host]/test-oauth2/mockbin/oauth2/authorize"

Kobito.FtCNIC.png

レスポンス

{

"redirect_uri": "https://www.getpostman.com/oauth2/callback#access_token=582c503605ee4e89a0c45e2b113eef83&expires_in=7200&token_type=bearer"
}

response_type = codeの場合

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&response_type=code&scope=email&provision_key=96b48acff71a4fe8a198a1771a7ffc9c&authenticated_userid=oauthuser&grunt_type=implicit' "https://[your kong host]/test-oauth2/mockbin/oauth2/authorize"

Kobito.SAfKRQ.png

レスポンス

{

"redirect_uri": "https://www.getpostman.com/oauth2/callback?code=ec6e23952c2c4b5d9f5ab4d03ab9b9a4"
}


resource owner password credentials

これもcode flowと同じ感じで事前にログインIDとパスワードで認証済み前提。

認証が済んだらkongにPOSTしてtokenをもらう感じ。

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&client_secret=secret&scope=email&provision_key=96b48acff71a4fe8a198a1771a7ffc9c&authenticated_userid=anystring&username=anystring&password=anystring&grant_type=password' "https://[your kong host]/test-oauth2/mockbin/oauth2/token"

Kobito.hTU1SZ.png

当たり前といえばそうだけどkong側に渡している

authenticated_userid

username

password

は検証されない。

usernamepasswordに関しては省略してもOK


refresh_token

Kobito.nZsutu.png

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=oauth-trial-application&client_secret=secret&grant_type=refresh_token&refresh_token=08ff72351d5740d59259995ca258affc' "https://[your kong host]/test-oauth2/mockbin/oauth2/token"

成功時のレスポンスはこんな感じ

{

"refresh_token": "2a122910eddb43c0bb9d7d78f0fda5b6",
"token_type": "bearer",
"access_token": "8770f201c00a495a99e66a2f9fb2023a",
"expires_in": 7200
}


JWT認証が必要なAPIの作成


apiの作成

mockbinでapi mockを作成

Kobito.8EacdN.png

APIをKONGに登録

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=mockbin-jwt&upstream_url=http://mockbin.org/bin/f8090f01-3a0f-418c-b516-18a3df0498ba&uris=/test-jwt/mockbin' "http://[kong host name]:8001/apis"


apiにjwt pluginを適用

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=jwt&config.uri_param_names=jwt&config.claims_to_verify=exp&config.key_claim_name=iss&config.secret_is_base64=false' "http://[kong host name]:8001/apis/mockbin-jwt/plugins/"

Kobito.lx4EDr.png

パラメータについて補足

parameter
説明
設定例

config.uri_param_names
jwtをqueryで渡す時のkey名
jwt

config.claims_to_verify

expnbfを指定できる。expなら有効期限超過、nbfなら有効期限前かのチェックをする(多分)
exp,nbf

config.key_claim_name
後で発行するcredentialsのkeyに設定した値を格納するkey名。通常issにしておく
iss

config.secret_is_base64
credentialsのsecretをbase64するかどうか。secretがbynaryの場合。通常false
false


consumersの作成

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=jwtuser&custom_id=A01003' "http://[kong host name]:8001/consumers"


JWT credentialsの作成

parameter
説明
設定例

key
JWT発行時にissに設定する名前と同じものを設定する
oauth-server

algorithm
暗号化アルゴリズム RS256, HS256, ES256が選べる
RS256

rsa_public_key
公開鍵 RS256なら -----BEGIN PUBLIC KEY-----から 省略時自動生成
下記参照

secret
HS256, ES256の時のseacret文字列、省略時自動生成
下記参照


postmanからだとうまく叩けなかった
公開鍵と秘密鍵を入れるのはコツがいる模様
[参考](https://github.com/Mashape/kong/issues/1458)

curlで叩くイメージは以下のような感じ

curl -X POST "http://[kong host name]:8001/consumers/jwtuser/jwt" --data-urlencode "key=oauth-server" --data-urlencode "algorithm=RS256" --data-urlencode "rsa_public_key=

-----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvc6AuzxgawTudoGXChgl

.....

0MYRedkNsYab1kpSYYyDxlhP6H/l0saI817z9U8PPasjBSahllPQxnJJceojh3SC

BQIDAQAB

-----END PUBLIC KEY-----

"


または直接鍵ファイルを送る指定も可能(multipartになる)

curl -X POST http://[kong host name]:8001/consumers/{consumer}/jwt \

-F "rsa_public_key=@/path/to/public_key.pem" \

```


APIの実行

残念ながらKONGにはJWT発行機能がない模様。

別の認証サーバ等で発行したJWTを使って試す

この時、発行したJWT内は以下の条件を満たす必要がある


  • 秘密鍵・公開鍵のキーペアにて暗号化されていること

  • kongにJWT pluginを追加した時、config.key_claim_name に指定したkey名のclaimがあること(例ではiss)

  • 上記claimの値がkongにJWT credentialsを登録した時にkeyに指定した値がセットされていること

これはKONG側でJWTの検証時に公開鍵の検証+αの要素として検証される模様。

通常はJWT発行側でセットされるissの値を予め入手(決定)しておき

KONG側でJWT pluginの追加、JWT credentialsの追加の時にセットしていく感じ

APIを実行する場合はBearerにJWTを設定して実行すればOK

Kobito.pnM47O.png

curl -X GET -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzcHJpbmctb2F1dGgtc2VydmVyLXN1YiIsImF1ZCI6WyJzcHJpbmctYm9vdC1hcHBsaWNhdGlvbiJdLCJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiXSwiaXNzIjoic3ByaW5nLW9hdXRoLXNlcnZlci1pc3MiLCJleHAiOjE0OTMyNjI1ODYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwianRpIjoiNjJhZTU1MmUtYWNlNy00ZWI4LWExY2YtNWYxOTIxZTUyMDY1IiwiZW1haWwiOiJhZG1pbkBleGFtcGxlLmNvbSIsImNsaWVudF9pZCI6InB1YmxpYyJ9.XLOETjeE9Ud9pnAx6ChYryd8yeiqCEd8KPJLlS_9-U62YkvrTFVfqQdI8-JhbWWEVvW76B1TWLMUfLZ--X4NACkLOeKokXtBKpEsJ9Y-nbiyiRum-ysb8w819csTW9kV6mT70ZEwT5b_1Mo9jdRpxEPPZM1hMfSpwN50W9exZFZ4vn_vTba6JFQBQt4p_OvVqfdmsLrPIAlSYtB13100TY0CVHdLWpDxwjbhJPPcF6TVhgdJlpqjyEY5NXUzRv3A6RUfAD5NhZxWrgnZTVN_Jlnu1GVkuN3oCqj_1tuGYdMF6YPb4ToT344Xu1Qq4cHgMPYF2ns3xKD4U3w3lkNppw" "http://[kong host name]/test-jwt/mockbin"


補足

KONG上でJWT認証後、APIサーバ側にproxyする際以下の情報がHeaderに埋め込まれる


  • X-Consumer-ID

  • X-Consumer-Custom-ID(設定されている場合)

  • X-Consumer-Username(設定されている場合)

  • X-Anonymous-Consumer(認証されていない場合、trueが入る)


セキュリティ系のplugin


ACL

だいたい概要は以下のような感じ


  • KONG登録済みのAPI毎に設定

  • consumersにgroupを設定し、APIに対してgroupの利用可・不可を設定する

  • ACLとは別にconsumersを特定するための認証pluginの設定も必要(今回key-authで実施したがOAuth, JWTでもいけるはず)

  • whitelistに入っていない or blacklistに入っている consumerの場合、403 forbiddenで返る

以下試した結果


ACLを試す用のAPIを作成

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=test-acl&upstream_url=http://mockbin.org/bin/4ed664ea-cf51-4f0e-89ad-a8f86c3f8841&uris=/test-acl/mockbin' "http://[kong host name]:8001/apis"


consumersの追加

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=acltest&custom_id=A01005' "http://[kong host name]:8001/consumers"

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=acltest2&custom_id=A01006' "http://[kong host name]:8001/consumers"


apiにkey-authを適用

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=key-auth' "http://[kong host name]:8001/apis/test-acl/plugins/"


consumersにkey-authのkeyを発行

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'key=acltest-key' "http://[kong host name]:8001/consumers/acltest/key-auth/"

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'key=acltest2-key' "http://[kong host name]:8001/consumers/acltest2/key-auth/"


consumersにgroupを追加

/consumers/[consumer name]/acls に対してPOSTする

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'group=group1' "http://[kong host name]:8001/consumers/acltest/acls"

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'group=group2' "http://[kong host name]:8001/consumers/acltest2/acls"


APIにACLを適用する

group1をwhitelistに入れる。

なおwhitelistとblacklistはどちらかしか指定できない。

また複数のグループを指定する場合はvalueをカンマ区切りで入れる

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=acl&config.whitelist=group1' "http://[kong host name]:8001/apis/test-acl/plugins"


APIをkey-auth利用して実行する

whitelistに入っているgroupに属するconsumerの場合→OK

curl -X GET -H "apikey: acltest-key" "http://[kong host name]/test-acl/mockbin"

whitelistに入っていないgroupに属するconsumerの場合→403 forbidden

curl -X GET -H "apikey: acltest2-key" "http://[kong host name]/test-acl/mockbin"


CORS

CORS用ヘッダーもKONGでつけることができる

APIに対して設定する感じ(未検証だがほぼこのままのはず)

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=cors&config.origins=mockbin.com&config.methods=GET%2C POST&config.headers=Accept%2C Accept-Version%2C Content-Length%2C Content-MD5%2C Content-Type%2C Date%2C X-Auth-Token&config.exposed_headers=X-Auth-Token&config.credentials=true&config.max_age=3600' "http://[kong host name]:8001/apis/test-acl/plugins"


2017/3/28 0.10.1 updateによりconfig.originパラメータがconfig.originsになり複数(カンマ区切り)が可能になっている



CORS pluginの制限事項

apiをuri指定している時のみCORS plugingが使える

apiをhosts指定している時は使えない


IP制限 plugin

APIに対して設定する

未実施だがきっとこのままなのでとりあえず公式をコピーしておく

curl -X POST http://[kong host name]:8001/apis/{api}/plugins \

--data "name=ip-restriction" \
--data "config.whitelist=54.13.21.1, 143.1.0.0/24"

なお、同時にconsumer_idをパラメタに渡すことで特定のconsumerに限ることも可能らしい。

(当然認証pluginの併用が必要)


bot制限plugin

ruleに則ってbotからのリクエストをはじくplugin

デフォルトで有名なbotはルールに登録されている デフォルトルール

ルールの追加はconfig.whitelistconfig.blacklistにUser-Agentヘッダーの文字(正規表現可能)を指定することでカスタマイズできる

これも公式をコピーだけ

curl -X POST http://kong:8001/apis/{api}/plugins \

--data "name=bot-detection"


Traffic系のplugin


Rate Limiting plugin


  • 流量制限のplugin

  • API毎に設定

  • 年・月・日・時・分・秒毎に最大アクセス回数を指定できる

  • consumer_idを一緒に設定することでconsumer毎に設定できる


    • これにより1日100アクセス等の制御が簡単で課金対象¥に使える(かも)



  • 設定値を超えたアクセスがあった場合は429で返却する

  • 残り何回かはResponseのHttp Headerに返却されるので確認できる

なお、カウンターのためのポリシーが3つ用意されている


  • cluster : datastoreを使ってKONGの他ノードと共有(おすすめ)

  • local : localメモリーでカウント、クラスタリングしていると別々カウント

  • redis : 別途redisを立ち上げる必要がある。他で多重化したredisがあれば使うかも?

curl -X POST http://kong:8001/apis/{api}/plugins \

--data "name=rate-limiting" \
--data "config.second=5" \
--data "config.hour=10000"

設定できるconfig値の説明

key
説明

name

rate-limiting固定

consumer_id
省略可、指定する場合認証pluginの併用が必要

config.second
最大アクセス数を指定

config.minute
最大アクセス数を指定

config.hour
最大アクセス数を指定

config.day
最大アクセス数を指定

config.month
最大アクセス数を指定

config.year
最大アクセス数を指定

config.limit_by
consumer, credential, ipが選択できる。指定した対象がlimit overした時にエラーとする。consumer, credentialがわからない場合は自動的にipになる

config.policy
local or cluster or redis ※上記説明参照

config.fault_tolerant
trueにするとdatastoreが動いていない場合、rate limitは通過するようになる。falseにするとすぐさま500で返す

config.redis_host
config.policyがredisの場合に指定

config.redis_port
config.policyがredisの場合に指定

config.redis_password
config.policyがredisの場合に指定

config.redis_timeout
config.policyがredisの場合に指定

config.redis_database
config.policyがredisの場合に指定

Responseの Http Headerに返却される残り回数は以下のようになる

X-RateLimit-Limit-Second: 5

X-RateLimit-Remaining-Second: 4
X-RateLimit-Limit-Minute: 10
X-RateLimit-Remaining-Minute: 9


Request Size Limiting

リクエストのサイズ制限をするplugin

DOS攻撃を防ぐ意味でも全APIへの適用が推奨されている

curl -X POST http://[kong host name]:8001/apis/{api}/plugins \

--data "name=request-size-limiting" \
--data "config.allowed_payload_size=128"


config.allowed_payload_size```に指定する単位はMb。

また、上記例にはないが```consumer_id```も追加することができる

# Logging系のplugin

Logの出力先毎にpluginが分かれている

| plugin | 説明 |
|--------|--------|
| TCP | TCPでログを送信する ログはJSON形式 |
| UDP | UDPでログを送信する ログはJSON形式 |
| HTTP | HTTP(POSTなど)でログを送信する ログはJSON形式 |
| FILE | ファイルにログを出力する ログはJSON形式 |
| SYSLOG | syslogにログを出力する ログはJSON形式 |
| StatsD | Collectdにログを出力する Statsd plugin形式 |
| Loggly | UDPでLogglyにログを出力する |

いずれも他logサービスとの連携が前提
とりあえずFILE出力だけ試す


curl -X POST http://[kong host name]:8001/apis/{api}/plugins \

--data "name=file-log" \

--data "config.path=/tmp/kong-file.log"

```

APIを実行するとその都度指定したファイルへJSON形式のログが出力された


Transform系のPlugin

リクエスト・レスポンスの内容を加工するplugin


Request Transformer

リクエストのheader, body, queryについてパラメータをadd, removeできる


Response Transformer

レスポンスのheader, body, queryについてパラメータをadd, removeできる

若干のjsonの加工(追加、削除くらい)もできるみたい

→parseして加工になるのでレスポンス・リソース影響あり


Correlation ID

リクエスト(とレスポンス)にUUID等のユニークなIDをHeaderに追加する

ログ等でリクエスト単位のユニークIDが確認できるようになる(使いみち?)


KONGのクラスター化

KONGを複数nodeにして(データストアは同じ)運用する場合

以下のクラスタ設定をしなくてはいけない


公式に記載があるとおり、複数node化した際にクラスタ化をしないとシンクロされないデータが発生し問題につながるとのこと


クラスタ化の設定自体は簡単だが、各nodeがネットワーク上同一セグメント内に属していない場合はIPv4にて特定できる必要がある(NAT等を通さない、NATしている場合post7946のフォワーディングが必要(未検証))

今回は別のEC2にKONGをインストールし、IPも別のものを立てて試す

またあくまでKONG間の整合を取るだけの機能で、node間のロードバランシング等は別途用意しておく必要がある


クラスタ化設定


port7946の開放

全ノードでport7946での疎通ができるようにする

今回はEC2のセキュリティグループにてport7946を許可(全ノード)


別EC2にKONGをインストール

上述した手順どおり、datastore(postgres)は不要、kong startする前に

以下のとおりkong.confを設定する


  • データストア(postgres)は全ノード同じものを指す


  • cluster_advertiseに[自サーバのip(基本private ipでないもの)]:7946 を設定する→全ノードのkong.confに設定


  • kong startで反映(全ノード)、既に稼働しているkongはkong restart



    • kong reloadでは反映しない




クラスタ化されているか確認

kong cluster reachability で以下のように全ノードを確認できる

Total members: 2, live members: 2

Starting reachability test...
Successfully contacted all live nodes

またkong cluster members で一覧と状態を確認できる

ip-172-31-10-200_0.0.0.0:7946_b5bfea301c864406bc0b4878dfe2e104  xxx.xxx.xxx.xxx:7946    alive

ip-172-31-5-21_0.0.0.0:7946_b3ad232a0795474786bc9c7522ab0650 xxx.xxx.xxx.xxx:7946 alive


つまりどころ


KONGのログはどこ

/etc/kong/kong.conf の中の prefix で指定しているパスが

KONGのディレクトリとなる(デフォルトだと /usr/local/kong

KONGディレクトリ配下のlogs配下にログが出力される


DNSエラー

通常のDNS解決できないリモートホストへ接続する時、KONGインストールOSの

/etc/hosts などを利用する場合→hosts編集後にKONG再起動が必要

/etc/hosts を修正したあとKONGの再起動をしないとKONGからはDNSエラーのまま


Cookieを使うサイトの場合

apiを追加する際に preserve_host をtrueに設定しておく

でないとupstream側のURLで発行したcookieが

クライアントに返却されてしまう

  • KONGの設定
  • APIと認証
  • セキュリティ系のplugin
  • Traffic系のplugin
  • Transform系のPlugin
  • KONGのクラスター化
  • つまりどころ