AWS／AppRunner上でフォワードプロキシサーバの構築を試みた


## 経緯

* EC2上にSquidを導入してフォワードプロキシサーバとして運用しているのですが、可用性向上／運用負担軽減を目的にAppRunner上でSquidのDockerイメージが動作可能か試してみました。

## 結論

* AppRuuner上でフォワードプロキシは動作不可。(Fargateでは動作可）

## リバースプロキシとフォワードプロキシの違い

* リバースプロキシとフォワードプロキシはhttp(s)通信を中継する点では役割同じですが、リバースプロキシをフォワードプロキシとして使ってしまうと、

  * 外部送信を避けたい組織内の情報（X-Forwarded-forに個別端末のIPアドレスが付与される等）が転送される。

  * 送信先のURLとリバースプロキシのURLが分離されない。

というとわかりやすくなるかもしれません。

``` text:送信先URLの指定方法のイメージ(yahooにアクセスする場合を想定）

 # フォワードプロキシの場合（プロキシサーバのURLと送信先URLは分離されている）
 curl https://www.yahoo.co.jp -x forward-proxy-url:3128
 
 # リバースプロキシの場合（プロキシサーバのURLと送信先URLは分離されていない）
 # 構築方法でURL(含むクエリストリング)の組み方は変えられる
 curl https://reverse-proxy-url?dest_url=www.yahoo.co.jp
 curl https://reverse-proxy-url/www.yahoo.co.jp
 
```

* 比較表を作ると以下のような感じですが、双方http(s)通信を中継する役割は同じなので少しわかりにくいかもしれず、上記解説にて補足したものです。


| 特徴                | リバースプロキシ                          | フォワードプロキシ                           |
|---------------------|-------------------------------------------|---------------------------------------------|
| **役割**            | クライアントからのリクエストを受け取り、適切なサーバーに転送する。 | クライアントの代理として外部のリソースにアクセスする。 |
| **主な目的**        | 負荷分散、キャッシング、セキュリティ       | インターネットアクセスの制御、匿名化、キャッシング     |
| **主な用途**        | - ウェブサーバーの負荷分散<br> - SSL終端<br> - キャッシング | - 社内ネットワークのインターネットアクセス制御<br> - 匿名ブラウジング<br> - コンテンツフィルタリング |
| **設置場所**        | サーバーの前面                            | クライアントの前面                           |
| **接続の方向**      | インバウンドトラフィック（クライアント→サーバー） | アウトバウンドトラフィック（クライアント→インターネット） |
| **IPアドレス**      | クライアントにはリバースプロキシのIPアドレスが見える | 外部サイトにはフォワードプロキシのIPアドレスが見える   |
| **セキュリティ機能**| Webアプリケーションファイアウォール（WAF）、DoS攻撃の緩和 | フィルタリング、アクセス制御、匿名化                 |
| **クライアントからサーバに送信するデータの取り扱い**| 極力サーバに転送しようとする | 必要最小限にとどめる |
| **有名なソフトウェア**| Squid | Apache、Nginx |

## ざっくり手順

* squidのコンテナイメージを作成するDockerfile

```text:Dockerfile-squid
# 使用するベースイメージ
FROM ubuntu/squid:latest

# デフォルトのSquid設定ファイルをバックアップ
RUN mv /etc/squid/squid.conf /etc/squid/squid.conf.original

# Squid設定を直接書き込み
RUN echo "\
# Allow local machine to access\n\
http_access allow localhost\n\
\n\
# IPアドレスによるアクセス制限の設定\n\
acl allowed_ips src xxx.xxx.xxx.xxx \n\
http_access allow allowed_ips\n\
\n\
# Squid の設定\n\
acl Safe_ports port 1-65535          # all ports\n\
acl SSL_ports port 443\n\
acl CONNECT method CONNECT\n\
\n\
http_access allow Safe_ports\n\
http_access allow CONNECT SSL_ports\n\
http_access deny all\n\
\n\
# デフォルトのポート\n\
http_port 3128\n\
\n\
# キャッシュを無効にする設定\n\
cache deny all\n\
cache_mem 0 MB\n\
maximum_object_size_in_memory 0 KB\n\
cache_dir null /tmp\n\
\n\
# ログファイルの場所\n\
access_log /var/log/squid/access.log\n\
cache_log /var/log/squid/cache.log\n\
\n\
# その他のオプション\n\
coredump_dir /var/spool/squid" > /etc/squid/squid.conf

# 必要なログファイルとキャッシュディレクトリの作成
RUN mkdir -p /var/spool/squid \
    && chown -R proxy:proxy /var/spool/squid \
    && mkdir -p /var/log/squid \
    && chown -R proxy:proxy /var/log/squid \
    && squid -z

# Squidサーバーを起動
CMD ["squid", "-N", "-d", "1"]
```

```text:squidのDockerイメージをECRにプッシュ（ your-account-id を適宜変更）
# Dockerfile-squidを使ってSquidのDockerイメージをビルドし、イメージ名をhogehoge-squidにタグ付けする
docker build -f Dockerfile-squid -t hogehoge-squid .

# AWS ECRにログインするためのパスワードを取得し、Dockerにログインする
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com

# hogehoge-squidイメージに最新タグを付け、AWS ECRリポジトリのsquid:latestとしてタグ付けする
docker tag hogehoge-squid:latest your-id.dkr.ecr.ap-northeast-1.amazonaws.com/squid:latest

# 最新タグが付いたSquidイメージをAWS ECRリポジトリにプッシュする
docker push <your-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/squid:latest

```

* AWSマネジメントコンソールからAppRunnerを選択して、プッシュしたイメージを元にサービスを起動。（実際の手順は[公式](https://docs.aws.amazon.com/ja_jp/apprunner/latest/dg/getting-started.html)に委ねますが、体感して簡単に設定可能でした。）

## 検証結果

* 上記コンテナをAppRunnerにデプロイすると標準出力にログ出力されることになるが、端末からデプロイしたサービス経由で接続してもタイムアウトになり、サービス上のログもヘルスチェック用のログしか記録されない。

`通信シーケンスまで確認できていないですが、フォワードプロキシの場合、通信開始する際にHTTPのCONNECTメソッド（送信元がプロキシサーバーに送信先とTCP接続を確立するよう要求する役割）を使うのですが、AppRunnerの目的からして当然ではありますがAppRunnerはこのメソッドをサポートしていないからだと思われます。`

 
```
07-15-2024 12:15:45 AM 1720970144.822      0 169.254.175.249 NONE_NONE/000 0 - error:transaction-end-before-headers - HIER_NONE/- -
07-15-2024 12:15:55 AM 1720970154.823      0 169.254.175.249 NONE_NONE/000 0 - error:transaction-end-before-headers - HIER_NONE/- -
07-15-2024 12:16:05 AM 1720970164.823      0 169.254.175.249 NONE_NONE/000 0 - error:transaction-end-before-headers - HIER_NONE/- -
```
* AppRunner上にデプロイしても、以下のようなSquidのエラーページは表示されました。（以下は検証時と全く同じものではないです）
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3454854/9321588f-aac6-cc5f-9533-d2c136a6a201.png)

## 補足

* AWS上でフォワードプロキシを構築する際は、踏み台にならないように必ず送信元制限はかけましょう。（AWSの規約違反になります）

* 上記は厳密に検証／整理したわけではありませんので、その点割り引いてお読みください。