-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathnsp.azcli
309 lines (279 loc) · 19.4 KB
/
nsp.azcli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#################################################
# AzCLI commands to test NSP functionality
#
# Jose Moreno, October, 2023
#################################################
# Variables
rg=nsptest
location=northcentralus
nsp_name=nsp01
suffix=$RANDOM
storage_account_name="nsptest${suffix}"
storage_id_name="nsptest${suffix}-id"
storage2_account_name="nonsptest${suffix}"
sql_server_name="nsptest${suffix}"
sql_server_user=$(whoami)
akv_name="nsptest${suffix}"
vnet_name=vnet01
vnet_prefix=10.13.76.0/24
subnet_name=vm
subnet_prefix=10.13.76.0/28
vm_size=Standard_B1s
vm_name=vm01
vm_pip_name="${vm_name}-pip"
vm_nsg_name="${vm_name}-nsg"
ep_subnet_name=ep
ep_subnet_prefix=10.13.76.32/27
# Second VM to test access over service endpoints
vm2_name=vm02
vm2_pip_name="${vm2_name}-pip"
vm2_nsg_name="${vm2_name}-nsg"
vnet2_name=vnet02
vnet2_prefix=10.13.77.0/24
subnet2_name=vm2
subnet2_prefix=10.13.77.16/28
# Get default password to use in examples
default_password=$(az keyvault secret show --vault-name "erjositoKeyvault" -n "defaultpassword" --query value -o tsv)
# Make sure you have the NSP extension
extension_name=nsp
echo "Checking if you have the Azure CLI '$extension_name' extension installed..."
extension_output=$(az extension show --name "$extension_name" 2>&1)
if echo $extension_output | grep -q "not installed"; then
echo "Adding extension $extension_name..."
az extension add --name "$extension_name" -o none
else
echo "Updating extension $extension_name..."
az extension update --name "$extension_name" -o none
fi
echo ""
# Create resource group
echo "Creating resource group..."
az group create --name $rg --location $location -o none --only-show-errors
# Create NSP and profiles
echo "Creating NSP..."
az network perimeter create -n $nsp_name -g $rg -l $location -o none --only-show-errors
az network perimeter profile create -n profile00 -g $rg --perimeter-name $nsp_name -o none --only-show-errors
az network perimeter profile create -n profile01 -g $rg --perimeter-name $nsp_name -o none --only-show-errors
# Create Diagnostic settings for NSP
logws_name=$(az monitor log-analytics workspace list -g $rg --query '[0].name' -o tsv)
if [[ -z "$logws_name" ]]
then
logws_name="nsplog${suffix}"
echo "INFO: Creating log analytics workspace ${logws_name}..."
az monitor log-analytics workspace create -n $logws_name -g $rg -o none
else
echo "INFO: Log Analytics workspace $logws_name found in resource group $rg"
fi
logws_id=$(az resource list -g $rg -n $logws_name --query '[].id' -o tsv)
logws_customerid=$(az monitor log-analytics workspace show -n $logws_name -g $rg --query customerId -o tsv)
nsp_id=$(az network perimeter show -n $nsp_name -g $rg --query id -o tsv)
echo "Creating diagnostic settings for NSP ID $nsp_id..."
# az monitor diagnostic-settings create -n "diag$suffix" --resource $nsp_id --workspace $logws_id \
# --metrics '[{"category": "AllMetrics", "enabled": true, "retentionPolicy": {"days": 0, "enabled": false }]' \
# --logs '[{"category": "allLogs", "enabled": true, "retentionPolicy": {"days": 0, "enabled": false}}]'
az monitor diagnostic-settings create -n "diag$suffix" --resource $nsp_id --workspace $logws_id -o none \
--logs '[{categoryGroup:allLogs,enabled:true,retentionPolicy:{days:0,enabled:false}}]'
# Create storage account
echo "Creating storage account $storage_account_name..."
az storage account create -n $storage_account_name -g $rg -l $location --sku Standard_LRS -o none --only-show-errors
storage_account_key=$(az storage account keys list -n $storage_account_name -g $rg --query '[0].value' -o tsv)
az storage container create -n "container01" --account-name $storage_account_name --account-key $storage_account_key -o none --only-show-errors
echo "This is a test file" > /tmp/test.txt
az storage blob upload -f "/tmp/test.txt" -c "container01" -n "test.txt" --account-name $storage_account_name --account-key $storage_account_key -o none --only-show-errors
sas_end_date=$(date -u -d "30 days" '+%Y-%m-%dT%H:%MZ')
blob_sas=$(az storage blob generate-sas -c "container01" -n "test.txt" --account-name $storage_account_name --account-key $storage_account_key --permissions r --expiry $sas_end_date -o tsv)
storage_account_url=$(az storage account show -n $storage_account_name -g $rg --query 'primaryEndpoints.blob' -o tsv)
blob_url="${storage_account_url}container01/test.txt?${blob_sas}"
echo "Associating storage account $storage_account_name with NSP $nsp_name and profile00..."
profile_name=profile00
profile_id=$(az network perimeter profile show -n $profile_name --perimeter-name $nsp_name -g $rg --query id -o tsv)
storage_account_id=$(az storage account show -n $storage_account_name -g $rg --query id -o tsv)
az network perimeter association create -n $storage_account_name --perimeter-name $nsp_name -g $rg -o none\
--access-mode Learning --private-link-resource "{id:$storage_account_id}" --profile "{id:$profile_id}"
# Create 2nd storage account outside of the NSP
echo "Creating storage account $storage2_account_name..."
az storage account create -n $storage2_account_name -g $rg -l $location --sku Standard_LRS -o none --only-show-errors
storage2_account_key=$(az storage account keys list -n $storage2_account_name -g $rg --query '[0].value' -o tsv)
# Create AKV
echo "Creating AKV $akv_name..."
az keyvault create -n "$akv_name" -g $rg -l $location -o none --only-show-errors
user=$(az ad signed-in-user show --query userPrincipalName -o tsv)
az keyvault set-policy -n $akv_name --upn $user --secret-permissions all --key-permissions all --certificate-permissions all -o none --only-show-errors
echo "Adding test secret to AKV..."
az keyvault secret set --vault-name $akv_name -n testsecret --value 'HelloWorld' -o none --only-show-errors
echo "Adding test key to AKV..."
az keyvault key create --vault-name $akv_name -n testkey --kty RSA --size 4096 -o none --only-show-errors
echo "Adding AKV to NSP..."
profile_name=profile00
profile_id=$(az network perimeter profile show -n $profile_name --perimeter-name $nsp_name -g $rg --query id -o tsv)
akv_id=$(az keyvault show -n $akv_name -g $rg --query id -o tsv)
az network perimeter association create -n $akv_name --perimeter-name $nsp_name -g $rg -o none\
--access-mode Learning --private-link-resource "{id:$akv_id}" --profile "{id:$profile_id}"
# Create Azure SQL Server and database
az sql server create -n $sql_server_name -g $rg -l $location -u $(whoami) -p $default_password -o none --only-show-errors
az sql db create -n "db01" -s $sql_server_name -g $rg -o none --only-show-errors --no-wait
# Create sample workload in VNet1 for direct access / access over private link
echo "Creating VM1..."
az vm create -n $vm_name -g $rg --image Ubuntu2204 --size $vm_size --admin-username $(whoami) --generate-ssh-keys -l $location \
--vnet-name $vnet_name --vnet-address-prefix $vnet_prefix --subnet $subnet_name --subnet-address-prefix $subnet_prefix \
--public-ip-address $vm_pip_name --public-ip-sku Standard --nsg $vm_nsg_name \
-o none --only-show-errors --no-wait
# Create sample workload in VNet2 for access over vnet service endpoint
echo "Creating VM2..."
az vm create -n $vm2_name -g $rg --image Ubuntu2204 --size $vm_size --admin-username $(whoami) --generate-ssh-keys -l $location \
--vnet-name $vnet2_name --vnet-address-prefix $vnet2_prefix --subnet $subnet2_name --subnet-address-prefix $subnet2_prefix \
--public-ip-address $vm2_pip_name --public-ip-sku Standard --nsg $vm2_nsg_name \
-o none --only-show-errors
az network vnet subnet update -n $subnet2_name --vnet-name $vnet2_name -g $rg --service-endpoints Microsoft.Storage -o none --only-show-errors
# Create private endpoint for Azure Storage
echo "Creating private endpoint for Azure Storage..."
ep_name=blobendpoint
az network vnet subnet create -n $ep_subnet_name --vnet-name $vnet_name -g $rg --address-prefix $ep_subnet_prefix -o none --only-show-errors
storage_account_id=$(az storage account show -n $storage_account_name -g $rg --query id -o tsv)
az network private-endpoint create -n $ep_name -g $rg --vnet-name $vnet_name --subnet $ep_subnet_name --private-connection-resource-id $storage_account_id --group-id blob --connection-name blob -o none --only-show-errors
dns_zone_name=privatelink.blob.core.windows.net
az network private-dns zone create -n $dns_zone_name -g $rg -o none --only-show-errors
az network private-dns link vnet create -g $rg -z $dns_zone_name -n $vnet_name --virtual-network $vnet_name --registration-enabled false -o none --only-show-errors
# az network private-endpoint dns-zone-group delete --endpoint-name $ep_name -n deployedByPolicy -g $rg -o none --only-show-errors
az network private-endpoint dns-zone-group create --endpoint-name $ep_name -g $rg -n myzonegroup --zone-name zone1 --private-dns-zone $dns_zone_name -o none --only-show-errors
# Create private endpoint for Azure SQL
echo "Creating private endpoint for Azure SQL..."
ep_name=sqlendpoint
sql_server_id=$(az sql server show -n $sql_server_name -g $rg --query id -o tsv)
az network private-endpoint create -n $ep_name -g $rg --vnet-name $vnet_name --subnet $ep_subnet_name --private-connection-resource-id $sql_server_id --group-id sqlServer --connection-name sql -o none --only-show-errors
dns_zone_name=privatelink.database.windows.net
az network private-dns zone create -n $dns_zone_name -g $rg -o none --only-show-errors
az network private-dns link vnet create -g $rg -z $dns_zone_name -n $vnet_name --virtual-network $vnet_name --registration-enabled false -o none --only-show-errors
# az network private-endpoint dns-zone-group delete --endpoint-name $ep_name -n deployedByPolicy -g $rg -o none --only-show-errors
az network private-endpoint dns-zone-group create --endpoint-name $ep_name -g $rg -n myzonegroup --zone-name zone1 --private-dns-zone $dns_zone_name -o none --only-show-errors
# Create NSP rules
# - Inbound from IP addresses
az network perimeter profile access-rule create -n rule01 --profile-name profile00 --perimeter-name $nsp_name -g $rg --address-prefixes "['$vnet_prefix']" -o none
# - Inbound from other NSPs
# az network perimeter profile access-rule create -n rule02 --profile-name profile00 --perimeter-name $nsp_name -g $rg --nsp "[{id:<NSP_ARM_ID>}]" -o none
# - Inbound from subscriptions
subscription_id=$(az account show --query id -o tsv)
az network perimeter profile access-rule create -n rule04 --profile-name profile00 --perimeter-name $nsp_name -g $rg --subscriptions "[{id:${subscription_id}}]" -o none
# az network perimeter profile access-rule create -n rule05 --profile-name profile00 --perimeter-name $nsp_name -g $rg --subscriptions [0].id="${subscription_id}" -o none
# - Outbound to FQDNs
az network perimeter profile access-rule create -n rule03 --profile-name profile00 --perimeter-name $nsp_name -g $rg --fqdn "['www.abc.com', 'www.google.com']" --direction "Outbound" -o none
# - Outbound to email addresses
az network perimeter profile access-rule create --name rule06 --profile-name profile00 --perimeter-name $nsp_name --resource-group $rg --email-addresses "[\'[email protected]\', \'[email protected]\']" --direction "Outbound"
# - Outbound to phone numbers
az network perimeter profile access-rule create --name rule07 --profile-name profile00 --perimeter-name $nsp_name --resource-group $rg --phone-numbers "[\'+919898989898\', \'+929898989898\']" --direction "Outbound"
# Test access to storage account from local machine and switch between Learning and Enforced
echo "Switching NSP association to 'Learning'"
az network perimeter association update -n $storage_account_name --perimeter-name $nsp_name -g $rg --access-mode Learning -o none
sleep 15 # Takes some seconds to propagate
if [[ -f "/tmp/test2.txt" ]]; then
rm /tmp/test2.txt
fi
echo "Trying to download file with Azure CLI..."
az storage blob download -c "container01" -n "test.txt" -f "/tmp/test2.txt" --account-name $storage_account_name --account-key $storage_account_key -o none --only-show-errors
cat /tmp/test2.txt
echo "Trying to download file with curl and SAS..."
curl $blob_url
echo "Switching NSP association to 'Enforced'"
az network perimeter association update -n $storage_account_name --perimeter-name $nsp_name -g $rg --access-mode Enforced -o none
sleep 15 # Takes some seconds to propagate
if [[ -f "/tmp/test2.txt" ]]; then
rm /tmp/test2.txt
fi
echo "Trying to download file with Azure CLI..."
az storage blob download -c "container01" -n "test.txt" -f "/tmp/test2.txt" --account-name $storage_account_name --account-key $storage_account_key -o none --only-show-errors
cat /tmp/test2.txt
echo "Trying to download file with curl and SAS..."
curl $blob_url
# Reset SSH keys (if required)
az vm user update -g $rg -u $(whoami) --ssh-key-value "$(< ~/.ssh/id_rsa.pub)" -o none -n $vm_name -o none
az vm user update -g $rg -u $(whoami) --ssh-key-value "$(< ~/.ssh/id_rsa.pub)" -o none -n $vm2_name -o none
# Test access to storage account from Azure VMs
vm1_pip=$(az network public-ip show -n $vm_pip_name -g $rg --query ipAddress -o tsv)
vm2_pip=$(az network public-ip show -n $vm_pip_name -g $rg --query ipAddress -o tsv)
echo "Switching NSP association to 'Learning'"
az network perimeter association update -n $storage_account_name --perimeter-name $nsp_name -g $rg --access-mode Learning -o none
sleep 15 # Takes some seconds to propagate
echo "Trying to download file from VM using a private endpoint..."
ssh -n -o StrictHostKeyChecking=no $vm1_pip "curl -s \"$blob_url\""
echo "Trying to download file from VM using a service endpoint..."
ssh -n -o StrictHostKeyChecking=no $vm2_pip "curl -s \"$blob_url\""
echo "Switching NSP association to 'Enforced'"
az network perimeter association update -n $storage_account_name --perimeter-name $nsp_name -g $rg --access-mode Enforced -o none
sleep 15 # Takes some seconds to propagate
echo "Trying to download file from VM using a private endpoint..."
ssh -n -o StrictHostKeyChecking=no $vm1_pip "curl -s \"$blob_url\""
echo "Trying to download file from VM using a service endpoint..."
ssh -n -o StrictHostKeyChecking=no $vm2_pip "curl -s \"$blob_url\""
echo "Now removing NSP rule01 and trying again..."
az network perimeter profile access-rule delete -n rule01 --profile-name profile00 --perimeter-name $nsp_name -g $rg -y -o none --only-show-errors
sleep 15 # Takes some seconds to propagate
echo "Trying to download file from VM using a private endpoint..."
ssh -n -o StrictHostKeyChecking=no $vm1_pip "curl -s \"$blob_url\""
echo "Trying to download file from VM using a service endpoint..."
ssh -n -o StrictHostKeyChecking=no $vm2_pip "curl -s \"$blob_url\""
#############
# WIP #
#############
# Enable storage encryption with AKV's key when AKV is in/out the NSP??
# https://learn.microsoft.com/en-us/azure/storage/common/customer-managed-keys-overview
# https://learn.microsoft.com/en-us/azure/storage/common/customer-managed-keys-configure-existing-account?tabs=azure-portal
# az storage account update --encryption-key-source Microsoft.KeyVault --encryption-key-name --encryption-key-source --key-vault-user-identity-id
echo "Creating identity $storage_id_name..."
az identity create -n $storage_id_name -g $rg -l $location -o none --only-show-errors
storage_id_id=$(az identity show -n $storage_id_name -g $rg --query id -o tsv)
storage_id_principal=$(az identity show -n $storage_id_name -g $rg --query principalId -o tsv)
storage_id_client=$(az identity show -n $storage_id_name -g $rg --query clientId -o tsv)
akv_id=$(az keyvault show -n $akv_name -g $rg --query id -o tsv)
az storage account update -n $storage_account_name -g $rg --user-identity-id $storage_id_id -o none --only-show-errors
echo "Granting key permissions to identity $storage_id_name..."
az role assignment create --assignee-object-id $storage_id_principal --role "Key Vault Crypto Service Encryption User" --scope $akv_id --assignee-principal-type ServicePrincipal -o none
az keyvault set-policy -n $akv_name --key-permissions list encrypt decrypt get getrotationpolicy wrapKey unwrapKey --spn "${storage_id_client}" -o none
echo "Configuring customer-managed SSE on storage account $storage_account_name..."
akv_uri=$(az keyvault show -n $akv_name -g $rg --query properties.vaultUri -o tsv)
# For storage account in NSP
az storage account update -n $storage_account_name -g $rg -o none --only-show-errors \
--encryption-key-vault $akv_uri --encryption-key-name testkey --encryption-key-source Microsoft.Keyvault \
--key-vault-user-identity-id $storage_id_id --identity-type UserAssigned --user-identity-id $storage_id_id
# Message: The operation failed because of authentication issue on the keyvault. For more information, see - https://aka.ms/storagekeyvaultaccesspolicy
# For storage account outside of NSP
az storage account update -n $storage2_account_name -g $rg -o none --only-show-errors \
--encryption-key-vault $akv_uri --encryption-key-name testkey --encryption-key-source Microsoft.Keyvault \
--key-vault-user-identity-id $storage_id_id --identity-type UserAssigned --user-identity-id $storage_id_id
#################
# Diagnostics #
#################
az network perimeter list -g $rg -o table
az network perimeter profile list --perimeter-name $nsp_name -g $rg -o table
az network perimeter onboarded-resources list -l $location -o table
az network perimeter profile access-rule list --profile-name profile00 --perimeter-name $nsp_name -g $rg -o table
az network perimeter association list --perimeter-name $nsp_name -g $rg -o table
subscription_id=$(az account show --query id -o tsv)
az network perimeter association list --perimeter-name $nsp_name -g $rg --query '[].{Name:name, AccessMode:properties.accessMode, Resource:properties.privateLinkResource.id, Profile:properties.profile.id}' -o table | awk '{ gsub(/\/subscriptions\/'$subscription_id'\/resourceGroups\/'$rg'\/providers\/Microsoft/,""); print }'
az network perimeter profile access-rule list --profile-name profile00 --perimeter-name $nsp_name -g $rg -o table
az network perimeter profile access-rule list --profile-name profile00 --perimeter-name $nsp_name -g $rg -o table --query '[].{Name:name, Direction:properties.direction, Addresses:properties.addressPrefixes[0], Emails:properties.emailAddresses[0], FQDNs:properties.fullyQualifiedDomainNames[0], Phones:properties.phones[0], Subscriptions:properties.subscriptions[0]}'
################
# Logs #
################
# NSP access logs categories
query='NSPAccessLogs
| where TimeGenerated > ago(1d)
| distinct Category'
az monitor log-analytics query -w $logws_customerid --analytics-query "$query" -o table
# NSP allowed by resource rules
query='NSPAccessLogs
| where TimeGenerated > ago(1h)
| where Category == "NspPublicInboundResourceRulesAllowed"
| project TimeGenerated, OperationName, Profile, MatchedRule, SourceIpAddress'
az monitor log-analytics query -w $logws_customerid --analytics-query "$query" -o table
# NSP denied by perimeter rules
query='NSPAccessLogs
| where TimeGenerated > ago(1h)
| where Category == "NspPublicInboundPerimeterRulesDenied"
| project TimeGenerated, OperationName, Profile, MatchedRule, SourceIpAddress'
az monitor log-analytics query -w $logws_customerid --analytics-query "$query" -o table
# Access allowed coming from private
query='NSPAccessLogs
| where TimeGenerated > ago(1h)
| where Category == "NspPrivateInboundAllowed"
| project TimeGenerated, OperationName, Profile, MatchedRule, SourceIpAddress'
az monitor log-analytics query -w $logws_customerid --analytics-query "$query" -o table