Azure Network Security Group(NSG)についておさらい

**[今後本記事のアップデートについてはこちらの新サイトにて行います、最新情報を希望の方はクリックお願いします](https://zakki.nya-n.net/?p=223)**

#はじめに
**[内容を2018年4月時点のものに更新しました]**

Azureで主にIaaSを使って仮想マシンを構成する場合、仮想ネットワークを作成すると思いますが、仮想ネットワークのサブネット間、仮想マシン間のネットワークトラヒックを制御するためにNetwork Securyty Group(NSG)は大変重要なものですよね。

NSGの大まかな説明や、詳細な説明については本家MSのサイトを参考にするのが良いのですが、もともと物理サーバのインフラエンジニアをやっていた筆者が最近NSGで気になったトピックをまとめてみました。

**本記事はARMでの説明となっています**

#NSG
詳しくは[ネットワーク セキュリティ グループを使用したネットワーク トラフィック フローの制御](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-networks-nsg?WT.mc_id=AZ-MVP-5002499)あたりに書かれています。

##適用できるリソース
NSGは仮想ネットワークのサブネット、もしくはNetwork Interface Card(NIC)に適用できるのはご存知の通りです。
一つのNSGを複数のサブネットに割り当てたり、複数のNICに割り当てることもできます。

下図の例では仮想ネットワークの「サブネット1」「サブネット2」それぞれに「NSG-1」「NSG-2」が適用されており、仮想マシン「VM-3」には(実際にはNICにですが)「NSG-VM3」が適用されている状態です。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/550de3d3-18e4-4b95-066e-f7c1f1a7cdc7.png)

ここで、VM-1からVM-3に対して通信を行う際にはどういったルールでNSGが判定されるというと以下の通りですよね。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/97de719b-ae0f-99e6-f4c7-c303f2dcedb0.png)

1. NSG-1の送信ルール
2. NSG-2の受信ルール
3. NSG-VM3の受信ルール

これはサブネット間のトラヒックになるため、特に引っかかることなく理解できます。元インフラエンジニアも納得です。

続いて、VM-1から同じセグメントにあるVM-2に対して通信を行う場合はどうでしょう。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/0e3e1e5e-2bcf-88eb-e797-717e63a6333f.png)

この場合は
1. NSG-1の送信ルール
2. NSG-1の受信ルール

が評価されます。
同じサブネット内であれば、隣(?)のサーバにはフリーで繋がるわけではないのでご注意ください。
次の章のNSG既定ルールでも書きますが、デフォルトルールで仮想ネットワーク間の通信は全ポート送受信ともに「許可」設定になっているため自在に接続ができているように見えているだけなのです。

念のため、VM-4からVM-3の通信の場合はこんな感じです。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/cd5d9e9b-e125-e9da-9079-5378b91bba70.png)

1. NSG-2の送信ルール
2. NSG-2の受信ルール
3. NSG-VM3の受信ルール

##受信規則、送信規則と既定のルール
以下の表はNSG作成時に無条件に定義される既定ルールです。

【受信の既定ルール】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|ALLOW VNET INBOUND|65000|VirtualNetwork|*|VirtualNetwork|*|*|ALLOW|
|ALLOW AZURE LOAD BALANCER INBOUND|65001|AzureLoadBalancer|*|*|*|*|ALLOW|
|DENY ALL INBOUND|65500|*|*|*|*|*|DENY|

【送信の既定ルール】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|ALLOW VNET OUTBOUND|65000|VirtualNetwork|*|VirtualNetwork|*|*|ALLOW|
|ALLOW INTERNET OUTBOUND|65001|*|*|Internet|*|*|ALLOW|
|DENY ALL OUTBOUND|65500|*|*|*|*|*|DENY|

ポータル上からもNSGリソースの「受信セキュリティ規則」もしくは「送信セキュリティ規則」のメニューから「既定の規則」をクリックすると確認することができます。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/593a0c92-d122-dff6-56d1-bb5488edef85.png)

既定のルールは「インターネットからのアクセスには厳しく、しかし仮想ネットワーク内の通信はゆるゆる。おまけにインターネットへのアクセスはなんでもおｋ」という設定になっています。

数台の仮想マシンを一つのサブネットで立ち上げて小規模なサービスを提供する場合には既定のルールに追加でインターネットからの受信ルールとして、TCP/443とTCP/80だけを空ける等で問題なさそうですね。

少し規模が大きくなって、サブネット毎にサーバ用途を分ける場合、既定のルールが有効なままだとDMZセグメントのサーバを踏み台にして、プライベートセグメントのDBサーバに侵入されてしまうケースもあり得ます。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/ff790d6f-835a-dda3-b332-96f0948b0016.png)

なので、NSGの受信ルールには既定ルールの手前にVirtualNetowrk間の受信も制限を入れて、それよりも優先度の値の小さい場所に必要な受信ルールを個々に設定するとよいと思います。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/a1230824-6db0-10f6-1586-c9f93d60d770.png)

【受信規則の優先度4090を追加】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|DenyVnetInbound|4090|VirtualNetwork|*|VirtualNetwork|*|*|DENY|

##タグの説明
NSGで指定可能なタグは以下の6つがあります(Azure Resource Managerの場合、Classicの場合は上の3つのみ)。
以下の通りMSサイトに書いてある説明で既に分かりやすいですが、しつこく補足します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/137436/4754b4c1-1c59-4583-3837-b2a6a74f35d6.png)

###「VirtualNetwork」
以下の全てが「VirtualNetwork」の範囲内になります。

- 仮想ネットワーク内の同一サブネット(当然ですね)
- 仮想ネットワーク内の別サブネット
- [仮想ネットワークピアリング](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-peering-overview?WT.mc_id=AZ-MVP-5002499)で接続された別仮想ネットワーク
- Site to Site接続された別の仮想ネットワーク(Azure、オンプレ)
- Point to Site接続されたクライアント側PC
- Express Routeによって接続されたオンプレ側ネットワーク

……結局のところ、インターネット以外すべてですね。
なので、安易にVirtualNetworkタグを使って受信規則をフルオープンにしてしまうと、社内の誰からも、どこからもアクセスできてしまうのでご注意ということです。

###「AzureLoadBalancer」
「AzureデータセンターIPに変換されます」と記載がありますが、たぶん「168.63.129.16」に変換されます。
このIPアドレス自身はグローバルIPアドレスなのですが、必ずAzureデータセンター内にある仮想IPアドレスであるため、ロードバランサー(Internal,External問わず)設定する際には許可してあげる必要があります。

既定のルールでは優先度65001で、AzureLoadBalancerからの受信はすべて許可となっていますが、こちらを無効化した場合は「ロードバランサーにProbeされるすべての仮想マシンの必要ポート」を空けます。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/30d3dffa-222d-b25c-6f25-eab9c8cc98be.png)
※なぞのAzureLoadBalancerがVM-2とVM-3の死活確認用ポートにアクセスする(例えばTCP/80)

###「Internet」
「VirtualNetwork」以外のアドレス空間は(多分)全てこちらの「Internet」になります。
通常は受信規則で「特定のWebサーバのTCP/443だけを空ける」などの場合に指定しますよね。

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowInternetHTTPInbound|200|Internet|*|10.0.0.4|443|TCP|ALLOW|

MSの資料にもあるとおり、こちらのINTERNETタグはAzure内の他のPaaSサービス等で使われるIPアドレスも含まれています。

特に受信規則ではAzureのPaaSサービスを起点として仮想ネットワーク内の仮想マシンにアクセスされるケースは少ないので問題になりづらいのですが、送信規則でInternet向けの通信を遮断した場合、色々面倒な事がおきます。
具体的には以下のルールを送信規則に設定した場合です。

【送信規則の優先度4090を追加】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|DenyInternetOutbound|4090|VirtualNetwork|*|INTERNET|*|*|DENY|

無論仮想マシン上のIE等のブラウザからgoogle検索などは行えなくなります。
他に私が知る限り以下の不都合が発生します。

- 仮想マシンに拡張機能(BGInfoなど)の追加操作をしてもデプロイが正常終了しない
- 仮想マシンの診断機能(Diagnostics)を有効にしてもストレージアカウントに結果が出力されない
- LogAnalyticsが有効なのに一向にログが転送されてこない
- 仮想マシンのバックアップが正常に完了しない

これらは全て仮想マシンのOS内からAzureのPaaSサービス(ストレージアカウント含む)への接続が行えないため発生します。

対処方法としては、LogAnalyticsはコントロールパネルの設定でproxyサーバが指定できるので回避可能かもしれませんが、他はやはりNSGの送信規則は絞らない方向で調整するのが宜しいかと思います。

###「AzureTrafficManager」【New!】
比較的最近追加されたタグですね。
Azure Traffic Managerからの着信だけを受けたいなどの場合にこちらを受信規則に指定します。

###「Storage」【New!】
MSドキュメントに記載の通りなのですが、Azureストレージアカウントに対して禁止・許可をすることができるタグです。
「Storage」の後に「.リージョン名」が記載できて、特定のリージョンだけを指定することもできます。
以下の送信規則を適用すると、「基本インターネット向け(Azureサービス含む)のOutboundは拒否だけど、ストレージアカウント(東日本)だけは繋げてよいよ」といったルールになります。

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowStorageOutbound|4080|VirtualNetwork|*|Storage.japaneast|*|*|Allow|
|DenyInternetOutbound|4090|VirtualNetwork|*|INTERNET|*|*|DENY|

2018年に入ってGAした「[仮想ネットワーク サービスのエンドポイント](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-service-endpoints-overview?WT.mc_id=AZ-MVP-5002499)」は、StorageとSQL DBを特定のVNET内Subnetからのみ限定的に接続するサービスですが、こちらのサービスタグを併用して必要に応じて送信規則を空けておく必要があります。

###「Sql」【New!】
先の「Storage」を同じですが、Azure SQL DBに対して禁止・許可をすることができるタグです。
「Sql」の後に「.リージョン名」が記載できて、特定のリージョンだけを指定することもできます。

以下の送信規則を適用すると、「基本インターネット向け(Azureサービス含む)のOutboundは拒否だけど、SQL DB(西日本)だけは繋げてよいよ」といったルールになります。

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowSqlOutbound|4080|VirtualNetwork|*|Sql.japanwest|*|*|Allow|
|DenyInternetOutbound|4090|VirtualNetwork|*|INTERNET|*|*|DENY|

2018年に入ってGAした「[仮想ネットワーク サービスのエンドポイント](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-service-endpoints-overview?WT.mc_id=AZ-MVP-5002499)」は、StorageとSQL DBを特定のVNET内Subnetからのみ限定的に接続するサービスですが、こちらのサービスタグを併用して必要に応じて送信規則を空けておく必要があります。

##Application Security Group(ASG)【New!】
ASGも2018年3月末にGAしたのですが、本記事執筆時点では特にAzureポータル上から簡単に使える感じではなさそうです。
以前書いたプレビューの時の記事をとりあえず参考にしてください。

[Azure Application Security Group(ASG)がPublic Previewになったので使ってみた](https://qiita.com/yotan/items/9fc45d759aecdf0b5967)

こちらの公式ドキュメントもご参考にどうぞ。

[Filter network traffic with a network security group using PowerShell](https://docs.microsoft.com/ja-jp/azure/virtual-network/tutorial-filter-network-traffic?WT.mc_id=AZ-MVP-5002499)

##ソースIPアドレスと宛先IPアドレスの複数記述【New!】
2018年1月から、NSGのソースIPアドレスと宛先IPアドレスが1ルール内に複数個書くことができるようになりました。
複数の特定IPアドレスからのsshを許可する場合、今までは複数行受信規則を書かなければいけなかったのですが、これが1ルールで記述可能になっています。

【今まで】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowSshInbound1|4080|xxx.xxx.xxx.1/32|*|*|22|TCP|Allow|
|AllowSshInbound2|4081|xxx.xxx.yyy.2/32|*|*|22|TCP|Allow|
|AllowSshInbound3|4082|xxx.xxx.zzz.3/32|*|*|22|TCP|Allow|

【これからは！】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowSshInbound|4080|xxx.xxx.xxx.1/32, xxx.xxx.yyy.2/32, xxx.xxx.zzz.3/32|*|*|22|TCP|Allow|

1つのルールに記述可能なIPアドレス(CIDR)は標準で2,000個、MSサポートにお願いすると最大4,000個記述可能になります。
(参考:[ネットワークの制限 - Azure Resource Manager](https://docs.microsoft.com/ja-jp/azure/azure-subscription-service-limits#azure-resource-manager-virtual-networking-limits?WT.mc_id=AZ-MVP-5002499))



##無条件に通るパケット
実はNSGの送信規則でInternet側を絞っても、例外的に通過するルールが存在します。
MS宇田さんの「[IP アドレス 168.63.129.16 について](https://blogs.technet.microsoft.com/jpaztech/2016/04/01/about-168-63-129-16/)」を参照して頂きたいのですが、仮想マシンの起動や名前解決に欠かせないDNS、DHCPについては168.63.129.16宛に常に空いているようです。

##ロードバランサーに適用する場合
ロードバランサの種類としてはPublic IP Addressを持った"External Load Balancer"と仮想ネットワーク内からのアクセスのみ許容する"Internal Load Balancer"の二種類がありますね。

ロードバランサー経由の受信規則をNSGに書く場合、「発信元」「宛先」に何を指定するのかを説明します。

下図のようにVM-1(10.0.0.4)からVM-2(10.0.1.5)とVM-3(10.0.1.6)のTCP/443にだけアクセスさせたい場合。

![image](https://qiita-image-store.s3.amazonaws.com/0/137436/b5340b2c-3105-a39b-683e-9bdb623944b0.png)

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowLbHttpsInbound1|200|10.0.0.4/32|*|10.0.1.5/32|443|TCP|ALLOW|
|AllowLbHttpsInbound2|201|10.0.0.4/32|*|10.0.1.6/32|443|TCP|ALLOW|

このように、個々の仮想マシン毎にVM-1(10.0.0.4)からのアクセス許可を追加します。
(ロードバランサー(10.0.1.4)は受信規則の中に登場しません)

Public IP Addressを持つExternal Load Balancerでも本ルールは変わりません。

(ひとりごと)VM-1からLBを経由しないでダイレクトにVM-2、VM-3に繋げられてしまうけど、これの抑止はどうするんだろう

##ping(icmp)について
既定の受信ルールが有効の場合はVirtualNetwork→VirtualNetworkのトラヒックは全てのプロトコルが許可されているため、ping(icmp)は通ります。
一旦受信規則でVirtualNetworkからの通信を制限した場合ですが、NSGのプロトコルの指定は「*」「TCP」「UDP」のみ指定可能であるためダイレクトに「ICMP」とか指定ができません。
もし、PINGだけを通したくなったら以下をお試しください。

【受信ルールでVirtualNetwork間の通信はICMPのみ】

|Name|優先順位|発信元 IP|発信元ポート|宛先 IP|宛先ポート|プロトコル|Access (アクセス)|
|:---|---:|:---|---:|:---|---:|:---|:---|
|AllowICMPInbound1|4090|VirtualNetwork|*|VirtualNetwork|*|TCP|DENY|
|AllowICMPInbound2|4091|VirtualNetwork|*|VirtualNetwork|*|UDP|DENY|
|AllowICMPInbound3|4092|VirtualNetwork|*|VirtualNetwork|*|*|ALLOW|

TCPとUDPは拒否するけど、その他は許可ということで、ICMPだけが通過するようになります。

#おわりに
こちらでまとめた内容は既に既知の情報ではありますが、NSGの受信、送信ルールを設計していく上で意外と引っかかりがちな部分をピックアップしました。

本内容は執筆時点(2018/04)の情報となっており、その後Azureの機能改善、機能アップが行われ次第不定期に更新予定です。