Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

KONGことはじめ

More than 3 years have 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が
クライアントに返却されてしまう

AkihiroTakamura
仕事でやったことを問題ない範囲で気ままに上げていきます web系多し
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away