1. はじめに
1-1 ご挨拶
初めまして、井村と申します。
Azure Firewallを調査する機会があり公式ドキュメントを読んだり、手動でAzure Firewallを作成したりしていました。公式ドキュメントには手動、コマンド、IaC等の手順でデプロイ方法があります。本記事は自分の理解を深めるため、Bicepを用いて構築、簡単な検証を行います。
2. Azure Firewallとは
2-1 ファイアウォールの概要
一般的なファイアウォールの概要となります。
- ファイアウォールは、ネットワークの送受信トラフィックを 事前定義されたセキュリティ規則 に基づいて監視・制御するセキュリティ装置です。
- 主な目的は、 信頼された内部ネットワーク と 信頼されていない外部ネットワーク(例:インターネット) の間に障壁を設け、 悪意のある攻撃から保護することです。
2-2 Azure Firewall の主な機能
Azure Firewallの主な機能は以下の通りです。
1 . ステートフル ファイアウォール
- アクティブな接続の状態を追跡し、トラフィックのコンテキストに基づいて判断することができます。
2 . 高可用性と可用性ゾーン対応
- 障害や高負荷時でも安定運用している。
- 複数の可用性ゾーンにまたがる構成で、回復性と信頼性を実現できます。
3 . ネットワーク & アプリケーションレベルのフィルタリング
- IP、ポート、プロトコルに基づくトラフィック制御をおこないます。
- HTTP/Sなどのアプリケーション層プロトコルにも対応し、FQDN(完全修飾ドメイン名)単位でアクセス制御可能です。
4 . NAT(ネットワークアドレス変換)
- SNAT:内部のプライベートIPをAzureのパブリックIPに変換(外向き通信)。
- DNAT:外部からのパブリックIPを内部のプライベートIPに変換(内向き通信)。
5 . 脅威インテリジェンス統合
- Microsoftの脅威情報を活用し、悪意のあるIPやドメインを検出・ブロックできます。
- アラートや拒否ルールの設定が可能。
6 . ログ記録と監視
- Azure Monitor、Log Analytics、Event Hubsと連携して、トラフィックの分析や問題診断が可能。
2-3 Azure Firewall のSKU(種類)とおすすめの用途
Azure Frewallには3つの種類があります。種類によって利用可能な機能、金額が異なります。
SKU | 対象 | 主な特徴 |
---|---|---|
Basic | SMB(中小企業) | 最大 250 Mbps、基本的な保護機能 |
Standard | 一般企業 | L3〜L7のフィルタリング、脅威インテリジェンス、DNSプロキシ、Webカテゴリなど |
Premium | 機密性の高い業務(例:支払処理) | マルウェア対策、TLS検査、IDPSなどの高度な脅威保護 |
詳細な内容は公式ドキュメントから確認できます。
3. 事前準備
3-1 リソースグループの作成
公式ドキュメントに沿って Azure CLI、Visual Studio Code と Bicep 拡張機能をインスールします。
上記終了後、Azure上にリソースを作成します。
1 . Azureへログイン
Azure上にリソースを作成するためログインします。
az login
現在ログインしているAzureサブスクリプションを確認するには、以下のAzure CLIコマンドを使用します
az account show
2 . リソースグループの作成
東日本リージョンで作成します。
az group create --name rg-demo-dev --location japaneast
Azure上にリソースグループが作成されました。
4. 構築
Bicepで作成するリソースは以下の通りです。
4-1 Bicepの準備
main.bicep
@description('作成するシステム名を指定します。')
param systemName string = 'demo'
@description('リソースグループのロケーション。今回は東日本リージョン (Japan East) を指定します。')
param location string = 'japaneast'
@description('環境を指定します。dev, stg, pro のいずれかを選択してください。')
@allowed([
'dev'
'stg'
'pro'
])
param env string = 'dev'
@description('仮想ネットワークのアドレスプレフィックス(例: 10.0.0.0/16)')
param addressPrefixes array = [
'10.0.0.0/16'
]
@description('可用性ゾーンを設定します。Zone numbers e.g. 1,2,3.')
param availabilityZones array = []
@description('仮想マシンのOSバージョンを指定します。')
@allowed([
'2022-datacenter'
'2022-datacenter-azure-edition'
'2022-datacenter-azure-edition-core'
'2022-datacenter-azure-edition-core-smalldisk'
'2022-datacenter-azure-edition-smalldisk'
'2022-datacenter-core'
'2022-datacenter-core-g2'
'2022-datacenter-core-smalldisk'
'2022-datacenter-core-smalldisk-g2'
'2022-datacenter-g2'
'2022-datacenter-smalldisk'
'2022-datacenter-smalldisk-g2'
])
param OSVersion string = '2022-datacenter-azure-edition-smalldisk'
@description('仮想マシンのサイズを指定します。')
@allowed([
'Standard_B1s'
'Standard_B1ms'
'Standard_B2s'
'Standard_F1'
'Standard_B2ms'
])
param vmSize string = 'Standard_B2ms'
@description('マネージドディスクの種類を指定します。')
@allowed([
'PremiumV2_LRS'
'Premium_LRS'
'Premium_ZRS'
'StandardSSD_LRS'
'StandardSSD_ZRS'
'Standard_LRS'
'UltraSSD_LRS'
])
param storageAccountType string = 'Standard_LRS'
@description('仮想マシンのログインユーザー名を指定します。' )
param adminUsername string = 'azureuser'
@description('仮想マシンのログインパスワードを指定します。' )
@minLength(12)
@secure()
param adminPassword string // P@ssw0rd1234!
@description('ネットワークセキュリティグループの名前。')
var nsgName = 'nsg-${systemName}-${env}'
@description('仮想ネットワークの名前。')
var vnetName = 'vnet-${systemName}-${env}'
@description('作成するサブネットの一覧。各サブネットに名前とアドレスプレフィックスを指定します。')
var subnets = [
{
name: 'snet-worker'
subnetPrefix: '10.0.1.0/24'
}
{
name: 'AzureFirewallSubnet' // Azure Firewall 用の予約サブネット名
subnetPrefix: '10.0.10.0/26'
}
{
name: 'AzureBastionSubnet' // Azure Bastion 用の予約サブネット名
subnetPrefix: '10.0.11.0/26'
}
]
@description('Azure Firewall のプライベートIPアドレス。')
var nextHopIP = '10.0.10.4'
@description('Azure Firewall のプライベートIPアドレス。')
var vmPriIP = '10.0.1.4'
@description('ファイアウォールのパブリックIPアドレスの名前。')
var pipFWName = 'pip-${systemName}-${env}-fw'
@description('Azure BastionのパブリックIPアドレスの名前。')
var pipbasName = 'pip-${systemName}-${env}-bas'
@description('ファイアウォールの名前。')
var afwName = 'afw-${systemName}-${env}'
@description('ファイアウォールポリシーの名前。')
var afwpName = 'afwp-${systemName}-${env}'
@description('ルートテーブルの名前。')
var rtName = 'rt-${systemName}-${env}'
@description('カスタムルート(UDR:ユーザ定義ルート)の名前。' )
var udrName = 'udr-${systemName}-${env}'
@description('ネットワークインターフェイスの名前。')
var nicName = 'nic-${systemName}-${env}'
@description('仮想マシンの名前。')
var vmName = 'vm-${systemName}-${env}'
@description('仮想マシンのマネージドランコマンドの名前。')
var rcName = 'rc-${systemName}-${env}'
@description('Azure Bastionの名前。')
var basName = 'bas-${systemName}-${env}'
// リソース作成
// ネットワークセキュリティグループの作成
resource nsg 'Microsoft.Network/networkSecurityGroups@2024-07-01' = {
name: nsgName
location: location
properties: {
securityRules: [
{
name: 'AllowInBoundRDP'
properties: {
priority: 100
protocol: 'TCP'
access: 'Allow'
direction: 'Inbound'
sourceAddressPrefix: subnets[2].subnetPrefix
destinationAddressPrefix: subnets[0].subnetPrefix
sourcePortRange: '*'
destinationPortRange: '3389'
}
}
{
name: 'AllowVnetInBound'
properties: {
priority: 200
protocol: 'Tcp'
access: 'Allow'
direction: 'Inbound'
sourceAddressPrefix: subnets[1].subnetPrefix
destinationAddressPrefix: subnets[0].subnetPrefix
sourcePortRange: '*'
destinationPortRange: '80'
}
}
]
}
}
// ファイアウォール用パブリックアドレスアドレスの作成
resource pipFW 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: pipFWName
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
publicIPAddressVersion: 'IPv4'
}
}
// Azure Bastion用パブリックアドレスの作成
resource pipBastion 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: pipbasName
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
publicIPAddressVersion: 'IPv4'
}
}
// ルートテーブルの作成
resource rt 'Microsoft.Network/routeTables@2023-09-01' = {
name: rtName
location: location
properties: {
disableBgpRoutePropagation: false
routes: [
{
name: udrName
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: nextHopIP
}
}
]
}
}
// 仮想ネットワークの作成
resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: addressPrefixes
}
subnets: [
{
name: subnets[0].name
properties: {
addressPrefix: subnets[0].subnetPrefix
routeTable: {
id: rt.id
}
networkSecurityGroup: {
id: nsg.id
}
}
}
{
name: subnets[1].name
properties: {
addressPrefix: subnets[1].subnetPrefix
}
}
{
name: subnets[2].name
properties: {
addressPrefix: subnets[2].subnetPrefix
}
}
]
}
}
// ファイアウォールポリシーの作成
resource afwp 'Microsoft.Network/firewallPolicies@2022-01-01'= {
name: afwpName
location: location
properties: {
threatIntelMode: 'Off'
}
}
// DNATルールコレクショングループの作成
resource dnatRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
parent: afwp
name: 'DefaultDnatRuleCollectionGroup'
properties: {
priority: 100
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyNatRuleCollection'
action: {
type: 'Dnat'
}
name: 'dnat-rule-collection'
priority: 100
rules: [
{
ruleType: 'NatRule'
name: 'dnat-rule-01'
sourceAddresses: [
'*'
]
destinationAddresses: [
pipFW.properties.ipAddress
]
destinationPorts: [
'80'
]
translatedAddress: vmPriIP
translatedPort: '80'
ipProtocols: [
'TCP'
]
}
]
}
]
}
}
// アプリケーションルールコレクショングループの作成
resource applicationRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
parent: afwp
name: 'DefaultApplicationRuleCollectionGroup'
properties: {
priority: 300
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'update-rule-win'
priority: 100
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'winupdate-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
{
protocolType: 'Http'
port: 80
}
]
fqdnTags: [
'WindowsUpdate'
]
terminateTLS: false
sourceAddresses: [
subnets[0].subnetPrefix
]
}
]
}
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'global-rule-url-microsoft'
priority: 200
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'microsoft-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
]
targetFqdns: [
'*.microsoft.com'
]
terminateTLS: false
sourceAddresses: [
subnets[0].subnetPrefix
]
}
]
}
]
}
dependsOn: [
dnatRuleCollectionGroup
]
}
// ファイアウォールの作成
resource afw 'Microsoft.Network/azureFirewalls@2023-11-01' = {
name: afwName
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
properties: {
sku: {
name: 'AZFW_VNet'
tier: 'Standard'
}
firewallPolicy: {
id: afwp.id
}
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
subnet: {
id: vnet.properties.subnets[1].id
}
publicIPAddress: {
id: pipFW.id
}
}
}
]
}
dependsOn: [
dnatRuleCollectionGroup
applicationRuleCollectionGroup
]
}
// ネットワークインターフェイスの作成
resource nic 'Microsoft.Network/networkInterfaces@2021-02-01' = {
name: nicName
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, subnets[0].name)
}
}
}
]
}
}
// 仮想マシンの作成
resource vm 'Microsoft.Compute/virtualMachines@2021-07-01' = {
name: vmName
location: location
properties: {
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
adminPassword: adminPassword
}
storageProfile: {
imageReference: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: OSVersion
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: storageAccountType
}
}
}
networkProfile: {
networkInterfaces: [
{
id: nic.id
}
]
}
}
}
// Managed Run Command
resource runCommand 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = {
name: rcName
location: location
parent: vm
properties: {
source: {
script: 'Install-WindowsFeature -Name Web-Server -IncludeManagementTools'
}
}
}
// Azure Bastionの作成
resource bastion 'Microsoft.Network/bastionHosts@2023-11-01' = {
name: basName
location: location
properties: {
ipConfigurations: [
{
name: 'bastionIpConfig'
properties: {
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, 'AzureBastionSubnet')
}
publicIPAddress: {
id: pipBastion.id
}
}
}
]
}
}
4-2 リソースのデプロイ
az deployment group create --template-file main.bicep --resource-group rg-demo-dev
※仮想マシンのログインパスワードを聞かれますので設定します。
正常終了するとリソースが作成されます。
5. 検証
DNATルールコレクショングループおよびアプリケーションルールコレクショングループの検証をおこないます。
5-1 DNat
1 . DNAT検証を行います。Azure Firewallに関連付けされているパブリックIPアドバイスに対してブラウザからアクセスします。
2 . Webサーバにアクセスできました。
5-2 アプリケーション
Azure Bastion経由で仮想マシンにログインします。
1 . 仮想マシンのEdgeを開きOfficeを開きます。
2 . ブロックされることが確認できました。
3 . 許可している microsoft.com にアクセスします。
4 . アクセスできました。
5 . Windows Updateを確認します。
6 . インストール&再起動後、適用済みのWindows Updateを確認します。反映されていることが確認できました。
6. 終わりに
本記事を最後まで読んで頂きましてありがとうございます。
無事にIaCを利用して構築・検証まで出来ました。課題が発生した際に調査し、解決すると一安心ですね。