はじめに
自身の学習用にAzure Web Appsを使ってWebアプリを立てています。
Webアプリへの通信をHTTPS化するため、Let's Encryptによる無料証明書を発行し、Webアプリに食わせています。
Let's Encryptは3ヶ月に1回の頻度で証明書の更新を行う必要がありますが、
Azure Web Appsには便利な拡張機能として「Azure Let's encrypt」があり、本機能を利用することで煩わしい更新作業を自動化することができます。
※参考記事※
Azure Web Apps + Azure Let's Encryptの実装方法については別記事を引用しておきます。
● Let’s Encrypt を使って Azure Web Apps でのhttps通信を設定する
● Securing an Azure App Service Website under SSL in minutes with Let's Encrypt
本題
私のAzure上Webアプリはプライベート用途なので限られた環境からのみアクセスできるよう、送信元IPアドレスの制限をかけようと試みました。
まず最初にAzure App Service のアクセス制限を利用したのですが、3ヶ月後にLet's Encryptの証明書の更新が失敗する問題が発生しました。
その際に別の方法を使って解決することができましたので備忘録として残しておきます。
環境
- Azure Web Apps
- Webアプリ(ASP.NET MVC 5)
証明書更新ができなくなった原因
Azure App Service のアクセス制限により、Let's Encryptサーバ → Webアプリへの通信が遮断されていたことが原因でした。
Azure Let's encryptを使って証明書を発行/更新する場合、
Let's Encryptサーバが対象Webアプリの特定のディレクトリに対してACMEチャレンジ通信(http接続)を行うのですが、
IPアドレス制限によりアクセスできず、更新に失敗していました。(盲点でした、、)
どうするか?
改めてやりたいことを整理します。
①公開Webアプリの画面には許可された送信元IPのみ接続を許可する
②Let's Encryptとの証明書更新通信は許可する
③それ以外の通信は遮断する
上記を実現するため、最初にAzure App Service のアクセス制限にLet's EncryptサーバのIPアドレス/FQDNを登録できれば解決できると考えましたが、
Let's EncryptのIPアドレスは公開されておらず(そもそもIPが動的に変わるためIPによる制限は非推奨とのこと)、
また、Azure App Service のアクセス制限はFQDNの登録が不可のため、
①と②を実現するには別の手段を使って対応する必要がありました。
解決策
結論、web.config
ファイルを使って目的を達成することができました。
本ファイルを利用すれば、Web Appsのディレクトリ単位で柔軟に接続制限を行うことができるため、
今回のようにWebアプリの公開ディレクトリはIP制限をかけるが、
Let's Encryptのチャレンジ通信用のディレクトリへは不特定の送信元からでも接続できるようにする といったことが可能です。
※参考記事※
● Azure WebサイトでソースIPアドレスによるアクセス許可/拒否(制限)を設定する
● Azure App ServiceでIP制限
設定手順
【1】 IPアクセス制限(ホワイトリスト用)のweb.configを作成する
接続を許可するIPアドレスを以下のような構成で記述し、web.configを作成します。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<ipSecurity allowUnlisted="false" denyAction="NotFound">
<add allowed="true" ipAddress="x.x.x.x" subnetMask="x.x.x.x" /> <!-- ここに許可するIPを記述-->
<add allowed="true" ipAddress="x.x.x.x" subnetMask="x.x.x.x" /> <!-- 複数記述することも可能-->
</ipSecurity>
</security>
</system.webServer>
</configuration>
上記のdenyAction
にて、許可されていないIPからアクセスされた際の応答ステータスコードを指定することができます。
私の環境ではNotFound
(404)としましたが、他にも以下のように指定できるようです。
ステータスコード | denyAction属性の設定 |
---|---|
401 Unauthorized | denyAction="Unauthorized" |
403 Forbidden | denyAction="Forbidden" |
502 Bad Gatewayなど(応答中止) | denyAction="AbortRequest" |
【2】 Webアプリ公開ディレクトリ(wwwroot
)に手順1で作成したweb.config
を配置する
Kuduを開き、 D:\home\site\wwwroot ディレクトリ内にweb.config
を配置します。
wwwroot
└── App_Data
└── Views
└── web.config # ←配置
この状態にすることで、wwwroot
およびその配下へのアクセスは指定した送信元IP以外拒否されます。
【3】 手順1のIPアクセス制限を「無効化」させるweb.configを作成する
Let's Encryptのチャレンジ接続をlistenできるよう、手順1で作成したweb.config
を無効化させる用のweb.config
を別途作成します。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<ipSecurity allowUnlisted="true">
<clear/> <!-- 親ディレクトリに設定されているIP制限を無効化させる-->
</ipSecurity>
</security>
</system.webServer>
</configuration>
【4】 Let's Encryptのチャレンジ通信が行われるディレクトリを作成する
※1回でも証明書の作成・更新を行っていて、本ディレクトリがすでに作られている場合は読み飛ばして下さい。
Let's Encryptがチャレンジ通信の際に接続を試みるディレクトリを手動で作成します。
Kuduを開き、 D:\home\site\wwwroot ディレクトリに移動し、以下2つのディレクトリを新規作成します。
wwwroot
└── App_Data
└── Views
└── web.config
└── .well-known # ←作成
└── acme-challenge # ←作成
【5】 手順3で作成したweb.configを手順4のディレクトリ内に配置する
Kuduを開き、 手順3で作成したweb.config
を
手順4で作成したD:\home\site\wwwroot\.well-known\acme-challenge ディレクトリ内に配置します。
wwwroot
└── App_Data
└── Views
└── web.config
└── .well-known
└── acme-challenge
└── .web.config # ←手順3のweb.configを配置
こうすることで、.well-known\acme-challenge
へのアクセス時は親ディレクト(wwwroot直下においたweb.config)に設定したIP制限が無効化される状態となるため、送信元IPが特定できないLet's Encryptサーバからの通信を受け付けることが可能です。
※私の環境では無効化用のweb.config
をacme-challenge
配下に格納しましたが、仕様上、.well-known
配下に置いてもよいかもしれません。
以上で設定作業は完了です。
【6】 接続確認
今回の要件を満たしているかを以下のパターンで接続を行い確認します。
①Webアプリへのアクセス(接続が許可されたIP)
結果: アクセス可能(Web画面が表示される)
②Webアプリへのアクセス(接続が許可されていないIP)
結果: アクセス不可(手順1で設定したdenyAction
に応じたエラーコードが返る)
③http://[url]/.well-known/acme-challenge
へのアクセス(接続が許可されていないIP)
結果: アクセス可能
【7】 Let's Encryptの証明書更新
この状態であれば、Azure Web Appsの拡張機能 [Azure Let's encrypt]を利用して証明書の更新ができるはずです。
※更新手順は割愛します。
更新できた場合、Let's Encryptサーバが.well-Known\acme-challenge
ディレクトリ内にチャレンジ検証用のファイルを作成することが確認できます。
wwwroot
└── App_Data
└── Views
└── web.config
└── .well-known
└── acme-challenge
└── web.config
└── 3bN#87AtTUrQME9F1CCTknf9Oafpm8r43XJGRxdejml # ←チャレンジ通信時に生成されるファイル
以上です。