Kong

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のクラスター化
  • つまりどころ