概要
仮想環境において、ゲストの通信を必要最低限の範囲に制限しておきたい場合があります。特に、ネットワーク側のファイアウォール設定だけでは都合が悪い場合、ホスト側で制限をかけられると便利です。
本記事は、Hyper-V環境でAdd-VMNetworkAdapterExtendedAcl
コマンドレット(拡張ACL)によりゲストの通信を制限する方法についてまとめたものです。Windows 10上のHyper-Vで確認したものですが、Windows Serverでも同様です。
この拡張ACLは便利ですが、Microsoftのページを含む公開情報では欲しい説明を探しづらく、仕様把握に手間取りました。事前に知っておきたかったと思うポイントを整理しておきます。
本記事の目的
目次
- 概要
- 基本
- 詳細
- 参考URL等
基本
拡張ACLの概要とユースケース
概要
- Hyper-Vホスト上のWindowsファイアウォールは、ゲストの通信に対しては適用されません。そのため、ゲストの通信をホスト側で制限したい場合には
Add-VMNetworkAdapterExtendedAcl
のような別の設定が必要です。 Add-VMNetworkAdapterExtendedAcl
は、Hyper-V上の各仮想ネットワークアダプタに対し、アクセス制御ルール(ACL)を作成するコマンドレットです。Add-VMNetworkAdapterAcl
のACLと対比する場合等、"拡張ACL"と呼ぶこともあります。- IPアドレスやポート番号によるACLを作成可能です。
- 簡易的な実装ではあるものの、各ゲストOS内の設定でなくホスト側で集中的にアクセス制御できる利点があります。
- 拡張ACLは、初期状態では未定義であり、ゲストの通信は全て許可されます。
- 仮想スイッチの種類に関わらず拡張ACLを使用可能です。補足事項は後述します。
- NAT(WinNAT)の有無に関わらず、拡張ACLを使用可能です。
- 仮想マシンごと(仮想ネットワークアダプタごと)に定義が必要です。便利な一括設定の方法については後述します。
- Hyper-Vホスト上のWindowsファイアウォールは、ゲストの通信に対しては適用されません。そのため、ゲストの通信をホスト側で制限したい場合には
ユースケース
以下のように、トラブル防止等の理由により、Add-VMNetworkAdapterExtendedAcl
を使用できます。- そのHyper-V環境をテストや検証のために使用する際、Hyper-Vの外にある本番環境に影響を与えないようにしたい
- ゲストから特定のネットワーク以外に対する通信を遮断したい
- あるいは、逆に特定のネットワークに対する通信を遮断したい
- ゲストOSのローカルファイアウォール設定だけでなく、追加の対策としてホスト側でも通信を制限しておきたい
- ネットワーク側のファイアウォール設定では都合がよくない
- セキュリティポリシー上、ゲストに許可する通信を必要最低限の範囲に制限しておきたい
- そのHyper-V環境をテストや検証のために使用する際、Hyper-Vの外にある本番環境に影響を与えないようにしたい
設定例
例として、以下の図に示すアクセス制御を実現するACLを設定してみます。
例1 (基本的な定義)
Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -Weight 1 …(1) Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Inbound -VMName "VM1" -Weight 1 …(1') Add-VMNetworkAdapterExtendedAcl -Action Deny -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.0/16 -Weight 10 …(2) Add-VMNetworkAdapterExtendedAcl -Action Deny -Direction Inbound -VMName "VM1" -RemoteIPAddress 172.16.0.0/16 -Weight 10 …(2') Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 192.168.0.0/24 -Weight 11 …(3) Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Inbound -VMName "VM1" -RemoteIPAddress 192.168.0.0/24 -Weight 11 …(3') Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.1/32 -RemotePort 53 -Protocol TCP -Weight 21 -Stateful $True …(4) Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.1/32 -RemotePort 53 -Protocol UDP -Weight 22 -Stateful $True …(4') Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.2/32 -RemotePort 443 -Protocol TCP -Weight 23 -Stateful $True …(5)
(1)、(1') デフォルトアクション/社外ネットワーク(インターネット)との通信
- 他のどのACLにもマッチしない場合に適用するACLです。IPアドレスやポート番号の指定はありません。
-Weight 1
は最後に評価されるACLという意味です。- 今回の例ではインターネット向けの通信を許可するため、
-Action Allow
としています。拒否したい場合は-Action Deny
を指定します。 - 例なので明示的に定義していますが、この許可ACLは定義しなくても動作に変わりはありません(デフォルトでマッチするACLが無ければ許可されるため)。
- デフォルトで拒否したい場合には、
-Action Deny
のACL定義が必要です。
- デフォルトで拒否したい場合には、
- なお今回の例では、仮想マシンは社外に公開するサービスが無く、社外から仮想マシンに対する通信は社外向けファイアウォールにより遮断されることを前提にしています。
(2)、(2') 社内ネットワーク(172.16.0.0/16)との通信
(3)、(3') Hyper-V内ネットワーク(192.168.0.0/24)との通信
(4)、(4') DNSサーバ(172.16.0.1)との通信
(5) Webサーバ(172.16.0.2)との通信
例2 (省略した定義)
次に、例1からできるだけACLを省略した場合の例を記載します。
以下のいずれかに該当する場合、Hyper-Vの外のネットワークとの通信に関する-Direction Inbound
の方向の拒否ACLは不要です。
省略しても動作に変わりのないACLを省略、さらに-Direction Inbound
の方向の拒否ACLを省略すると、以下のようになります。
Add-VMNetworkAdapterExtendedAcl -Action Deny -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.0/16 -Weight 10 …(2) Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.1/32 -RemotePort 53 -Protocol TCP -Weight 21 -Stateful $True …(4) Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.1/32 -RemotePort 53 -Protocol UDP -Weight 22 -Stateful $True …(4') Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "VM1" -RemoteIPAddress 172.16.0.2/32 -RemotePort 443 -Protocol TCP -Weight 23 -Stateful $True …(5)
かなりシンプルになります。仮想スイッチの構成にもよりますが、図に記載のアクセス制御を実現するだけであれば、この定義で十分です。
(2')のInbound方向のACLを定義していないので、(4)~(5)の-Stateful $True
は省略可です。
詳細
仮想スイッチの種類ごとの拡張ACLの適用
どの仮想スイッチを使用していても拡張ACLは適用できますが、考慮するポイントが少しずつ違うので整理しておきます。
Default Switch
外部ネットワーク(External)
内部ネットワーク(Internal)
プライベート(Private)
なお、New-VMSwitch
により個別に作成した仮想スイッチでも使用可能です。
コマンドレット説明
Add-VMNetworkAdapterExtendedAcl
以下のように実行できるコマンドです。
Add-VMNetworkAdapterExtendedAcl -Action Allow -Direction Outbound -VMName "仮想マシン名" -RemoteIPAddress x.x.x.x/x -RemotePort x -Protocol x -Weight x -Stateful $True
主要なオプションは以下です。
-Action
(必須)- パケットが条件にマッチした際のアクションです。
- 指定可能な値は、Allow(許可)、Deny(拒否)のいずれかです。
-Direction
(必須)-Weight
(必須)-LocalIPAddress
、-RemoteIPAddress
(任意)-LocalPort
、-RemotePort
(任意)-Protocol
(任意)-Stateful
(任意)- ステートフルなアクセス制御とするかどうかを指定します。
- 指定可能な値は、Bool値です(PowerShellの"$True"や"$False"を指定可能)。
-Stateful
を指定する際には、-Protocol
の指定が必須のようです。詳細は後述します。- 省略時はステートフルなアクセス制御を行いません。
-IdleSessionTimeout
(任意)- 無通信タイムアウトを秒単位で指定します。
- 指定可能な値は、1以上の整数です(型はINT32のようです)。
- 省略時は255が設定されるようです(
Get-VMNetworkAdapterExtendedAcl
の結果より)。
Get-VMNetworkAdapterExtendedAcl
以下のように実行できるコマンドです。
`Get-VMNetworkAdapterExtendedAcl -VMName "仮想マシン名"
定義済みのACLの内容を出力します。
以下、出力例です(一部省略してあります)。
デフォルトで仮想マシン(仮想ネットワークアダプタ:ParentAdapter
)ごとにソートはされるようです。
Direction : Outbound Action : Allow LocalIPAddress : ANY RemoteIPAddress : 172.16.0.1/32 LocalPort : ANY RemotePort : 53 Protocol : TCP Weight : 21 Stateful : True IdleSessionTimeout : 255 IsolationID : 0 ParentAdapter : VMNetworkAdapter (Name = 'ネットワーク アダプター', VMName = 'VM1') [VMId = 'xxxxx-xxxxx-xxxxx-xxxxx-xxxxx'] IsTemplate : False CimSession : CimSession: . ComputerName : DESKTOP-XXXXXX IsDeleted : False … Direction : Outbound Action : Allow LocalIPAddress : ANY RemoteIPAddress : 192.168.0.0/24 LocalPort : ANY RemotePort : ANY Protocol : ANY Weight : 11 Stateful : False IdleSessionTimeout : 0 IsolationID : 0 ParentAdapter : VMNetworkAdapter (Name = 'ネットワーク アダプター', VMName = 'VM1') [VMId = 'xxxxx-xxxxx-xxxxx-xxxxx-xxxxx'] IsTemplate : False CimSession : CimSession: . ComputerName : DESKTOP-XXXXXX IsDeleted : False … Direction : Inbound Action : Allow LocalIPAddress : ANY RemoteIPAddress : 192.168.0.0/24 LocalPort : ANY RemotePort : ANY Protocol : ANY Weight : 11 Stateful : False IdleSessionTimeout : 0 IsolationID : 0 ParentAdapter : VMNetworkAdapter (Name = 'ネットワーク アダプター', VMName = 'VM1') [VMId = 'xxxxx-xxxxx-xxxxx-xxxxx-xxxxx'] IsTemplate : False CimSession : CimSession: . ComputerName : DESKTOP-XXXXXX IsDeleted : False
Remove-VMNetworkAdapterExtendedAcl
以下のように実行できるコマンドです。
特定のACLを1つ削除する場合は、-Direction
と-Weight
を指定して実行します。
Remove-VMNetworkAdapterExtendedAcl -VMName "VM1" -Direction InBound -Weight 23
その仮想マシンに紐づくACLを全て削除する場合は、ACLをオブジェクトで渡して実行します。
Get-VMNetworkAdapterExtendedAcl -VMName "VM1" | Remove-VMNetworkAdapterExtendedAcl
注意点として、ACLの削除が成功しても失敗しても何も出力されません。このコマンドを実行する前後でGet-VMNetworkAdapterExtendedAcl
の結果をテキストに保存しておき、差分チェックすると安心です。
-Stateful $True指定時に-Protocol指定が必須な理由と対処
Microsoftのページに記載は見当たりませんが、Add-VMNetworkAdapterExtendedAcl
では、-Stateful
を指定する際、-Protocol
の指定が必須のようです。
その理由として、-Protocol
の省略時は、全てのIPベースのプロトコルが対象となるようですが、このときTCP、UDP以外の-Stateful
に対応できないプロトコルも対象になってしまうためと思われます。例えば拡張ACLでは、ICMPに対して-Stateful
を指定できないので、行きと戻り両方のパケットにACLを適用する場合には、InboundとOutboundの両方の定義が必要です。
この動作により、DNS等、TCPとUDPで同じポート番号を使用するプロトコルにステートフルなACLを適用する場合に配慮が必要となります。
具体的には、-Protocol TCP
のACLと、-Protocol UDP
のACLをそれぞれ定義することになります。
これは、-Protocol
を省略すると-Stateful
指定はできないという点、また-Protocol
でTCPとUDPの2つを指定することができないという点から、結果的にACLを複数定義する必要性があるということです。
拡張ACLは、一般的なファイアウォールに比べると簡易的な実装ではあるので、このあたりは注意して使うしかないでしょう。
Add-VMNetworkAdapterAclとAdd-VMNetworkAdapterExtendedAclの違い
Add-VMNetworkAdapterAcl
(ACLの追加)とAdd-VMNetworkAdapterExtendedAcl
(拡張ACLの追加)は、異なるコマンドレットです。
異なるACLを扱います。併用すべきではないでしょう。混在させた場合の動作は不明です。
後者の拡張ACLが新しい機能です。Windows Server 2012 R2から使用できるようになったようです。
前者のACLは、ポート番号を指定したACLやステートフルなACLを作成できません。ACL自体に優先度指定は無く、最長マッチしたACLが優先的に適用されます。
後者の拡張ACLの方が多機能です。ポート番号を指定したACLやステートフルなACLを作成できます。ACLごとに優先度指定(-Weight
)が可能です。ただしMACアドレスベースのアクセス制御に関しては前者のACLにのみ備わっているようです。IPベースのACLと言えるでしょう。
また参考までに、構成によってはSCVMM経由で拡張ACLの管理ができるようです(portACL)。
拡張ACLの一括設定
全仮想マシンを対象に拡張ACLを一括設定
拡張ACLは全仮想マシンに適用されるグローバルな定義を設定できません。
代替手段として、全仮想マシンの仮想ネットワークアダプタを対象に、一括設定する方法があります。
Get-VMNetworkAdapter -vmname "*" | Get-VMNetworkAdapterExtendedAcl > "C:\any\path\before.txt" Get-VMNetworkAdapter -vmname "*" | Add-VMNetworkAdapterExtendedAcl … Get-VMNetworkAdapter -vmname "*" | Add-VMNetworkAdapterExtendedAcl … …(定義したい分、繰り返す)… Get-VMNetworkAdapter -vmname "*" | Get-VMNetworkAdapterExtendedAcl > "C:\any\path\after.txt"
全ての仮想マシン("*")を対象に
Get-VMNetworkAdapter
コマンドレットを実行して得られる仮想ネットワークアダプタのオブジェクトを、Add-VMNetworkAdapterExtendedAcl
等にパイプで渡すと、一括で処理してくれます。例えば、仮想マシンを追加した際に実行するバッチとして準備しておくことで、全ての仮想マシンに対して一律同じ設定を維持できます。
なお、繰り返し全ての仮想マシンを対象に
Add-VMNetworkAdapterExtendedAcl
を実行すると、追加しようとするACLと同じACL(同じ-Direction
と-Weight
)が既に存在することになります。この場合、そのACLの追加に関してはAdd-VMNetworkAdapterExtendedAcl
がエラーとなりますので、重複した内容のACLが登録されることはありません。ACLが未設定の仮想ネットワークアダプタに対してのみ追加されます。
言い換えると、Add-VMNetworkAdapterExtendedAcl
はACLの上書きをしません。
全仮想マシンを対象に拡張ACLを一括削除
同様に、全仮想マシンの仮想ネットワークアダプタを対象に、既に定義されている拡張ACLを一括削除する方法です。
Get-VMNetworkAdapter -vmname "*" | Get-VMNetworkAdapterExtendedAcl | Remove-VMNetworkAdapterExtendedAcl もしくは Get-VMNetworkAdapterExtendedAcl -VMName "*" | Remove-VMNetworkAdapterExtendedAcl
(参考)ホストWindows側の仮想ネットワークアダプタに対するACL設定
あまりユースケースが無さそうですが、ホストWindows側の仮想ネットワークアダプタに対してACL設定することもできるようです。
-ManagementOS
オプションを使用します。このサイトに例が載っています。拡張ACLでなくVMNetworkAdapterACLの方ですが、拡張ACLでも-ManagementOS
オプションは使用可能です。
以下、Add-VMNetworkAdapterAcl
で試した際のメモです。
- ホストWindowsの仮想ネットワークアダプタに対するACLを設定すると、そのIPインタフェースを使用した通信を制限できます。
- そのACLは、ホストWindowsの仮想ネットワークアダプタをゲートウェイとして使用する仮想マシンが送受信するパケットに対しては適用されないようです。これはWinNAT環境で確認した結果ですが、WinNATの有無は関係無い気がします。
- また、その結果から、ACLはパケットの送信元IPアドレスと宛先IPアドレスに対してのみ適用されるのかと思ったら、一方でホストWindowsのゲートウェイのIPアドレスを指定した拒否ACLを定義してみると、それはゲストの通信(パケットの送信元IPアドレスと宛先IPアドレスはHyper-Vの外部)に適用されてゲートウェイを越えられない、といった動作でした。