Azure Webapp をセキュアにする（プライベートエンドポイント、ApplicationGateway、WAF、NSG）

# 概要

- 以下のような構成です
    - webapp のパブリックアクセスを遮断して、ApplicationGateway からプライベートエンドポイント経由にします
    - 受信規則を WAF、送信規則を NSG か FW で作成します
        - やり方は一応どちらも調べたのですが、FW は料金がすごく高くて（月１０万ぐらい）今回の場合は NSG で対応出来そうだったので NSG で進めます
:::note info
- 送信規則を NSG で作成した場合
    - FQDN で規則を作成出来ないので、送信先の IP が変わったり、一つの FQDN に大量の IP が存在する場合に設定が面倒くさい
    - 無料
- 送信規則を Firewall で作成した場合
    - FQDN による規則を作成出来る（Standard 以上）
    - 料金が高い（月１０万円超え）
- デプロイ時の pip install も pypi.org の送信規則許可が必要で、IP を許可する事で pip install 出来ますが、FQDN で設定出来ない NSG は IP が変わる度に設定を更新する必要がありそうです
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/fdc2e7b9-c95c-54e8-8caa-f7599b22b0e3.png)


# リソース作成

- 各リソースを作成します

## VirtualNetwork

- VNET を 3 つ作成します
    - 動作確認のため NSG, FW 用をどちらも作っていますが、初めからどちらでやるか決めている場合は片方の作成で良いです
- リージョンは揃えます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/bc95fedf-7421-0f18-4c3a-14b29e402929.png)

- プライベートエンドポイント用にサブネットを作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a81c2a1e-2fdf-56f3-5a16-30851a3105a6.png)

- FW 用にサブネットを 2 つ作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/d704703f-6003-85a7-2fa5-5090bd6f4e21.png)

## Webアプリ

- AppService プランを作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/749c3d3f-a5c0-44e7-58da-d3c8c2c0d01b.png)
- web アプリを作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/ccb20a4a-d5e2-606a-8e83-b94acaa4d3e5.png)
- 適当なリクエストするアプリを作ります

```python:app.py
import requests
import gradio as gr

def request(input: str):
    try:
        r = requests.get(input, timeout=(3.0, 5.0))
        return r.text
    except Exception as e:
        return e

demo = gr.Interface(
    fn=request,
    inputs=[gr.Textbox(value="https://pypi.org/")],
    outputs=["text"],
    flagging_mode="never"
)

demo.launch(server_port=8000, server_name='0.0.0.0')
```

```python:requirements.txt
gradio==5.8.0
requests==2.32.3
```

- 以下通りにデプロイします

https://qiita.com/KUROMAGOORO/items/9f4a942e4bee651bf850#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4

:::note warn
統合する VNET は NSG, FW どちらか利用する方を選択して下さい
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/8a7969e4-44b3-212f-f36c-dbbd7717f8ad.png)

## NetworkSecurityGroups

- NSG を作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/da65ddae-171f-ec55-3ac8-6db3d882d996.png)

- サブネットと関連付けします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/97b31a0a-9f5f-2edb-3279-badaace6b796.png)

- デフォルトの設定では外部（Internet）の送信が全て許可となっているので、全て拒否（下記画像の優先度:200 のルール）を追加すると、下記のようにデプロイ時の pip install で失敗します
    - 下記画像の優先度 180, 181,（ 190,191 は無くても良いかも）の pypi に関する規則を追加するとインストールが正常に完了して、アプリを立ち上げられました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/025fec16-7752-2fdd-93ab-0694fbfb8232.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/fc0bcd51-ccaa-b5b8-bcf5-d91e2d882d7d.png)

## Firewall

:::note warn
web アプリの VNET 統合を FW のサブネットに設定してください
NSG のサブネットと統合している場合は切断して付け替えます
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/f51630c7-12a2-dd75-fb91-0337357ad0c8.png)

- 公式ページを参考に作っていきます
https://learn.microsoft.com/ja-jp/azure/app-service/network-secure-outbound-traffic-azure-firewall

- FW を作ります
    - ポリシー、管理パブリック IP は適当な名前で新規作成しました
    - DNS を使えるようにする設定は Standard 以上で可能す（Basic では FQDN による規則が作れなさそうでした。Firewall の Basic を使うなら NGS の方が無料・シンプルで良いかもしれないです）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/c0e00993-52cd-1b5e-6edf-41ee6170fa8c.png)

- ルートテーブルを作ります
    - ゲートウェイのルートを伝達する：No にします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/e01d2cd7-f7d7-5411-ff64-9b12ae90f78f.png)

- ルートを追加します
- 「次ホップ IP アドレス」は FW のプライベート IP アドレスです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/9653bef5-00f3-8dcd-35cd-ac2174f16644.png)

- サブネットを関連付けします
- web アプリで VNET 統合したサブネットを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/8b2ace9b-3d2e-6c9e-8db1-1128621f1b37.png)

<details><summary>メモ　実施不要</summary>

:::note warn
DNS サーバを有効にした場合にリクエストが飛ばせなかった
時間切れなのでメモ
:::

- DNS Proxy を「有効」にします
- 送信規則で FQDN を設定するために必要です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/289319ca-a640-b6e3-5a4f-46664cc82ba0.png)

</details>

### FW ポリシーを更新
- VNET 統合したサブネットから全ての送信を拒否してみます
    - ソースはアプリのプライベート IP 指定でも良いみたいです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a15d157d-a8a5-a40d-c32d-97d197537589.png)
- リクエストは飛ばなくなります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/8ef32fcf-d5ec-6dd3-a7ca-5256d674f825.png)
- pypi の IP を許可するとリクエストが飛びます（優先度は値が低い方が優先されます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/4eb4b7fc-8ab0-8e81-ad9e-ac8365aa7438.png)


## ApplicationGateway

- 以下手順で ApplicationGateway を作成します
- 「既定のドメイン」で進めていますが、証明書の問題があるので「カスタムドメイン」が良さそうです
- 参考
<https://learn.microsoft.com/ja-jp/azure/application-gateway/configure-web-app?tabs=defaultdomain%2Cazure-portal>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/8e55ba6f-6c09-b9c8-7cd2-2b904e623951.png)

- この時点では web アプリのパブリックアクセスが有効であれば、ApplicationGateway に設定したパブリック IP にアクセスする事で web サイトが表示されます

# ApplicationGateway の動作確認（web アプリのパブリックアクセス無効化～）

## 事前準備

- web アプリのパブリックアクセスを無効化します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/d0546496-dd28-3f60-3203-225effc93ee0.png)

- web アプリのネットワーク設定から、プライベートエンドポイントを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/e64ec236-3d1c-1df0-03bb-3e526e82156c.png)

## アクセスして確認

- うまく画面に表示されない時は以下のコマンドで再起動を試します
- 参考
<https://zenn.dev/microsoft/articles/20240113-appgw-webapp-pe>
```ps1
az network application-gateway stop --resource-group rgname --name app-gatewayname
az network application-gateway start --resource-group rgname --name app-gatewayname
```

- https://[webアプリ名].japanwest-01.azurewebsites.net/ にアクセスすると web アプリのネットワーク設定によりブロックされます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/0a601e83-475e-7e27-38e5-f31a96a5bb93.png)

- ApplicationGateway に設定したパブリック IP にアクセスすると web ページが表示され……ますが css のロードが上記 URL なのでうまくいきません…
- なぜか書き換えがうまくいっていないようです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/89af8358-c3a0-a9df-4a4a-369964fa662c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/051ecc60-f9d6-49c4-35da-e0a9d12d297a.png)

## 受信規則の設定

- WAF のモードを「検出」から「防止」に変更します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/cf80f591-12b5-8076-abea-ebc949e8a76b.png)

- WAF ポリシーから受信規則を設定します
    - 指定した IP を含まない場合は全て拒否と設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/6780b5f4-7402-3bae-e85e-4e89ee86ccb0.png)
    - 指定した IP の場合（許可）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/fbf93e34-5e5e-124e-0ff8-6826f280f69a.png)
    - 指定した IP ではない場合（拒否）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/23d17c9d-88f6-8742-52ea-29a73eb1ff3d.png)