Kong GatewayではmTLSで通信を行う際、以下のように行うことができる。
- クライアントとKong GW間:mTLS Pluginで実施
- Kong GWとサービス間:Serviceの機能で実施
これについては過去に@xibukaさんがKong Gateway入門 - mTLS対応で解説しているが、今回Kong GWとサービス間のmTLS通信についてもう少し踏み込んで検証してみる。
今回は以下の手順で検証を行う。
- 証明書の準備
- サービス側(Nginx)の設定
- Kong設定
- テスト
検証環境
今回はローカル環境(MacOS)にコンテナを使ってKong GatewayおよびmTLSを要求するサービス(Nginx)を用意する。
Kong Gatewayは3.10で検証した。
また利用する証明書は自己署名証明書を利用する。
1. 証明書の準備
まずサービス側、Kong側それぞれを認可するためのCA証明書を作成する。
mkdir certs
cd certs
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=My-Private-CA"
次にサービス側の証明書を作成し、CA証明書を使って署名する。
openssl genrsa -out service.key 2048
openssl req -new -key service.key -out service.csr -subj "/CN=backend-service"
openssl x509 -req -in service.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out service.crt -days 365 -sha256
同様にKong側の証明書を作成し、CA証明書を使って署名する。
openssl genrsa -out kong-client.key 2048
openssl req -new -key kong-client.key -out kong-client.csr -subj "/CN=kong-client"
openssl x509 -req -in kong-client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kong-client.crt -days 365 -sha256
2. サービス側の設定
次にNginxの設定を行う。以下でディレクトリを作成して設定ファイルを格納する。
cd -
mkdir nginx
cat << 'EOF' > nginx/default.conf
log_format mtls_debug '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'client_cert_subject="$ssl_client_s_dn" '
'client_cert_verify="$ssl_client_verify"';
server {
listen 443 ssl;
server_name backend-service;
ssl_certificate /etc/nginx/certs/service.crt;
ssl_certificate_key /etc/nginx/certs/service.key;
ssl_verify_client on;
ssl_client_certificate /etc/nginx/certs/ca.crt;
access_log /var/log/nginx/access.log mtls_debug;
location / {
add_header Content-Type text/plain;
return 200 "mTLS Connection Successful!\n";
}
}
EOF
ポイントとしては、ssl_verify_client on;でクライアント証明書の検証を有効化し、ssl_client_certificateで信頼するCA証明書を指定している点である。
これによって、Kongからの接続時にクライアント証明書が提示され、その証明書が指定したCAで署名されているかが検証できる。
またログから証明書の検証の結果が分かるようにログフォーマットを変更している。
3. Kong設定
次にKong Gatewayの設定を行う。
今回はKong GatewayとNginxをdocker composeで立ち上げて、その後にdeckを使ってService/RouteとmTLS Pluginの設定を行う。
まずKong GatewayとNginxサービスを起動するためのdocker-compose.ymlを作成する。
cat << 'EOF' > docker-compose.yml
x-default: &default
networks:
- kong-net
restart: on-failure
platform: linux/arm64
x-kong-env: &kong-env
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_PASSWORD: kongpass
KONG_PG_USER: kong
networks:
kong-net:
driver: bridge
services:
kong-database:
<<: *default
image: postgres:16
container_name: kong-database
ports:
- "5432:5432"
restart: unless-stopped
environment:
POSTGRES_USER: kong
POSTGRES_DB: kong
POSTGRES_PASSWORD: kongpass
healthcheck:
test: ["CMD", "pg_isready", "-U", "kong"]
interval: 5s
timeout: 5s
retries: 5
kong-bootstrap:
<<: *default
image: kong/kong-gateway:3.10
container_name: kong-bootstrap
depends_on:
kong-database:
condition: service_healthy
environment:
<<: *kong-env
command: kong migrations bootstrap
kong:
<<: *default
image: kong/kong-gateway:3.10
container_name: kong-gateway
user: "root"
depends_on:
kong-bootstrap:
condition: service_completed_successfully
environment:
<<: *kong-env
KONG_PROXY_LISTEN: 0.0.0.0:8000
KONG_ADMIN_LISTEN: 0.0.0.0:8001
KONG_LICENSE_DATA: ${KONG_LICENSE_DATA}
KONG_LOG_LEVEL: debug
volumes:
- ${PWD}/certs:/etc/kong/certs:ro
ports:
- "8000:8000"
- "8001:8001"
- "8002:8002"
backend-service:
<<: *default
image: nginx:latest
container_name: backend-service
volumes:
- ${PWD}/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ${PWD}/certs:/etc/nginx/certs:ro
ports:
- "8443:443"
EOF
ここでは先程作った証明書と設定ファイルをNginxコンテナにマウントして読み込ませている。
platform: linux/arm64の部分はMac以外の人は要変更となる。
ライセンスファイルの内容をKONG_LICENSE_DATAに設定して起動する。
export KONG_LICENSE_DATA=$(cat ~/license.json)
docker-compose up -d
起動後、以下のようにlocalhost:8000にアクセスしてKong Gatewayが起動していることを確認する。
$ curl localhost:8000/httpbin
{
"message":"no Route matched with those values",
"request_id":"d0c541735f24db1e62b83a339281d203"
}
次にService/Routeを作成する。
deckのYAMLを格納するディレクトリを作成する。
mkdir kong
次に証明書の内容を環境変数に設定する。これは後ほどdeckのYAMLに埋め込む際に利用する。
CA_CERT=$(cat certs/ca.crt | sed ' s/^/ /')
KONG_CERT=$(cat certs/kong-client.crt | sed ' s/^/ /')
KONG_KEY=$(cat certs/kong-client.key | sed ' s/^/ /')
同様に証明書のUUIDも設定する。通常はGW側に採番してもらうが、事前に設定しておくことでServiceに紐付けやすくなる。
MY_CA_ID="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
MY_CERT_ID="11111111-2222-3333-4444-555555555555"
証明書をKong Gatewayに登録するためのYAMLを作成する。
cat << EOF > kong/mtls-cert.yaml
_format_version: "3.0"
# 1. 信頼するCAの登録(KongがNginxを検証する場合に使用)
ca_certificates:
- id: $MY_CA_ID
cert: |
$CA_CERT
# 2. Kong自身の証明書の登録(mTLSでNginxに提示する用)
certificates:
- id: $MY_CERT_ID
cert: |
$KONG_CERT
key: |
$KONG_KEY
EOF
次にService/Routeも作成する。
Serviceでは先程定義した証明書の各IDを指定してmTLS通信を行うように設定する。
cat << EOF > kong/mtls-service.yaml
_format_version: "3.0"
services:
- name: mtls-service
url: https://backend-service:443
# 提示する証明書のIDを指定
client_certificate: $MY_CERT_ID
# 必要に応じて、Kong側でもバックエンドを検証する場合
ca_certificates:
- $MY_CA_ID
tls_verify: true
routes:
- name: mtls-route
paths:
- /test
EOF
設定を適用する。
deck gateway apply kong/
4. テスト
まず、Nginxに直接アクセスしてmTLSが有効になっていることを確認する。
curl -k https://localhost:8443/
証明書を渡していないのでエラーとなる。
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.29.0</center>
</body>
</html>
Nginxのログも以下のように証明書に関連する部分がNONEとなっている。
172.18.0.1 - - [26/Jan/2026:05:47:34 +0000] "GET / HTTP/1.1" 400 237 client_cert_subject="-" client_cert_verify="NONE"
証明書を渡してアクセスしてみる。
curl -k --cert certs/kong-client.crt --key certs/kong-client.key https://localhost:8443/
結果は以下のようになった。
mTLS Connection Successful!
ログ出力は以下のようになった。
172.18.0.1 - - [26/Jan/2026:05:49:00 +0000] "GET / HTTP/1.1" 200 28 client_cert_subject="CN=kong-client" client_cert_verify="SUCCESS"
次にKong Gatewayからアクセスしてみる。
curl http://localhost:8000/test
証明書は指定しなかったが、以下のように成功した。
mTLS Connection Successful!
これはKong GatewayがNginxに対してmTLS通信を行い、正常に接続できたことを示している。
Nginxのログも以下のように証明書が正しく検証されzていることが分かる。
172.18.0.4 - - [26/Jan/2026:05:49:30 +0000] "GET / HTTP/1.1" 200 28 client_cert_subject="CN=kong-client" client_cert_verify="SUCCESS"
以上より、サービス側からも証明書の検証が正常に行われていることが確認できた。