AZ-500 · LAB 31 ~60 phút Chương 07 Private Access Security Trung bình

Access Restriction + VNet Integration cho App Service

Cấu hình inbound Access Restrictions (chỉ cho phép IP/subnet cụ thể), bật VNet Integration cho outbound traffic, tạo Private Endpoint cho inbound private — kiểm soát toàn diện inbound và outbound network của Azure App Service.

🎯 Mục Tiêu Lab

Tạo App Service Plan và Web App cơ bản

Cấu hình Access Restrictions — chỉ cho phép subnet hoặc IP cụ thể truy cập inbound

Bật VNet Integration — cho phép app gọi outbound đến resources trong VNet (SQL, Storage private endpoint)

Tạo Private Endpoint cho App Service (inbound private — không expose public hostname)

Phân biệt Access Restriction vs Private Endpoint vs VNet Integration

Kiểm tra App Service gọi được Storage private endpoint qua VNet Integration

📋 Chuẩn Bị

Yêu cầu:
  • Azure subscription Active
  • VNet vnet-az500-private từ lab trước
  • App Service Plan Standard S1 hoặc cao hơn (cần để dùng VNet Integration)
  • Azure CLI ≥ 2.50
Chi phí & Lưu ý:
  • App Service Plan S1: ~$0.10/giờ — xóa sau lab
  • VNet Integration cần subnet riêng (không share với PE hay app subnet)
  • Private DNS Zone App Service: privatelink.azurewebsites.net
  • Free/Shared tier KHÔNG hỗ trợ VNet Integration

🏗️ Kịch Bản & Kiến Trúc

App Service cần: (1) chỉ nhận inbound từ subnet frontend hoặc Application Gateway — không public Internet, (2) gọi outbound đến SQL và Storage private endpoints trong VNet. Giải pháp: Access Restriction + VNet Integration, hoặc tạo Private Endpoint cho App Service nếu cần private hoàn toàn.

# Kiến trúc bảo mật App Service
Internet ──[Access Restriction: DENY]──✗ App Service (appservice-az500-lab31)
snet-frontend (10.10.4.0/24) ──[Allow]──────────────►│ INBOUND
App Service ──[VNet Integration: snet-vnetint]──────►│ OUTBOUND
├── Storage PE (10.10.3.4)
└── SQL PE (10.10.3.5)
Access Restriction
Inbound firewall — chỉ cho phép nguồn cụ thể (IP/subnet/Service Tag)
VNet Integration
Outbound từ app vào VNet — app gọi được resources private trong VNet
Private Endpoint
Inbound private — tắt public hostname, truy cập qua private IP từ VNet

🧪 Các Bước Thực Hiện

1

Tạo Subnet Bổ Sung và App Service

Tạo snet-frontend (nguồn inbound được phép) và snet-vnetint (subnet cho VNet Integration — phải delegate cho App Service).

Cách 2 — Azure CLI
# Subnet frontend (nguồn được phép truy cập app)
az network vnet subnet create \
  --name snet-frontend \
  --vnet-name vnet-az500-private \
  --resource-group rg-az500-ch07-private \
  --address-prefix 10.10.4.0/24

# Subnet VNet Integration (phải delegate cho App Service)
az network vnet subnet create \
  --name snet-vnetint \
  --vnet-name vnet-az500-private \
  --resource-group rg-az500-ch07-private \
  --address-prefix 10.10.5.0/24 \
  --delegations Microsoft.Web/serverFarms

# Tạo App Service Plan (Standard S1 — bắt buộc cho VNet Integration)
az appservice plan create \
  --name asp-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --location southeastasia \
  --sku S1

# Tạo Web App
az webapp create \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --plan asp-az500-lab31 \
  --runtime "NODE:20-lts"

# Xem URL mặc định
az webapp show \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --query defaultHostName -o tsv
# → appservice-az500-lab31.azurewebsites.net
Cách 1 — Azure Portal
1. Portal → App Services → + Create → Web App
   Basics:
   - Resource group: rg-az500-ch07-private
   - Name: appservice-az500-lab31 (phải unique)
   - Publish: Code
   - Runtime stack: Node 20 LTS
   - Region: Southeast Asia
   - Plan: (New) asp-az500-lab31 → Standard S1

   Networking:
   - Enable public access: On (để test, sẽ restrict sau)

2. Review + Create → Create
2

Cấu Hình Access Restrictions (Inbound)

Thêm rule cho phép snet-frontend, sau đó deny tất cả Internet. Access Restrictions hoạt động như layer-7 firewall cho App Service.

Cách 1 — Azure Portal
1. App Service → Networking → Access restriction → + Add rule

   Rule 1 — Cho phép snet-frontend:
   - Name: Allow-snet-frontend
   - Priority: 100
   - Action: Allow
   - Type: Virtual Network
   - Virtual Network: vnet-az500-private
   - Subnet: snet-frontend

   Rule 2 — Deny tất cả (thêm sau Allow)
   - Name: Deny-All-Internet
   - Priority: 200
   - Action: Deny
   - Type: Any

2. Save → Xác nhận rules hiển thị đúng thứ tự priority

Lưu ý: App Service tự động có Deny All mặc định ở cuối.
Rule Allow ở priority thấp hơn sẽ được ưu tiên trước.
Cách 2 — Azure CLI
# Lấy subnet ID của snet-frontend
FRONTEND_SUBNET=$(az network vnet subnet show \
  --name snet-frontend \
  --vnet-name vnet-az500-private \
  --resource-group rg-az500-ch07-private \
  --query id -o tsv)

# Thêm rule Allow cho snet-frontend (priority 100)
az webapp config access-restriction add \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --rule-name Allow-snet-frontend \
  --action Allow \
  --subnet $FRONTEND_SUBNET \
  --priority 100

# Thêm rule Deny All (priority 200)
az webapp config access-restriction add \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --rule-name Deny-All-Internet \
  --action Deny \
  --ip-address Any \
  --priority 200

# Xem danh sách rules
az webapp config access-restriction show \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --output table
Kết quả (Output)
Name                  Priority  Action  Type           IpAddress/Subnet
--------------------  --------  ------  -------------  --------------------------
Allow-snet-frontend   100       Allow   VirtualNetwork snet-frontend (10.10.4.0/24)
Deny-All-Internet     200       Deny    Any            Any
3

Bật VNet Integration (Outbound)

VNet Integration cho phép App Service gọi outbound đến resources trong VNet (Storage PE, SQL PE). Subnet snet-vnetint được delegate cho Microsoft.Web/serverFarms.

Cách 1 — Azure Portal
1. App Service → Networking → VNet integration → + Add VNet integration
   - Virtual Network: vnet-az500-private
   - Subnet: snet-vnetint (phải đã delegate)
   → OK → đợi ~1 phút

2. App settings → (quan trọng) thêm app setting:
   WEBSITE_DNS_SERVER = 168.63.129.16
   → Để app dùng Azure DNS, phân giải được Private DNS Zone

3. Configuration → General settings → bật:
   "Route all traffic" = On
   → Để toàn bộ outbound đi qua VNet (bao gồm DNS queries)
Cách 2 — Azure CLI
# Bật VNet Integration với snet-vnetint
az webapp vnet-integration add \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --vnet vnet-az500-private \
  --subnet snet-vnetint

# Thêm app setting để dùng Azure DNS (bắt buộc cho Private DNS Zone)
az webapp config appsettings set \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --settings WEBSITE_DNS_SERVER=168.63.129.16

# Bật route all traffic qua VNet (để DNS query cũng dùng VNet DNS)
az webapp config set \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --generic-configurations '{"vnetRouteAllEnabled": true}'

# Xác nhận VNet Integration
az webapp vnet-integration list \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --output table
Kết quả (Output)
Name                   VnetResourceId                                    SubnetName    SwiftSupported
---------------------  ------------------------------------------------  ------------  ----------------
vnet-az500-private     /subscriptions/.../vnet-az500-private             snet-vnetint  True
Tại sao cần WEBSITE_DNS_SERVER? App Service mặc định dùng DNS public. Để phân giải được Private DNS Zone (10.x.x.x cho Storage/SQL PE), phải chỉ App Service dùng Azure DNS (168.63.129.16) cùng với route all traffic qua VNet — khi đó DNS query đi qua VNet và được trả lời bởi Private DNS Zone.
4

Tạo Private Endpoint cho App Service (Tùy chọn — Inbound Private)

Nếu muốn App Service không có public hostname, tạo Private Endpoint. Sau khi tạo PE, public URL vẫn tồn tại nhưng bị từ chối — chỉ truy cập qua private IP từ VNet.

Cách 2 — Azure CLI
# Lấy App Service ID
APP_ID=$(az webapp show \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private \
  --query id -o tsv)

# Tạo Private Endpoint cho App Service
az network private-endpoint create \
  --name pe-appservice-lab31 \
  --resource-group rg-az500-ch07-private \
  --vnet-name vnet-az500-private \
  --subnet snet-pe \
  --private-connection-resource-id $APP_ID \
  --group-id sites \
  --connection-name conn-appservice-lab31 \
  --location southeastasia

# Tạo Private DNS Zone cho App Service
az network private-dns zone create \
  --resource-group rg-az500-ch07-private \
  --name "privatelink.azurewebsites.net"

# Link DNS Zone với VNet
az network private-dns link vnet create \
  --resource-group rg-az500-ch07-private \
  --zone-name "privatelink.azurewebsites.net" \
  --name link-vnet-appservice \
  --virtual-network vnet-az500-private \
  --registration-enabled false

# Lấy private IP và tạo A record
NIC_ID=$(az network private-endpoint show \
  --name pe-appservice-lab31 \
  --resource-group rg-az500-ch07-private \
  --query "networkInterfaces[0].id" -o tsv)

APP_PRIVATE_IP=$(az network nic show --ids $NIC_ID \
  --query "ipConfigurations[0].privateIPAddress" -o tsv)

az network private-dns record-set a add-record \
  --resource-group rg-az500-ch07-private \
  --zone-name "privatelink.azurewebsites.net" \
  --record-set-name "appservice-az500-lab31" \
  --ipv4-address $APP_PRIVATE_IP

echo "App Service Private IP: $APP_PRIVATE_IP"
5

Kiểm Tra Access Restriction và VNet Integration

Kiểm tra Access Restriction — từ ngoài bị từ chối
# Từ máy local (ngoài snet-frontend) — phải bị 403
curl -I https://appservice-az500-lab31.azurewebsites.net
# HTTP/1.1 403 Forbidden
# X-MS-IIS-Reason: AccessRestriction

# Từ VM trong snet-frontend — phải được phép (200)
# SSH vào VM trong snet-frontend, rồi:
curl -s https://appservice-az500-lab31.azurewebsites.net
# → HTML của default page App Service
Kiểm tra VNet Integration — app gọi được Storage PE
# Dùng Kudu console của App Service để test từ bên trong app
# Portal → App Service → Advanced Tools (Kudu) → SSH hoặc Debug Console

# Trong Kudu console:
# Kiểm tra DNS phân giải Storage PE qua VNet
curl https://staz500lab29xk9m.blob.core.windows.net \
  --resolve staz500lab29xk9m.blob.core.windows.net:443:10.10.3.4 \
  -I
# Hoặc dùng nameserver 168.63.129.16:
nslookup staz500lab29xk9m.blob.core.windows.net 168.63.129.16
# → 10.10.3.4 ← App Service phân giải được private IP qua VNet Integration!

# Test từ Azure CLI (App Service có thể dùng managed identity)
# Portal → App Service → Identity → System assigned: On
az webapp identity assign \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private
Xác nhận qua Portal
1. App Service → Networking:
   - Inbound traffic: Private endpoint + Access restrictions ✓
   - Outbound traffic: VNet Integration → snet-vnetint ✓

2. App Service → Networking → VNet integration:
   - Status: Connected to vnet-az500-private
   - Route All: Enabled ✓

3. App Service → Networking → Access restriction:
   - Allow-snet-frontend (Priority 100) ✓
   - Deny-All-Internet (Priority 200) ✓

📊 Kết Quả Đầu Ra Lab 31

Access Restriction hoạt động

curl từ ngoài → 403 Forbidden (AccessRestriction). Từ snet-frontend → 200 OK

VNet Integration kết nối

App Service connected snet-vnetint, Route All: Enabled

DNS private phân giải được

nslookup trong Kudu → Storage/SQL PE trả về 10.x.x.x

Private Endpoint tạo thành công

pe-appservice-lab31 với private IP, DNS Zone privatelink.azurewebsites.net linked

Managed Identity bật

App có system-assigned managed identity để gọi Storage/SQL không cần credential

WEBSITE_DNS_SERVER đúng

App setting 168.63.129.16 để dùng Azure DNS qua VNet Integration

🧹 Dọn Dẹp Tài Nguyên

Azure CLI — Dọn dẹp Lab 31 + toàn bộ Lab 28-31
# Xóa Private Endpoint App Service
az network private-endpoint delete \
  --name pe-appservice-lab31 \
  --resource-group rg-az500-ch07-private --yes

# Xóa App Service Plan và Web App
az webapp delete \
  --name appservice-az500-lab31 \
  --resource-group rg-az500-ch07-private
az appservice plan delete \
  --name asp-az500-lab31 \
  --resource-group rg-az500-ch07-private --yes

# Xóa Private DNS Zones còn lại
az network private-dns zone delete \
  --resource-group rg-az500-ch07-private \
  --name "privatelink.azurewebsites.net" --yes

# Xóa toàn bộ resource group (bao gồm mọi thứ từ Lab 28-31)
az group delete \
  --name rg-az500-ch07-private \
  --yes --no-wait

# Xác nhận xóa
az group show --name rg-az500-ch07-private 2>/dev/null || echo "Resource group deleted."

❓ Câu Hỏi Ôn Tập

1. Access Restriction và Private Endpoint khác nhau như thế nào về inbound bảo mật App Service?

Gợi ý: Access Restriction là layer-7 firewall — public hostname vẫn tồn tại, traffic bị filter. Private Endpoint thay đổi DNS, app chỉ có private IP — public URL vẫn resolve nhưng không có service lắng nghe. PE bảo mật hơn nhưng tốn phí.

2. Tại sao VNet Integration cần subnet riêng được delegate cho Microsoft.Web/serverFarms?

Gợi ý: Delegation cho phép App Service Platform inject NIC vào subnet đó để route outbound traffic. Không thể share với subnet khác đã có delegation hoặc Private Endpoint.

3. Tại sao phải set cả WEBSITE_DNS_SERVER=168.63.129.16vnetRouteAllEnabled=true?

Gợi ý: WEBSITE_DNS_SERVER chỉ Azure DNS. vnetRouteAllEnabled bắt tất cả traffic (bao gồm DNS UDP port 53) đi qua VNet. Nếu thiếu một trong hai, DNS query vẫn đi public và không phân giải được Private DNS Zone.

4. App Service Free/Shared tier có hỗ trợ VNet Integration không? Tại sao cần Standard S1 trở lên?

Gợi ý: Free và Shared không hỗ trợ VNet Integration vì chạy trên shared infrastructure. Basic B1 hỗ trợ VNet Integration nhưng không hỗ trợ Private Endpoint (cần Standard+). Mỗi tier mở thêm tính năng networking.

5. Kịch bản nào nên dùng Access Restriction thay vì Private Endpoint cho App Service?

Gợi ý: Access Restriction phù hợp khi: cần restrict theo IP cụ thể/subnet/Service Tag, muốn giữ public hostname cho CDN/Front Door, không có budget cho PE, tier Basic. Private Endpoint phù hợp khi cần zero public exposure, hybrid scenario, enterprise compliance.

Lab 30: Private Endpoint SQL Thư viện Labs Lab 32: Azure Bastion
Zalo