Kong Gateway入門 - mTLS対応

API通信におけるセキュリティは、システムの信頼性を保つ上で非常に重要です。本記事では、Kong API Gatewayを使用して、mTLS（Mutual TLS、相互TLS）を設定する方法を詳しく解説します。特に、「Client -> Kong」と「Kong -> Upstream API」の2つの通信パスでのmTLSの設定方法について取り上げます。

## mTLSの基礎知識

mTLSとは、TLS（Transport Layer Security）を拡張したプロトコルで、通信の両側（クライアントとサーバー）が互いに証明書を使って認証を行います。通常のTLSはサーバー側の認証だけを行いますが、mTLSでは以下のようなプロセスが追加されます：
1. サーバー認証：クライアントは、サーバーが信頼できる証明書を持っていることを確認します。
2. クライアント認証：サーバーは、クライアントが信頼できる証明書を持っていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679136/187fa523-3a2c-72f0-1e0b-d205c9f9a4cd.png)

この双方向の認証により、より安全な通信が実現します。

## Kong API Gatewayで実現するmTLS通信

Kong API Gatewayを使えば、以下の2つの通信経路でmTLSを簡単に実現できます：
 - クライアント → Kong API Gateway
 - Kong API Gateway → Upstream API

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679136/1eb6b790-1f9d-e99a-7d29-fd6811ac97b6.png)


### Client->KongのmTLS設定

クライアントからKong API Gatewayへの通信にmTLSを実装するには、Kongの「mTLS Plugin」を使用します。以下は手順の概要です。
1. 管理者がクライアント認証用のCA証明書をKongに設定
2. クライアントはリクエスト時に自分の証明書をKongに提示
3. Kongがクライアント証明書を検証し、安全な通信を許可

#### CA証明書を登録

Clientの証明書を検証するためのCA証明書をvalutに登録

```bash
curl http://localhost:8001/ca_certificates -F cert=@ca.crt

{
  "cert": "<PEM_CERT>",
  "id": "c383d81a-bffc-4e2a-b0d3-ac56b441a07b",
  "cert_digest": "3e9099b5015e8f486c00bcea9d111ee721faba355a89bcf1df69561e3dc6325c",
  "tags": null,
  "created_at": 1607347576
}
```

#### mTLSプラグインを有効化

先ほど作ったca_certificateを利用して、mTLSプラグインを有効化します。ここではグローバル範囲に指定しています。

```bash 
curl --request POST \
  --url http://localhost:8001/plugins \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data name=mtls-auth \
  --data config.ca_certificates=c383d81a-bffc-4e2a-b0d3-ac56b441a07b
```

このタイミングで、clientの証明書なしでアクセスすると、`HTTP/1.1 401 Unauthorized`のエラーが返ってくるはず。

#### client証明書ありでのアクセス

mTLSは相互TLS認証を意味するため、アクセスする際には`HTTPS`プロトコルを使用する必要があります。もしKong側のTLS証明書が自己署名証明書である場合、証明書の検証をスキップするために`-k`フラグを追加する必要があります。

```bash
curl https://<PROXY_URL>:8443/mtls --key client.key --cert client.crt -k
```
```bash
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 752
Content-Type: application/json
Date: Fri, 20 Dec 2024 15:14:38 GMT
Server: gunicorn/19.9.0
Via: 1.1 kong/3.9.0.0-enterprise-edition
X-Kong-Proxy-Latency: 9
X-Kong-Request-Id: ada6f204577fe50a144706292a01f421
X-Kong-Upstream-Latency: 8

{
    "args": {},
    "data": "",
    "files": {},
    "form": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Connection": "keep-alive",
        "Host": "httpbin.backends:8080",
        "Kong-Request-Id": "e8560956-1bdf-491b-aa05-f962db06adcd",
        "User-Agent": "HTTPie/3.2.4",
        "X-Consumer-Custom-Id": "demo@apim.eu",
        "X-Consumer-Id": "35cfdf1b-11ef-4582-b80f-a0cfb1d67b07",
        "X-Consumer-Username": "demo@apim.eu",
        "X-Forwarded-Host": "localhost",
        "X-Forwarded-Path": "/mtls-auth",
        "X-Forwarded-Prefix": "/mtls-auth",
        "X-Kong-Request-Id": "ada6f204577fe50a144706292a01f421"
    },
    "json": null,
    "method": "GET",
    "origin": "192.168.65.1",
    "url": "https://localhost/anything"
}

```


### Kong->upstream APIのmTLS設定

Kong側がAPIにアクセスする際、Upstream API側がclientの証明書を求めています。そのため、Kong側でクライアントとしての証明書を登録する必要があります。登録の手法が二つありまして、Kong全体での登録と、Service単位の登録

#### Kong全体での登録

設定ファイルに、以下の三つのパラメータを追加します。KongがAPIにリクエストを送るときにclient 証明書も一緒に送ります。

```/etc/kong/kong.conf:
client_ssl = on
client_ssl_cert = /etc/secrets/my-kong-certificate/kong.crt
client_ssl_cert_key = /etc/secrets/my-kong-certificate/kong.key
```

#### Service単位での登録

Serviceを作る際に、`client_certificate`でclient 証明書を追加することができます。
優先順位として、Service単位＞Kong全体になるため、両方設定した場合は、 Service単位の方が使われます。


## まとめ
mTLSを適切に設定することで、API通信のセキュリティを大幅に向上させることができます。本記事では、「Client -> Kong」と「Kong -> Upstream API」の2つの通信経路における設定方法を紹介しました。

安全なAPI通信を実現するために、以下のポイントを押さえてください：
 - クライアント認証用のCA証明書を適切に管理する
 - KongとUpstream APIの間でクライアント証明書を正しく設定する
 - Kong全体の設定とサービス単位の設定を用途に応じて使い分ける

図を参考にしながら、ぜひ自分の環境にmTLSを導入してみてください。