はじめに
セキュリティ要件として、「明示的に許可した通信以外は全て拒否したい」というのはよくあると思いますが、Azureでそれをやろうとした際にちょっとハマったポイントがあり、調べて「そうなんだ」と思ったので内容をまとめます。
今は仕事で(ほぼ初めて)Azureを触っているのですが、Azureのコンソールは(AWSに比べて)あまり優しくないな、というのが現時点の正直な感想です。
システム構成
※やりたいこと
- クライアントサーバ(VNet-A)からWebサーバ(VNet-B)にHTTP接続する
- Webサーバの前段にALBを配置し、内部ロードバランシングを実施(ALBはWebサーバと同じサブネット上に配置)
環境情報
【クライアントサーバ】
- Windows Server 2016 Datacenter
- 自PCからの接続用にパブリックIP割り当てとRDP許可以外は全てデフォルトで構築
【Webサーバ】
- Ubuntu Server 18.04 LTS
- 構築後、NGINXをデフォルトでインストール
- 自PCからの接続用にパブリックIP割り当てを実施
- NSG設定は下記の通り(インバウンドルールのみ記載。アウトバウンドルールはデフォルト)
ソース | ポート | 宛先 | 許可/拒否 |
---|---|---|---|
10.0.1.0/24 | 80 | any | 許可 |
10.0.1.0/24 | 22 | any | 許可 |
(自PCのIP) | 22 | any | 許可 |
事象
Azureでセキュリティを厳密にするため、Webサーバ側のNSGに対し、個別にCIDR指定で許可したプロトコル(HTTPなど)以外のVirtualNetworkからの通信を全て拒否(Deny)するインバウンドルールを追加しました。
※追加設定
ソース | ポート | 宛先 | 許可/拒否 |
---|---|---|---|
any | * | VirtualNetwork | 拒否 |
NSG(AWSのSGも同様ですが)は優先度が高い(若い番号)のルールで許可されている方が優先されるはずでしたが、この設定を入れた瞬間にALB経由でのHTTP接続ができなくなりました。
事象切り分け
- NSG追加前はALB経由で接続OK
- NSG追加後もALBを経由しない場合(クライアントサーバからWebサーバに直接続する場合)は接続OK
エラーメッセージ
PS C:\Users\azureuser> curl http://10.1.1.4
curl : Unable to connect to the remote server
At line:1 char:1
+ curl http://10.1.1.4
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
※参考までに成功時
PS C:\Users\azureuser> curl http://10.1.1.6
StatusCode : 200
StatusDescription : OK
Content : <!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
<...
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Accept-Ranges: bytes
Content-Length: 612
Content-Type: text/html
Date: Tue, 18 Feb 2020 05:50:36 GMT
ETag: "5e4b58cf-264"
Last-Modified: Tue, 18 Feb 2020 ...
Forms : {}
Headers : {[Connection, keep-alive], [Accept-Ranges, bytes],[Content-Length, 612], [Content-Type,
text/html]...}
Images : {}
InputFields : {}
Links : {@{innerHTML=nginx.org; innerText=nginx.org; outerHTML=<A href="http://nginx.org/">nginx.org</A>;
outerText=nginx.org; tagName=A; href=http://nginx.org/}, @{innerHTML=nginx.com;
innerText=nginx.com; outerHTML=<A href="http://nginx.com/">nginx.com</A>; outerText=nginx.com;
tagName=A; href=http://nginx.com/}}
ParsedHtml : System.__ComObject
RawContentLength : 612
調査のためにやったこと
通信のどこで詰まっているかを調べるため、クライアントサーバ上でパケットキャプチャーを取得しました。
(Azureライクなやり方もあるのかもしれませんが)
Windows Serverでのパケットキャプチャー取得手順
手順はこちらの記事を参考にしました。
ただ、netshコマンドで作成した.etlファイルを.pcap形式に変換する際に使う「Microsoft Message Analyzer」は既に配布終了となっているそうです。
代替ツールがMicrosoftから提供されているので、今回はそちらを使用しました。
※参考情報
https://troushoo.blog.fc2.com/blog-entry-416.html
解析結果
接続NG/OK時のキャプチャーはそれぞれ下記の通りです。
※要点
- 「10.0.1.5」がクライアントサーバ
- 「10.1.1.4」がALB
- 「10.1.1.6」がWebサーバ
- 成功時は「10.1.1.4」から「10.0.1.5」に200が返却されている(接続OK時のNo.44)
- 「[TCP Retransmission]」は通信エラーで再送しました的なメッセージ
これだけ見てもなかなか難しい間違い探しになりますが、共通点として、目的の宛先に繋ぐ前に「168.63.129.16」というアドレスに対して接続を行っているように見えました。
何かの手がかりになると思い、このアドレスが何者かについて調べました。
168.63.129.16とは?
調べてみたら、Microsoftのサイトに情報がありました。
IP アドレス 168.63.129.16 は、Azure プラットフォーム リソースへの通信チャネルの使用を容易にするために使用される仮想パブリック IP アドレスです。
これは、既定のネットワーク セキュリティ グループの規則によって許可されています。 インバウンドとアウトバウンド両方向のすべてのローカル ファイアウォール ポリシーで、この IP アドレスを許可することをお勧めします。
要するに、Azureが提供するサービスにアクセスするためのパブリックIP、ということです。
AWSでもS3などのリージョンサービスはパブリックネットワーク上にあるため、そのサービスを利用するためにはインターネット接続(もしくはVPCエンドポイントによるプライベート接続)が必要になりますが、おそらくそれと似たような話なのだと思います。
理屈はさておき、NSGで「VirtualNetwork」からの通信を拒否したことにより、ALBを含むAzureサービスを利用するために必要な「168.63.129.16」への通信も遮断されてしまったため、ALB経由で通信ができなくなったのが原因だと考えられます。
検証①
上記を踏まえ、先程追加した定義の上にパブリックIPの通信を許可する設定を追加しました。
ソース | ポート | 宛先 | 許可/拒否 |
---|---|---|---|
168.63.129.16 | * | any | 許可 |
any | * | VirtualNetwork | 拒否 |
その結果、ALB経由でもHTTP接続が可能になりました。
検証②
一方で、ALBを前段にしてVMを構成するとNSGに「AzureLoadBalancer」の定義がデフォルト(優先度65001)で追加されますが、それを優先度を上げて定義したらどうなるか試してみました。
結論としてはこれでも接続OKでした。
ソース | ポート | 宛先 | 許可/拒否 |
---|---|---|---|
AzureLoadBalancer | * | any | 許可 |
any | * | VirtualNetwork | 拒否 |
下記のページによると、「AzureLoadBalancer」のサービスタグはパブリックIPに変換されるとあるので、これでも同じ意味になるようです。
※「VirtualNetwork」はもう少し範囲が広いものの、パブリックIPも含めた定義になっているようです。
まとめ
Azureはデフォルトだと同じVirtualNetworkからの通信は許可されてしまうため、「明示的に許可したもの以外は全て拒否」をしたい場合は、使用するサービスもサービスタグで明示的に許可を入れてあげる必要がある、とわかりました。
色々話を聞いていると、Azureも以前からするとかなり仕様が変わっていて、まだまだ発展途上ということらしいので、今後はアップデートに追いつけるようにしたいと思います。