
Azure NSGの許可規則を削除しても、接続中のSSHは切れない?20分つないだまま確かめた
どうも!オペ部の西村祐二です!
Azureのネットワークセキュリティグループ(NSG)で、VMへのSSHを許可する規則を削除したとします。それでも接続中のターミナルで操作が続けられたら、「設定変更が反映されていない」と考えてよいのでしょうか。
実機で確かめたところ、許可規則を削除した後も、接続中のSSHセッションは切れませんでした。一方、削除後に試した新しいSSH接続はタイムアウトしました。新しい接続が拒否されたので、規則の削除は反映されています。それでも既存の接続は止まりませんでした。
この記事では、今回の結果をもとに、規則を変更した後に「新しい接続を止められた」と「既存の接続も終わった」を取り違えずに確認する方法を整理します。既存の接続を再評価する flushConnection も試しましたが、2026年11月10日時点の検証用サブスクリプションでは有効にできませんでした。その結果も載せます。Azure CLI 2.91.0と東日本リージョンのVMで試した結果です。
SSHだけを許可するNSGと、5秒ごとに出力するSSHセッションを用意した
- VM: 東日本リージョン、Standard_F1als_v7、Ubuntu 24.04 LTS、パブリックIPあり。従量課金のサブスクリプション
- NSG
nsg-sshを、VMの作成時にネットワークインターフェイス(NIC)に関連付けた。サブネットにはNSGを付けていない nsg-sshには、手元のグローバルIPアドレス(/32)からのTCP 22番だけを許可する規則allow-ssh-from-me(優先度100)を作成。手元の通信はVPN経由なので、ここで指定したのは、Azureから見える出口のIPアドレス- 手元はmacOSのOpenSSH
az network nsg create --resource-group <resource-group> --name nsg-ssh
az network nsg rule create --resource-group <resource-group> --nsg-name nsg-ssh --name allow-ssh-from-me \
--priority 100 --direction Inbound --access Allow --protocol Tcp \
--source-address-prefixes <手元のグローバルIPアドレス>/32 --destination-port-ranges 22
# --nsg を付けないと、SSHを全送信元に開けたNSGが別に作られてNICに付く
az vm create --resource-group <resource-group> --name vm-nsg \
--image Canonical:ubuntu-24_04-lts:server:latest --size Standard_F1als_v7 \
--admin-username azureuser --ssh-key-values <公開鍵のファイル> \
--nsg nsg-ssh --public-ip-sku Standard --storage-sku Standard_LRS
接続中のセッションが生きているかを見分けるため、VMの時刻を5秒ごとに出力し続けるSSHセッションを張りました。
ssh azureuser@<VMのパブリックIP> 'for i in $(seq 1 240); do echo "vm $(date +%T) tick $i"; sleep 5; done'
240回(約20分)出力したらループが終わり、セッションが閉じます。出力が途切れれば、その時点で接続が切れたと分かります。
許可規則を削除しても、接続中のSSHは出力を続けた
セッションを張ってから20秒後に、許可規則を削除しました。続けて、別のターミナルから新しいSSH接続を試し、IP flow verifyで規則の判定も確かめました。
az network nsg rule delete --resource-group <resource-group> --nsg-name nsg-ssh --name allow-ssh-from-me
手元の操作記録とセッションの出力を並べると、次のとおりです。左端の時刻はJSTです。vm で始まる行はVMの時刻で、タイムゾーンがUTCのためJSTより9時間遅れて表示されます。
00:34:01 接続中のSSHセッションを開始
00:34:21 許可規則 allow-ssh-from-me の削除を開始
vm 15:34:23 tick 5
00:34:25 削除が完了
vm 15:34:28 tick 6
00:34:35 別のターミナルで新しいSSH接続を開始
00:34:55 新しいSSH接続がタイムアウト
vm 15:35:23 tick 17
00:35:35 IP flow verify を実行(00:35:51 に結果。判定は Deny)
vm 15:35:48 tick 22
...
vm 15:53:58 tick 240 <- ループが終わり、セッションが閉じた
削除の前後で出力は途切れず、tick 1からtick 240まで240行すべてが5秒間隔で届きました。新しい接続がタイムアウトしている間も、IP flow verifyが拒否と判定している間も、既存のセッションの出力は続いています。削除からループが終わるまでの約19分半、出力は途切れずに届き続けました。
削除後、ポータルの「受信セキュリティ規則」には、デフォルトの規則3つだけが残っています(赤枠は追記)。手元からのSSHを許可する規則はもうありません。

新しいSSH接続はタイムアウトし、規則の判定も拒否だった
新しいSSH接続は、待ち時間を20秒に指定して試しました。
ssh -o ConnectTimeout=20 azureuser@<VMのパブリックIP>
ssh: connect to host <VMのパブリックIP> port 22: Operation timed out
20秒は自分で指定した待ち時間です。指定しなければ、OSのデフォルトの時間まで待ってからタイムアウトします。
IP flow verifyでは、手元のIPアドレスからVMの22番ポートへの、新しく届く受信の通信を指定しました。IP flow verifyは、そのリージョンでNetwork Watcherが有効になっていることが前提です(今回は自動で作られました)。
az network watcher test-ip-flow --resource-group <resource-group> --vm vm-nsg \
--direction Inbound --protocol TCP \
--local <VMのプライベートIP>:22 --remote <手元のグローバルIPアドレス>:50000
{
"access": "Deny",
"ruleName": "defaultSecurityRules/DenyAllInBound"
}
IP flow verifyの判定は、デフォルトの DenyAllInBound による拒否でした。IP flow verifyは、指定したパケットが設定済みの規則で許可されるか拒否されるかを返すものです。今回もDenyと判定されている間にセッションが続いていたとおり、既存の接続が残っているかは、この結果には表れません。
NSGは接続の状態を保持している
既存の接続が切れなかったのは、NSGが接続ごとの状態を持っているためです。公式ドキュメントのSecurity rulesの節によると、既存の接続に対してフローレコードが作られ、その接続状態に基づいて通信が許可・拒否されます。これが「ステートフル」という性質です。
同じ節には、今回の実験とほぼ同じ例も書かれています。
- 接続を許可していた規則を削除しても、既存の接続は中断されない
- NSGの規則の追加・変更・削除が影響するのは、新しい接続だけ
- VMへのSSHセッション中にSSHを許可する規則を削除しても、そのセッションは使える状態のまま続き、削除後に新しいSSH接続を試すとブロックされる
次の図は自作した概念図で、公式の構成図ではありません。規則の削除前から張られている接続と、削除後に始めた接続を分けています。

AWSのセキュリティグループも、応答の通信を接続の状態に基づいて扱う点はNSGと似ています。ただし、この例えは「ステートフル」という考え方に限ったもので、規則を変更したときの挙動まで同じという意味ではありません。
公式ドキュメントのFlow timeoutの節には、フローレコードが有効な時間(フローのタイムアウト)を設定できるとも書かれています。今回試したのは5秒ごとにデータを流し続けるセッションだけです。通信のない時間が長いSSHセッションを開いたまま置いた場合に、規則の削除後もセッションが残るのか、フローの期限切れで切れるのかは、この記事の結果からは判断できません。
flushConnection: APIにはあるが、今回は有効にできなかった
「NSGは既存の接続を一切止められない」とまでは言えません。NSGのREST APIリファレンスには properties.flushConnection が定義されています。説明によると、このプロパティを有効にすると、有効にした時点と規則を更新した時点で、NSGの接続のフローが再評価されます。
そこで、既存の接続が切れるかを試すため、nsg-ssh で flushConnection を有効にしようとしました。
az network nsg update --resource-group <resource-group> --name nsg-ssh --set flushConnection=true
(FlushingNetworkSecurityGroupConnectionIsNotEnabled) Network Security Group Connection Flushing is not enabled yet for japaneast region. Please set networkSecurityGroup.FlushConnection property to false.
東日本リージョンでは、この機能がまだ有効になっていないというエラーでした。ほかのリージョンも確かめるため、flushConnection を有効にしたNSG(VMなし)を、REST API(api-version 2024-05-01)で東日本を含む8リージョンに作成しようとしました。
az rest --method put \
--url "/subscriptions/{subscriptionId}/resourceGroups/<resource-group>/providers/Microsoft.Network/networkSecurityGroups/flush-<region>?api-version=2024-05-01" \
--body '{"location": "<region>", "properties": {"flushConnection": true}}'
結果は、japaneast、japanwest、eastus、westus2、westcentralus、centralus、northeurope、southeastasiaのすべてで、同じ FlushingNetworkSecurityGroupConnectionIsNotEnabled による拒否でした。
あわせて、このサブスクリプションのプレビュー機能を確認すると、Microsoft.Network/AllowNsgFlushConnection は未登録でした。登録を申請しましたが、2026年11月10日1時32分(JST)時点では承認待ち(Pending)のままです。
az feature register --namespace Microsoft.Network --name AllowNsgFlushConnection
flushConnection を試したのは、このプレビュー機能が未登録の状態のときです。有効にできなかったため、有効にしたときに既存の接続が実際に切れるかも確かめられていません。プレビュー機能の登録が条件なのか、リージョンへの展開を待つ必要があるのかも切り分けていません。
新しい接続と既存の接続を分けて確認する
SSHの許可範囲を狭める変更では、「新しい接続」と「既存の接続」を別々に確認すると、結果を取り違えにくくなります。
| 確認する対象 | 確認の方法 | 今回の結果 | それだけでは分からないこと |
|---|---|---|---|
| 新しい接続 | 規則の判定(IP flow verify) | Deny(DenyAllInBound) |
すでに張られている接続が残っているか |
| 新しい接続 | 新しく張ったSSH接続 | タイムアウト | すでに張られている接続が残っているか |
| 既存の接続 | 変更前からあるSSHセッションの状態 | 約19分半、出力が途切れなかった(5秒ごとに通信し続けたセッション) | 新しい接続が拒否されるか |
規則の判定だけを見て「SSHは止まった」と判断すると、接続中のセッションが残っていることに気付けません。逆に、接続中のセッションが続いていることだけを見て「規則が反映されていない」と判断するのも誤りです。
既存のセッションをすぐに終わらせることが要件なら、NSGの規則だけで完了とせず、対象のサブスクリプションとリージョンで flushConnection を使えるかを確かめるか、VM側でSSHのセッションを終了させる手順を別に用意しておくのが良さそうです。VM上で接続中のSSHセッションを一覧したり終了させたりする操作は、今回は試していないため、どの手順なら既存の接続を確実に切れるかはこの記事では示せません。
まとめ
Azure NSGで許可規則を削除しても、接続中のSSHセッションは切れませんでした。実機では、ループが終わってセッションが閉じるまでの約19分半、削除後も通信が続きました。一方、削除後に試した新しい接続はタイムアウトしました。公式ドキュメントにも、flushConnection を使わない通常の設定では、規則の追加・変更・削除が影響するのは新しい接続だけと書かれています。
規則を変更した後は、「新しい接続が拒否されるか」と「既存の接続が終わったか」を別々の完了条件として確認する必要があります。既存の接続を再評価する flushConnection はAPIに定義されていますが、2026年11月10日時点の検証用サブスクリプションでは、試した8リージョンのどこでも有効にできませんでした。






