【Azure】 Application GatewayとAPI Managementの統合構成

# はじめに

平素より大変お世話になっております。
今回はApplication GatewayとAPI Managementの統合構成についてです。

API Managementは単体でもインターネットから接続させることが可能ですが、セキュリティ的に不安が残ります。
やはりセキュリティを考えたら、できる限りInternalな環境においておきたいものです。
API Managementは、DeveloperとPremiumのSKUの場合のみ、VNet内部に設置することができます。
Developerはテスト用なので、実用で用いることが可能なのはPremiumのみですね。

このとき、API Managementに対して外部から接続可能にするためには、Application Gatewayを利用する方法があります。
そのドキュメントがまぁまぁ複雑なので、今回ポータルベースでやってみることにしました。

# 構成
## 利用するリソース

今回の構成をするための最低限必要なリソースは以下の通り。

|リソース名|SKU|用途|
|:---|:---|:---|
|Application Gateway | Standard v2 |V1でもV2でも可能。|
|API Management | developer | Internalに構築する前提。開発者ポータルにも接続する|
|パブリックIP| Standard |Application GatewayとAPIManagement用の2つ|
|VNet|-|applicationgteway用とAPIManagement用の2つ用意する。サブネットも作成|
|プライベートDNSゾーン|-|API Managementで利用するドメインの名前解決用|

## ネットワーク構成

ネットワーク構成は以下の図の通り。
重要なのは、__Application GatewayのVNetとAPI ManagementのVNetに対して、プライベートDNSゾーンを関連付けすること__です。
これによって、API Management用カスタムドメインの名前解決ができるようになります。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/5f17386c-b439-066c-68e9-b094566e5ae1.png)|
|--:|

## Applicationgatewayの構成

リスナーを3つ用意し、バックエンドも3つ用意します。
API Managementに付与したカスタムドメインをリスナーのホスト名として指定します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/4722237e-3191-def0-b0b3-471437157bd4.png)|
|--:|

基本的にはAPI ManagementのカスタムドメインとApplicationGatewayのホスト名は一致させたほうがよいですが、API Gatewayのホスト名は一致させなくても大丈夫なようです。

# 構築

## API Managementの作成

まずはAPI Managementを構築していきます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/02e3cda2-bb5b-a988-6c0b-398b2c4bfddc.png)|
|:--|

パラメータは適当に入れてます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/59213080-8cd6-ce35-78d0-48d646996764.png)|
|:--|

ネットワークの構成で、「内部」を指定してVNet内に設置するように設定。
VNet名は「vnet-APIManagement」、サブネットは「API-Management」にしました。

あとは特に設定せず、「作成」を選択。
作成できたら、利用しているプライベートIPをメモしておきます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/93b25f06-8e7d-9f6a-f217-c7882c47d5bc.png)|
|:--|

## 自己証明書作成

HTTPS通信を行うので、CAを立てて自己証明書を発行しておきます。
以下のPowerShellスクリプトを利用します。

``` 
$trustedRootCert = New-SelfSignedCertificate `
    -Type Custom `
    -CertStoreLocation  "Cert:\CurrentUser\My" `
    -KeySpec Signature `
    -Subject "CN=myCA" `
    -KeyExportPolicy Exportable `
    -HashAlgorithm sha256 `
    -KeyLength 4096 `
    -KeyUsageProperty Sign `
    -KeyUsage CertSign `
    -NotAfter (Get-Date).AddMonths(24)
 
$sslCert = New-SelfSignedCertificate `
    -Type Custom `
    -CertStoreLocation  "Cert:\CurrentUser\My" `
    -KeySpec Signature `
    -Subject "CN=*.app-apitest.net" `
    -DnsName "app-apitest.net","*.app-apitest.net" `
    -KeyExportPolicy Exportable `
    -HashAlgorithm sha256 `
    -KeyLength 2048 `
    -Signer $trustedRootCert
 
Export-Certificate  `
    -Type CERT `
    -Cert $trustedRootCert `
    -FilePath .\trustedroot.cer
 
$pfxPassword = ConvertTo-SecureString -String "SomePassword123" -AsPlainText -Force
Export-PfxCertificate `
    -ChainOption BuildChain `
    -Cert $sslCert `
    -FilePath .\app-apitest.pfx `
    -Password $pfxPassword
```


## Applicationgatewayの作成

次にApplication Gatewayを作っていきましょう。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/c97ac82f-4ef9-37db-84d6-7a0147ea4275.png)|
|:--|

適当に設定をします。なぜかデフォルトが「japan west」になっているので注意。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/756be0a8-8127-8408-615f-90d4428049cd.png)|
|--:|

フロントエンドにパブリックIPをつけます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/092171a6-1211-f423-2bfe-65edb29aa5a6.png)|
|--:|

バックエンドプールに「gateway.app-apitest.net」を設定します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/e404024c-5efa-d978-fc3f-0c2f3f256d6d.png)|
|--:|

同じように、「developer.app-apitest.net」、「management.app-apitest.net」も設定します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/d58a1434-5b6f-872d-5e87-a3e6419bf36a.png)|
|:--|

ルーティング規則としてリスナーを設定します。さきほど作成したpfxをアップロード。
今回はApplicationGatewayに３つのリスナーを作成するので、マルチサイトを選択します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/828c56d3-8bee-39be-86dc-d87355278217.png)|
|:--|

バックエンド側のHTTP設定を作成します。
HTTPSを選択して、これもさきほど作成したcer証明書をアップロードします。
「新しいホスト名でオーバーライドする」で「はい」選択することを忘れずに。
カスタムプローブはのちに作成しますが、ここでは一旦「いいえ」にしておきます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/717b1627-c585-5913-c7b7-c107c272585f.png)|
|:--|

バックエンドターゲットとHTTP設定を紐づけする設定をして、追加します。
AzureポータルからApplication Gatewayの作成をする都合上、developerやmanegement用の設定は今作成できないので、このまま作成を選択。
(同じ証明書の登録が新規作成のときはできないため。)

これで作成完了です。

## プライベートDNSゾーンの作成

では次にプライベートDNSゾーンを作ります。
ドメインは、適当に「app-apitest.net」を利用します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/15691bc4-0d11-e523-9959-15c748726f04.png)|
|:--|

プライベートDNSゾーンが作成できたら、それぞれのレコードを作っていきます。
紐づけるIPは、API Management のプライベートIPアドレスです。

|カスタムドメイン名|紐づけるIP|用途|
|:---|:---|:---|
|gateway.app-apitest.net|10.0.0.5| API gateway用|
|developer.app-apitest.net|10.0.0.5|開発者ポータル用|
|management.app-apitest.net|10.0.0.5|開発者ポータル管理用|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/ad256b79-dd15-0ec5-45ab-c55b50f1bd33.png)|
|:--|

同じように3つ作成します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/0efc4307-fe72-e9a7-8890-663d8b353913.png)|
|--:|

API Management、Application GatewayのVNetに対して「仮想ネットワークリンク」を追加します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/769c01d6-0d28-3fb6-ae02-c80afa7cee49.png)|
|:--|

これで2つのVNetからプライベートDNSを参照できるようになります。

忘れないように、ここで2つのVNetのピアリングもやっときます。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/619622ae-177f-69a9-34e2-72c81fdbc415.png)|
|:--|

## API Manegementのカスタムドメイン設定

出来上がったAPI Management に対して、カスタムドメインを設定します。

API Managementのメニューから、カスタムドメインを選択。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/5dce4688-67cb-9fe1-ee92-2117fdd38453.png)|
|--:|

gatewayの場合、既定のSSLバインディングを指定を忘れずに。
これまた同じように、develop,managementも設定します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/93e49ada-2624-35ca-ca22-55ab7ea7ca1d.png)|
|:--|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/a6285b4a-9c58-55ee-45c1-ec9430c1e91f.png)|
|:--|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/679956e7-8305-27b1-f1b4-34eba313e517.png)|
|:--|

これで保存します。

## Application Gatewayのリスナー追加設定

作成時に作れなかったdeveloper、management用のリスナーを作成します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/898c5ed3-c406-bf5a-2972-c7cabd184d62.png)|
|--:|

同じようにmanagement用のリスナーも作成します。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/609a8943-81ac-2201-9127-21ee1075ead0.png)|
|:--|

さらに同様に、作れなかったリスナーも作ります。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/0fe39b5d-dfdc-a7cd-23dd-ff841493ac62.png)|
|--:|

同様にmanagement用も作成し、全部でリスナーが3つになります。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/d32e1c7b-3b2c-faa6-11c8-286700023833.png)|
|--:|

そしてルールを作成し、バックエンドターゲットとリスナーを紐づけします。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/a5f2c810-c7e2-ab3a-32ea-1ef9323d28c1.png)|
|:--|

## 正常性プローブ設定

ここまで来たらもう一息！
正常性プローブを設定します。デフォルトのプローブ設定では、正常性確認に失敗してしまいます。
設定するパスは次の通り。

|バックエンド|設定するパス|
|:---|:---|
|gateway|/status-0123456789abcdef|
|developer|/signin|
|management|/ServiceStatus|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/20e090df-d4c5-cd95-daa4-b34b0faa0417.png)|
|:--|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/d2fb64b0-a422-27a1-f4a4-87821c8fc01c.png)|
|:--|

全部設定するとこのようになります。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/7f60d2a4-b2c3-0e49-3392-66ddb42eccbe.png)|
|:--|

バックエンド正常性ですべて健全になっていればOK。どこかエラーが発生しているのであれば、どこかの設定が間違っています。


## 動作確認

ではローカル端末のHostsを設定して、動作確認をしましょう。
Hostsファイルの場所は以下です。

```
C:\Windows\System32\drivers\etc
```

Application GatewayのパブリックIPと、設定したドメインを紐づけます。

```
20.89.146.95 gateway.app-apitest.net
20.89.146.95 developer.app-apitest.net
20.89.146.95 management.app-apitest.net
```

ここまでできたら、APIManagementの上部メニューから、開発者ポータルにアクセスします。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/6284aa06-e12b-0c03-b556-72a21d6ef328.png)|
|:--|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626338/efeb6e8e-5c66-b7f5-e98c-3a24009c79a0.png)|
|:--|

このように開発者ポータルの管理画面が表示されれば正常です。お疲れ様でした！

# 終わりに

一点注意事項があります。
__Application GatewayでWAFを利用する場合は、WAFの穴あけ作業が必要です。__

>開発者ポータルで行う OpenAPI 仕様のダウンロードが Application Gateway WAF によって中断されないようにするには、ファイアウォール規則 942200 - "Detects MySQL comment-/space-obfuscated injections and backtick termination" を無効にします。
>以下の Application Gateway WAF ルールによって、ポータルの機能が中断されることがあります。
>管理モード用: 920300、920330、931130、942100、942110、942180、942200、942260、942340、および 942370
>発行されたポータル用: 942200、942260、942370、942430、および 942440

ご注意くださいませ。


# 参考
https://docs.microsoft.com/ja-jp/azure/api-management/api-management-howto-integrate-internal-vnet-appgateway