Code EngineでのClient IPの取得方法

# はじめに
アプリケーションを作成する際に、アクセスユーザーのClientIPを入手したいことがあるかと思います。今回はPythonのWebアプリケーションツールであるFlaskでサーバーをたて、そこにアクセスしたPCのClient IPを取得するアプリケーションをCode Engine上で実装することを目標として構築を行います。
Client IPサーバーを求める一般的な方法として、httpヘッダーの中にあるX-Forwarded-For(XFF)を利用します。しかし、Code EngineにはXFFを記録する機能がないため、Cloud Internet Serviceを間に挟むことで解決します。
なお、CISにはClient IPアドレスをヘッダーに埋め込む機能(True Client IP ヘッダー)もあり、最後に少し紹介します。

最終的な構成としては下図のように構成します。
![スクリーンショット 2023-02-12 21.41.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/1f7aa287-5de7-3092-1ef6-5d277b849c4c.png)


## XFFとは
X-Forwarded-For(XFF)とはX-Forwarded-Forとは、HTTPヘッダフィールドの1つです。ロードバランサやプロキシサーバーなどの機器を経由するクライアントのIPアドレスを特定するために用いられます。　クライアントの送信元IPアドレスの特定は、ロードバランサなどでクライアントの送信元IPアドレスが変換された場合でも、httpヘッダに元のクライアントIPアドレスの情報を付加することで実現します。
# 作業手順
## 1. FlaskによるWebサーバーの立ち上げ
まずはpythonでflaskを用いてWebサーバーを立ち上げ、サーバーのhttpヘッダーを取得するプログラムを作成します。

```python:server.py
from flask import Flask, request

app = Flask(__name__)

@app.route("/")
def http_header():
    c = dict(request.headers)
    return c

@app.route("/xfor")
def get_xfor():
    a = request.headers["X-Forwarded-For"]
    if a != None:
        return "<p>X-Forwarded-For</p>" + str(a)
    return "<p> None </p>"

if __name__ == "__main__":
    app.run()
```
flaskについて詳細な情報が知りたい方はこちらのサイトをご覧ください。
[【Python】Flaskとは？FlaskでWeb開発の基礎を学ぼう！](https://aiacademy.jp/media/?p=57)
このプログラムをローカル環境で実行すると[http://127.0.0.1:5000]でサーバーが立ち上がり、アクセスしてみると以下のようにhttpヘッダーの一覧が表示がされます。
![スクリーンショット 2023-02-08 9.51.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/ef18b416-3a2c-fb51-cf56-29eca04b29b4.png)
現在はローカル環境でアクセスを行っているため、XFFのヘッダーが存在していませんが問題ありません。





## 2. Code Engineへの実装
次に先ほど実装したプログラムをCode Eingineへデプロイしてみます。
おおよその流れとして、以下のサイトを参考に進めていきます。

https://qiita.com/satokota/items/b3cecd591e244fed391f

Code Engineのソースコードとしてpythonサンプルのgithubをベースに用います。
https://github.com/paketo-buildpacks/samples/tree/main/python/pip
プログラムに用いたFlaskをrequirements.txtに追加することでCode Engineのソースコードとして使用することができます。
実装の手順としては下記の通りに進めていきます。
1. Container Image保管用のContainer Registryの名前空間を作成
2. Code Engineのプロジェクトを作成
3. Code Engineにアプリをデプロイ
4. サンプルアプリへのアクセス

### 1. Container Image保管用のContainer Registryの名前空間を作成
まず最初にContainer Image保管用のレジストリーを作成します。
**「Container Registry」 ＞ 「名前空間」** を選択して名前空間一覧ページを開きます。
そこで、**「作成」** ボタンから名前空間を作成します。
![スクリーンショット 2023-02-13 13.21.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/348ae999-c854-f401-22e6-39f0fd55cbd2.png)


### 2. Code Engineのプロジェクトを作成
次にCode Engineのプロジェクトを作成します。
**「Code Engine」 ＞ 「プロジェクト」** を選択してプロジェクトページに移動します。
そこで、**「作成」** ボタンからプロジェクトを作成します。
プロジェクトの作成ページで必要項目を入力し、**「作成」** ボタンをクリックします。
![スクリーンショット 2023-02-13 1.45.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/51231b56-46fc-9d05-2e74-99cec1792e52.png)

### 3. Code Engineにアプリをデプロイ
最後にアプリケーションのデプロイを行います。
プロジェクトページから先ほど作成したプロジェクトを選択し、**「概要」 ＞ 「アプリケーションの作成」** を選択します。
アプリケーションの作成ページで必要な項目を入力します。
今回はソースコードとしてhttps://github.com/r1ku2/get_ip を使用します。
![スクリーンショット 2023-02-13 2.43.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/be0049b3-32a5-340b-e3f1-cfa357301b3b.png)
次にブランチ名やビルド方法を設定するために **「ビルドの作成」** を選択し、必要な項目を入力します。


最初は **「ソース」** です。プライベートリポジトリへのアクセス設定 や、ビルドする ブランチの指定 、ソースコードが配置されている コンテキスト・ディレクトリ の設定が可能です

今回はパブリックリポジトリで、ブランチ名はmain, コンテキスト・ディレクトリの変更の必要はないため、そのまま **「次へ」** をクリックします。
![スクリーンショット 2023-02-13 13.24.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/fd98515d-19cd-0b48-133c-1dd399d1b8ca.png)


次は **「戦略」** です。ここではビルドの方法を設定します
今回はアプリコードだけで自動ビルド（自分でDockerfileを用意しない方法）でのビルドを行うため **「Cloud Native Buildpack」** を選択し、**「次へ」** をクリックします。
![スクリーンショット 2023-02-13 2.44.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/1f64d4a6-c5b9-526c-9645-a88122fd75cd.png)

最後に **「出力」** です。ここではビルド後のコンテナイメージの名前や保管場所などを設定します。
ここでは **「名前空間」** に最初に作成した名前空間の名前を入力します。
![スクリーンショット 2023-02-13 13.24.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/814ad29a-7b8c-ca28-de87-e06853a1197e.png)
これでアプリの設定は完了なので、最後に **作成** ボタンを押してビルドを行います。
以下のようにビルド実行の状況:成功　選択されたリビジョン:対応可能となっていれば成功です。
![スクリーンショット 2023-02-13 13.26.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/e5a3693f-8783-b278-b1a4-7e9882c30fc9.png)


### 4. サンプルアプリへのアクセス
実際にデプロイしたアプリにアクセスしてみます。
右上のアプリケーションのテストからurlへアクセスすると以下のようなXFFを含めたhttpヘッダー入手できていることがわかります。("X-Forwarded-For":"X.X.X.X, 127.0.0.6, 127.0.0.6"のような出力が含まれている)
![スクリーンショット 2023-02-13 13.15.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/6e1db061-e711-3eec-f767-75eb775ea02f.png)



XFFに含まれているIPアドレスのうち、先頭のものがClientIP(この場合自分のPCのIPアドレス)になるはずですが、確認してみると一致しないかと思います。これはCode EngineにはアクセスしたクライアントIPアドレスを記録する機能がないことが原因となります。
## 3. Cloud Internet Serviceの経由
XFFにClientIPを記録するためにCode Engineの前にCloud Internet Service(CIS)を経由させるようにします。
CISを用いてCode Engineに割り振る設定に関しては以下の記事を

https://qiita.com/takason/items/b3342be6f4fcc2e8e2e6


CISを経由させるためには以下の二つを設定する必要があります。
1. page ruleの設定
2. DNSの設定

以上を設定し、CISを経由したカスタムドメインを作成します。
CISによって設定したカスタムドメインからアクセスすると無事に自分のPCのIPアドレスが保存されていることが確認できます。("X-Forwarded-For":"Y.Y.Y.Y, 127.0.0.6, 127.0.0.6"のような出力が含まれており、Y.Y.Y.Yが自身のPCのIPアドレスと一致してれば成功)
![スクリーンショット 2023-02-13 13.47.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/228c5892-7156-573f-6d54-c8ffa704e0d5.png)


### おまけ：True client IP ヘッダーの取得
CISのページルール設定の中には **「True client IP ヘッダー」** の設定があります。こちらを設定することでXFFと同様にClient IPの取得を行うことができます。
![スクリーンショット 2023-02-09 12.09.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/14b5c649-805a-a622-d967-2acb455bb5d3.png)
この設定をオンにして再びカスタムドメインからアクセスすると表示されているhttpヘッダーの中にTrue-Client-Ipが追加されており、自身のPCのIPアドレスが確認できます。
![samp6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2625926/e789d28e-470d-2dfc-643f-5b324a0f9d64.jpeg)