はじめに
VNet 統合した App Service は、アウトバウンド通信を Azure Firewall 経由にできます。この構成にすることで、App Service からインターネットへの送信元 IP アドレスを固定したり、App Service からのインターネットへの通信をホワイトリストで厳密に制御できるようになります。
Azure Firewall を使用して送信トラフィックを制御する
「この構成で App Service からの通信を TLS インスペクションできるのだろうか?」という素朴な疑問がわいたので調べた&試しました
TLS インスペクションの前提
TLS インスペクションを成立させるためには、Azure Firewall がクライアントとの HTTPS 通信で利用するサーバ証明書を発行する際に使う CA 証明書を、クライアントの証明書リストに追加する必要があります。
証明書を追加しないと、HTTPS 通信はエラーになります。TLS インスペクションな環境では、クライアントに届くサーバ証明書が Azure Firewall の CA 証明書で署名されています。そのため、Azure Firewall の CA 証明書がクライアントに追加されていない場合、クライアントはサーバ証明書を発行した認証局が正規なものではないと判断して SSL をエラーで終了させます。
つまり、PaaS の通信を TLS インスペクションできるかは、通信を行う PaaS のインスタンスに Azure Firewall の CA 証明書を追加できるかどうかが焦点になります。
App Service の場合
App Service の場合、通信を行うインスタンスがユーザ専用になる App Service Environment でのみ CA 証明書を追加できます。マルチテナント型の App Service には CA 証明書を追加できません。
Web Apps for Container の場合
Web App for Container の場合、App Service を構成するインスタンスではなく、実際に通信を行うコンテナに CA 証明書を追加する必要があります。コンテナの中身はユーザが自由にいじれますので、CA 証明書を配置できます。実際に試してみました。
検証
アクセスされたら ifcofig.me に HTTPS でアクセスして自分の送信元グローバル IP アドレスを返す Web アプリをコンテナで動作させます。HTTPS でアクセスしているので、TLS インスペクションの対象になります。
import http.server
import socketserver
import requests
LISTEN_PORT = 8080
class ServerHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
r = requests.get("https://ifconfig.me")
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(r.text.encode('utf8'))
if __name__ == "__main__":
HOST, PORT = '', LISTEN_PORT
with socketserver.TCPServer((HOST, PORT), ServerHandler) as server:
server.serve_forever()
FROM python:3.6.9
WORKDIR /app
COPY ./opt /app/opt
EXPOSE 8080
RUN pip install requests
CMD ["python", "opt/web.py"]
Azure Firewall で TLS インスペクションを無効にしている場合は、正常に動作します。VNet 統合によって経由している Azure Firewall の Public IP アドレスが返ってきていることが分かります。
> $azfw = Get-AzFirewall -ResourceGroupName labnet -Name azfw
> $pip = Get-AzResource -Id $azfw.IpConfigurations[0].PublicIpAddress.Id
> $pip.Properties.ipAddress
20.18.96.149
> curl https://ymacr.azurewebsites.net/
20.18.96.149
一方で、Azure Firewall で TLS インスペクションを有効化すると、ifconfig.me にアクセスできなくなるためアプリが正常に動作しません。502 が返ってきます。
> curl https://ymacr.azurewebsites.net/ --verbose
* Trying 13.73.26.73:443...
* Connected to ymacr.azurewebsites.net (13.73.26.73) port 443
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server accepted http/1.1
* using HTTP/1.1
> GET / HTTP/1.1
> Host: ymacr.azurewebsites.net
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 502 Bad Gateway
< Content-Length: 0
< Date: Wed, 15 May 2024 14:41:01 GMT
<
* Connection #0 to host ymacr.azurewebsites.net left intact
アプリのログには証明書の検証に失敗したことを示すエラーが記録されます。
requests.exceptions.SSLError: HTTPSConnectionPool(host='ifconfig.me', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:852)'),))
TLS インスペクションを成立させるために、コンテナに Azure Firewall の証明書を組み込みます。これが正しい方法なのか不明ではあるのですが動きはしました…
Azure Firewall の CA 証明書を KeyVault から CER 形式でダウンロードした上で、OpenSSL を利用して CRT に変換します。さらに Dockerfile に CA 証明書を配置する処理を追加した上でイメージをビルド、Azure Container Registory にプッシュします。
FROM python:3.6.9
WORKDIR /app
COPY ./opt /app/opt
RUN mkdir /usr/share/ca-certificates/mylocal
COPY ./opt/azfw.crt /usr/share/ca-certificates/mylocal/azfw.crt
RUN echo "mylocal/azfw.crt" >> /etc/ca-certificates.conf
RUN update-ca-certificates
EXPOSE 8080
ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
RUN pip install requests
CMD ["python", "opt/web.py"]
最後に Web Apps for Container が利用するイメージのバージョンを最新のものに変更します。そうすると、エラーなく動作するようになりました。Azure Firewall の CA 証明書を python の requests が利用することでサーバ証明書の検証が成功するようになったので、アプリが正常に動作して自分の送信元 IP アドレスを返せるようになりました。
> curl https://ymacr.azurewebsites.net/ --verbose
* Trying 13.73.26.73:443...
* Connected to ymacr.azurewebsites.net (13.73.26.73) port 443
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* ALPN: server accepted http/1.1
* using HTTP/1.1
> GET / HTTP/1.1
> Host: ymacr.azurewebsites.net
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Date: Wed, 15 May 2024 16:47:07 GMT
< Server: SimpleHTTP/0.6 Python/3.6.9
< Transfer-Encoding: chunked
<
20.18.96.149* Connection #0 to host ymacr.azurewebsites.net left intact
まとめ
「App Service からの通信を Azure Firewall で TLS インスペクションできるのか?」という素朴な疑問を試してみました。CA 証明書をインポートしなければならないという制約があるので、App Service Environment か Web Apps for Container を使えばよさそうです。
Note
- 当サイトは個人のブログです。このブログに示されている見解や意見は個人的なものであり、所属組織の見解や意見を表明するものではありません。
- 公開情報を踏まえて正確な情報を掲載するよう努めますが、その内容の完全性や正確性、有用性、安全性、最新性について一切保証しません。
- 添付文章やリンク先などを含む本サイトの内容は作成時点でのものであり、予告なく変更される場合があります。