「パブリックエンドポイント」という名の地雷原:Azure Private Linkで築く鉄壁のインフラ防衛術
やあ、エンジニア諸君。今日もクラウドのコンソール画面と格闘しているか?
最近、インシデント対応の現場で耳にする「うちはAzureのPaaSを使っているから大丈夫」という言葉。これを聞くと、私はいつも背筋が凍る思いがする。Azure StorageやAzure SQL Databaseを、何の疑いもなくパブリックエンドポイント経由で繋いでいないか? それは、「鍵をかけずに玄関を開けっ放しにして、監視カメラだけで泥棒を防ごうとしている」のと同じだ。
今日は、Azure Private Linkを使って、その「開けっ放しの玄関」を壁で埋め、ネットワークの迷宮の中にリソースを隠蔽する技術について、実務的な話をしよう。
なぜ「パブリックアクセス遮断」が必須なのか
攻撃者は、常に公開されているIPアドレスの範囲をスキャンしている。AzureのPaaSリソースがパブリックエンドポイントを公開しているということは、インターネット上の誰でも(攻撃者も)、そのリソースの認証ゲートウェイにアクセスできることを意味する。
例えば、ストレージアカウントが公開されていれば、そこに対して総当たり攻撃や設定ミスを突いた認証回避を試みることができる。これを防ぐ唯一の解が、Private Endpoint(プライベートエンドポイント)によるネットワークの閉域化だ。
Private Endpoint導入の勘所:ここを外すと骨折り損
Private Endpointを構成する際、多くのエンジニアが犯す最大のミスが、「設定しただけで満足してしまうこと」だ。以下の2点を徹底しなければ、守りはザルになる。
1. 「パブリックネットワークアクセス」の無効化: これを「無効」にしない限り、裏口(パブリックエンドポイント)は開いたままだ。
2. DNS解決の制御: Private Endpointは仮想ネットワーク(VNet)内のプライベートIPを持つ。クライアントがパブリックDNSを引いてしまったら、通信はパブリック側を通ってしまう。Azure Private DNS Zoneを正しく連携させることが、この防御の心臓部だ。
実装の指針:Terraformによるセキュアな構成例
インフラをコードで管理するのは現代の必須要件だ。以下に、Azure Storageアカウントに対してパブリックアクセスを遮断し、Private Endpointを適用するTerraformの基本骨子を示す。
# ストレージアカウントのパブリックアクセスを完全遮断
resource "azurerm_storage_account" "secure_storage" {
name = "proddata001"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
# 【最重要】パブリックネットワークアクセスを無効化
public_network_access_enabled = false
}
# プライベートエンドポイントの構成
resource "azurerm_private_endpoint" "pe" {
name = "storage-pe"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
subnet_id = azurerm_subnet.pe_subnet.id
private_service_connection {
name = "storage-connection"
private_connection_resource_id = azurerm_storage_account.secure_storage.id
subresource_names = ["blob"] # Blobストレージをプライベート化
is_manual_connection = false
}
}
アプリケーション側からの接続チェック(Python例)
プライベートエンドポイントが正しく機能しているか確認するには、VNet内のVMやコンテナから、対象リソースのFQDNに対してDNSクエリを投げてみるのが一番だ。
以下のコードは、SDKを使用してストレージに接続する際、パブリックIPではなくプライベートIPへルーティングされていることを確認するためのスニペットだ。
from azure.storage.blob import BlobServiceClient
import socket
# ストレージアカウントのエンドポイントをFQDNで指定
account_url = "https://proddata001.blob.core.windows.net"
def check_connection(url):
# ホスト名を抜き出し、解決されるIPアドレスを確認
hostname = url.replace("https://", "")
try:
ip = socket.gethostbyname(hostname)
print(f"解決されたIP: {ip}")
# VNetのプライベートIP範囲(例: 10.0.x.x)であることを確認すること
except Exception as e:
print(f"エラー: {e}")
# 接続テスト
check_connection(account_url)
運用で絶対に忘れてはならないこと
Private Linkを導入した後は、「ネットワークセキュリティグループ(NSG)」での出口制御を忘れてはならない。
「プライベートだから安全だ」と油断して、サブネットからの全方位通信(0.0.0.0/0)を許可していないだろうか? 悪意のあるコードが万が一コンテナ内で実行された場合、C&Cサーバーへの通信を許してしまうことになる。
守りのためのチェックリスト
- [ ] すべてのPaaSリソースで
public_network_access_enabled = falseを確認したか? - [ ] Azure Private DNS Zoneは、対象のVNetに正しくリンクされているか?
- [ ] NSGで、許可した通信以外をすべて拒否する「暗黙の拒否」ルールが機能しているか?
- [ ] Azure Monitorで、プライベートエンドポイント経由のアクセスログが記録されているか?
セキュリティは「設定して終わり」の静的なものではない。攻撃者は常にあなたの設定の隙間を探している。だが、今回紹介したような閉域化のアーキテクチャを徹底すれば、攻撃者が付け込む「インターネット経由の入り口」は物理的に消滅する。
泥臭い作業かもしれないが、この「見えない壁」を築くことこそが、エンジニアとしての真の矜持だ。現場のインフラを、誰にも侵されない聖域にしよう。健闘を祈る。
コメント