CORS対応のためのReverse Proxyにtraefikを使ってみた

## はじめに

ブラウザから外部サービスのAPIにアクセスするためにCORS対応目的のReverse Proxyが必要になり
[Technology Radar vol.20](https://www.thoughtworks.com/radar)に載っていた[traefik](https://docs.traefik.io/)を使ってみました。

## CORSによるクロスドメイン通信とは

CORS(Cross-Origin Resource Sharing)によるクロスドメイン通信とは悪意のあるサイトから情報を
守るためのブラウザの機能に対して信頼できるサイトとして許可するためのものです。

![スライド3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485398/51cdb8d0-2ef1-206f-ed9d-59c539d419b7.png)

信頼できるサイトに対してID/PASSなどでサインインしているとき、
ブラウザ内にはCookieで認証情報を保持しております。
その時フィッシングメールなど何らかの手段で悪意のあるサイトにアクセスしてしまったときに
罠コンテンツから正規コンテンツにアクセスされたときにCookieとともにアクセスして情報を抜くことが可能となってしまうため、
このような攻撃を防ぐためにブラウザのセキュリティ機能の1つにSOP(Same Origin Policy)があります。
これによって異なるドメイン間の通信にはアクセスを許可するためのCORS対応が必要となります。

JavaScriptのAjaxなどで異なるドメインからの呼び出しは制限されており下記のようなエラーが出力されることになります。

~~~
Origin http://example.com is not allowed by Access-Control-Allow-Origin.
~~~

このエラーを回避するためには、呼び出される側のAPIレスポンスヘッダ ```Access-Control-Allow-Origin: <Domain>```に許可するドメイン名を指定します。
広く公開されているAPIの多くは ```Access-Control-Allow-Origin: *``` (アスタリスク)が許可されておりますが、
最近のChromeではドメイン名指定ではないものもエラーとなるようになっております。

## Reverse Proxyとは

特定のサーバへの要求を必ず経由するように設置されたプロキシサーバです。

- ロードバランサー的な機能
 - URLに応じたサーバ分岐
 - 負荷分散
- キャッシュ<br />など

![スライド4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485398/a6b7d0f0-1f18-2b8c-1863-0ea2ae1a1e8a.png)
サービスサーバの手前にReverse Proxyを立てることでURLに応じたサービスサーバや
ポートの振り分けを行ったり負荷分散のために複数のサービスサーバに割り振ることができます。

## 今回のReverse Proxyの立ち位置

APIサーバ群の手前にKongによって全APIのCORS対応を行っている環境で外部APIへのアクセスを
Kongの後ろに立てたReverse Proxy経由で行うことでCORS対応を行うものです。
![SAS-ReverseProxy(traefik).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485398/0baf6add-e862-562f-861b-b08212fc1aff.png)

## traefikとは

![スライド6 - コピー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485398/1a6add68-433d-b639-5b46-eeb2b9079241.png)

traefikとは Go 言語で書かれたリバースプロキシで設定を動的に変えられることが特徴です。
リバースプロキシと言えば Nginx が主流ですがそれと比較して下記メリットが挙げられます。

- 設定ファイルを変更した後のリロードが不要
- バックエンドサーバが起動していなくてもOK
- Web UI

![Web UI](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485398/db517ef3-259f-949f-ecb1-2fadd68698b1.png)

設定方法のバリエーション

- 設定ファイル (ファイル監視による更新後自動反映)
- Rest API
- DynamoDB
- Dockerプロセス
- ECS
- など...

## 実行方法

[traefik download](https://docs.traefik.io/#downloads)からダウンロードしたバイナリファイルに設定ファイルを指定して実行する。

~~~
./bin/traefik -c ./config/traefik.toml
~~~

```toml:./config/traefik.toml
################################################################
# Global configuration
################################################################
defaultEntryPoints = ["http", "https"]

[accessLog]

[entryPoints]
  [entryPoints.http]
    address = ":80"

[file]
  directory = "./rules/"
  watch = true
```

:information_source: `watch = true` とすることで `rules` 配下のファイルが更新されると自動反映されます。

```toml:./rules/example.toml
[frontends.example]
  entryPoints = ["http"]
  backend = "example"
  passHostHeader = false
  [frontends.example.routes.route]
    rule = "PathPrefixStrip: /reverseproxy/https://example.com"

[backends.example.servers.server]
  url = "https://example.com"
```

:information_source: デフォルトでは転送先サーバへのリクエストホストヘッダーに転送元サーバのホストが渡るため
転送先に弾かれるケースがあるため `passHostHeader = false` とします。

こちらの設定で外部APIへReverse Proxy経由でのアクセスが可能となります。
```http://<Reverse Proxy Host>/reverseproxy/https://example.com```

## まとめ

今回は多機能なtraefikを用いて最小限の機能のみを使ってReverse Proxyを立ててみましたが、
動的に設定変更できるメリットに魅力を感じました。