適用於 Linux VM 的 Azure 磁碟加密範例指令碼
警告
本文會參考 CentOS,這是生命週期結束 (EOL) 狀態的 Linux 發行版本。 請據以考慮您的使用和規劃。 如需詳細資訊,請參閱 CentOS 生命週期結束指導。
適用於:✔️ Linux VM ✔️ 彈性擴展集
本文提供了對於準備預先加密 VHD 和其他工作的範例指令碼。
注意
除非另有說明,所有指令碼參考的都是 ADE 的最新且非 AAD 的版本。
Azure 磁碟加密的範例 PowerShell 指令碼
列出訂用帳戶中的所有已加密 VM
您可以使用 此 PowerShell 指令碼,訂閱中的所有資源群組中找到所有 ADE 加密的 VM 和延伸模組版本。
或者,這些 Cmdlet 也會顯示所有 ADE 加密的 VM (但不會顯示延伸模組版本):
$osVolEncrypted = {(Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name).OsVolumeEncrypted} $dataVolEncrypted= {(Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name).DataVolumesEncrypted} Get-AzVm | Format-Table @{Label="MachineName"; Expression={$_.Name}}, @{Label="OsVolumeEncrypted"; Expression=$osVolEncrypted}, @{Label="DataVolumesEncrypted"; Expression=$dataVolEncrypted}
列出訂用帳戶中的所有已加密 VMSS 執行個體
您可以使用 此 PowerShell 指令碼,訂閱中的所有資源群組中找到所有 ADE 加密的 VMSS 執行個體和延伸模組版本。
列出所有用來加密金鑰保存庫中 VM 的磁碟加密祕密
Get-AzKeyVaultSecret -VaultName $KeyVaultName | where {$_.Tags.ContainsKey('DiskEncryptionKeyFileName')} | format-table @{Label="MachineName"; Expression={$_.Tags['MachineName']}}, @{Label="VolumeLetter"; Expression={$_.Tags['VolumeLetter']}}, @{Label="EncryptionKeyURL"; Expression={$_.Id}}
使用 Azure 磁碟加密先決條件 PowerShell 指令碼
如果您已經熟悉 Azure 磁碟加密的必要條件,您可以使用 Azure 磁碟加密必要條件 PowerShell 指令碼。 如需使用此 PowerShell 指令碼的範例,請參閱加密 VM 快速入門。 您可以從指令碼區段 (起自 211 行) 中移除註解,以對現有資源群組中現有 VM 的所有磁碟加密。
下表顯示可在 PowerShell 指令碼中使用的參數:
參數 | 描述 | 必要? |
---|---|---|
$resourceGroupName | 金鑰保存庫所屬資源群組的名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的資源群組。 | True |
$keyVaultName | 要用來放置加密金鑰的金鑰保存庫名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的保存庫。 | True |
$location | 金鑰保存庫的位置。 請確定金鑰保存庫和要加密的 VM 位於相同位置。 使用 Get-AzLocation 取得位置清單。 |
True |
$subscriptionId | 要使用的 Azure 訂用帳戶識別碼。 您可以使用 Get-AzSubscription 取得您的訂用帳戶識別碼。 |
True |
$aadAppName | 會用來將祕密寫入到 KeyVault 的 Microsoft Entra 應用程式名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的應用程式。 如果此應用程式已經存在,請將 aadClientSecret 參數傳遞至指令碼。 | False |
$aadClientSecret | 稍早建立的 Microsoft Entra 應用程式用戶端密碼。 | False |
$keyEncryptionKeyName | 金鑰保存庫中選用金鑰加密金鑰的名稱。 如果不存在此名稱的應用程式,將會以此名稱建立新的金鑰。 | False |
加密或解密沒有 Microsoft Entra 應用程式的 VM
- 在現有或執行中的 Linux VM 上啟用磁碟加密
- 在執行中 Linux VM 上停用加密
- 只能在 Linux VM 的資料磁碟區上停用加密。
加密或解密具有 Microsoft Entra 應用程式 (舊版) 的 VM
- 在現有或執行中的 Linux VM 上啟用磁碟加密
- 在執行中 Linux VM 上停用加密
- 只能在 Linux VM 的資料磁碟區上停用加密。
- 從預先加密的 VHD/儲存體 Blob 建立新加密的受控磁碟
- 建立已加密的新受控磁碟,新的受控磁碟具備預先加密的 VHD 以及對應的加密設定
在執行中的 Linux VM 上加密 OS 磁碟機
OS 磁碟加密的必要條件
- VM 必須使用與 OS 磁碟加密相容的散發套件,如 Azure 磁碟加密支援的作業系統中所列示
- 必須從 Azure Resource Manager 的Marketplace 映像建立 VM。
- 具有至少 4 GB RAM 的 Azure VM (建議大小為 7 GB)。 如需詳細資訊,請參閱 記憶體需求。
- (適用於 RHEL 和 CentOS) 停用 SELinux。 若要停用 SELinux,請參閱「4.4.2. 停用 SELinux」,其位於 VM 上的 SELinux 使用者和系統管理員指南。
- 停用 SELinux 之後,至少重新啟動 VM 一次。
步驟
使用先前指定的其中一個散發套件來建立 VM。
根據您的需求設定 VM。 如果您要加密所有 (OS + 資料) 磁碟機,必須可從 /etc/fstab 指定和掛接資料磁碟機。
注意
使用 UUID=... 來指定 /etc/fstab 中的資料磁碟機,而不是指定區塊裝置名稱 (例如 /dev/sdb1)。 加密期間,VM 上的磁碟機順序會變更。 如果 VM 依賴特定的區塊裝置順序,則無法在加密後掛接裝置。
登出 SSH 工作階段。
若要加密 OS,請在啟用加密時將 volumeType 指定為 All 或 OS。
注意
未以
systemd
服務的形式執行的所有使用者空間程序皆應使用SIGKILL
來終止。 重新啟動 VM。 當您在執行中的 VM 上啟用 OS 磁碟加密時,請規劃 VM 停機時間。使用下一節的指示定期監視加密進度。
在 Get-AzVmDiskEncryptionStatus 顯示 "VMRestartPending" 之後,請登入 VM 或使用入口網站、PowerShell 或 CLI 來重新啟動 VM。
C:\> Get-AzVmDiskEncryptionStatus -ResourceGroupName $ResourceGroupName -VMName $VMName -ExtensionName $ExtensionName
OsVolumeEncrypted : VMRestartPending DataVolumesEncrypted : NotMounted OsVolumeEncryptionSettings : Microsoft.Azure.Management.Compute.Models.DiskEncryptionSettings ProgressMessage : OS disk successfully encrypted, reboot the VM
重新開機之前,我們建議您儲存 VM 的開機診斷。
監視 OS 加密進度
有三種方式可監視 OS 加密進度:
使用
Get-AzVmDiskEncryptionStatus
Cmdlet,並查看 ProgressMessage 欄位︰Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name
OsVolumeEncrypted : EncryptionInProgress DataVolumesEncrypted : NotMounted OsVolumeEncryptionSettings : Microsoft.Azure.Management.Compute.Models.DiskEncryptionSettings ProgressMessage : OS disk encryption started
在 VM 達到 "OS disk encryption started" 之後,需要在進階儲存體所支援的 VM 上耗費大約 40 至 50 分鐘。
由於 WALinuxAgent 發生 388 號問題,某些散發套件的
OsVolumeEncrypted
和DataVolumesEncrypted
會顯示為Unknown
。 若使用 WALinuxAgent 2.1.5 和更新版本,就會自動修正此問題。 如果您在輸出中看到Unknown
,可以使用 Azure 資源 Explorer 確認磁碟加密狀態。移至 Azure 資源 Explorer,然後在左側選取面板中展開這個階層︰
|-- subscriptions |-- [Your subscription] |-- resourceGroups |-- [Your resource group] |-- providers |-- Microsoft.Compute |-- virtualMachines |-- [Your virtual machine] |-- InstanceView
在 InstanceView 中,向下捲動以查看磁碟機的加密狀態。
查看開機診斷。 來自 ADE 擴充功能的訊息應該會在前面加上
[AzureDiskEncryption]
。透過 SSH 登入 VM,並從下列位置取得擴充功能記錄檔:
/var/log/azure/Microsoft.Azure.Security.AzureDiskEncryptionForLinux
不建議在 OS 加密進行時登入 VM。 其他兩個方法都失敗時,只複製記錄。
準備已預先加密的 Linux VHD
準備預先加密的 VHD 會根據散發套件而有所不同。 有關於準備 Ubuntu、openSUSE 和 CentOS 7 的範例可用。
在發佈安裝期間執行下列步驟以設定加密︰
在分割磁碟時選取 [設定加密的磁碟區]。
另外建立一個不得加密的開機磁碟機。 加密根磁碟機。
提供複雜密碼。 這是您上傳至金鑰保存庫的複雜密碼。
完成分割。
啟動 VM 時會要求輸入複雜密碼,請使用步驟 3 中提供的複雜密碼。
使用這些指示準備要上傳到 Azure 的 VM。 還不要執行最後一個步驟 (解除佈建 VM)。
設定加密來與 Azure 搭配運作,請執行下列步驟:
使用下列指令碼中的內容,在
/usr/local/sbin/azure_crypt_key.sh
建立檔案。 請注意 KeyFileName,因為它是 Azure 使用的複雜密碼檔案名稱。#!/bin/sh MountPoint=/tmp-keydisk-mount KeyFileName=LinuxPassPhraseFileName echo "Trying to get the key from disks ..." >&2 mkdir -p $MountPoint modprobe vfat >/dev/null 2>&1 modprobe ntfs >/dev/null 2>&1 sleep 2 OPENED=0 cd /sys/block for DEV in sd*; do echo "> Trying device: $DEV ..." >&2 mount -t vfat -r /dev/${DEV}1 $MountPoint >/dev/null|| mount -t ntfs -r /dev/${DEV}1 $MountPoint >/dev/null if [ -f $MountPoint/$KeyFileName ]; then cat $MountPoint/$KeyFileName umount $MountPoint 2>/dev/null OPENED=1 break fi umount $MountPoint 2>/dev/null done if [ $OPENED -eq 0 ]; then echo "FAILED to find suitable passphrase file ..." >&2 echo -n "Try to enter your password: " >&2 read -s -r A </dev/console echo -n "$A" else echo "Success loading keyfile!" >&2 fi
在 /etc/crypttab 中變更 crypt 設定。 其看起來應該如下:
xxx_crypt uuid=xxxxxxxxxxxxxxxxxxxxx none luks,discard,keyscript=/usr/local/sbin/azure_crypt_key.sh
在指令碼中新增可執行檔權限︰
sudo chmod +x /usr/local/sbin/azure_crypt_key.sh
透過附加行來編輯
/etc/initramfs-tools/modules
:vfat ntfs nls_cp437 nls_utf8 nls_iso8859-1
執行
update-initramfs -u -k all
來更新 initramfs,以讓keyscript
生效。現在您可以取消佈建 VM。
繼續進行下一個步驟,並將 VHD 上傳到 Azure。
上傳加密的 VHD 至 Azure 儲存體帳戶
在啟用 DM-Crypt 加密之後,需要將本機加密的 VHD 上傳至儲存體帳戶。
Add-AzVhd [-Destination] <Uri> [-LocalFilePath] <FileInfo> [[-NumberOfUploaderThreads] <Int32> ] [[-BaseImageUriToPatch] <Uri> ] [[-OverWrite]] [ <CommonParameters>]
將已預先加密 VM 的祕密上傳至金鑰保存庫
使用 Microsoft Entra 應用程式 (舊版) 加密時,先前取得的磁碟加密密碼必須上傳,作為金鑰保存庫中的密碼。 金鑰保存庫必須為 Microsoft Entra 用戶端啟用磁碟加密以及權限。
$AadClientId = "My-AAD-Client-Id"
$AadClientSecret = "My-AAD-Client-Secret"
$key vault = New-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -Location $Location
Set-AzKeyVaultAccessPolicy -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -ServicePrincipalName $AadClientId -PermissionsToKeys all -PermissionsToSecrets all
Set-AzKeyVaultAccessPolicy -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -EnabledForDiskEncryption
未使用 KEK 加密的磁碟加密密碼
若要在金鑰保存庫中設定密碼,請使用 Set-AzKeyVaultSecret。 複雜密碼會以 base64 字串編碼,然後上傳至金鑰保存庫。 此外,請確定在金鑰保存庫中建立密碼時會設定下列標籤。
# This is the passphrase that was provided for encryption during the distribution installation
$passphrase = "contoso-password"
$tags = @{"DiskEncryptionKeyEncryptionAlgorithm" = "RSA-OAEP"; "DiskEncryptionKeyFileName" = "LinuxPassPhraseFileName"}
$secretName = [guid]::NewGuid().ToString()
$secretValue = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($passphrase))
$secureSecretValue = ConvertTo-SecureString $secretValue -AsPlainText -Force
$secret = Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $secretName -SecretValue $secureSecretValue -tags $tags
$secretUrl = $secret.Id
在下一個步驟中使用 $secretUrl
,以便在不使用 KEK 的狀況下連接 OS 磁碟。
使用 KEK 加密的磁碟加密密碼
將密碼上傳至金鑰保存庫之前,您可以選擇性地使用金鑰加密金鑰來加密密碼。 使用包裝 API 先加密使用金鑰加密金鑰的密碼。 這個包裝作業的輸出是 base64 URL 編碼的字串,您可以接著使用 Set-AzKeyVaultSecret
Cmdlet 將它上傳做為密碼。
# This is the passphrase that was provided for encryption during the distribution installation
$passphrase = "contoso-password"
Add-AzKeyVaultKey -VaultName $KeyVaultName -Name "keyencryptionkey" -Destination Software
$KeyEncryptionKey = Get-AzKeyVaultKey -VaultName $KeyVault.OriginalVault.Name -Name "keyencryptionkey"
$apiversion = "2015-06-01"
##############################
# Get Auth URI
##############################
$uri = $KeyVault.VaultUri + "/keys"
$headers = @{}
$response = try { Invoke-RestMethod -Method GET -Uri $uri -Headers $headers } catch { $_.Exception.Response }
$authHeader = $response.Headers["www-authenticate"]
$authUri = [regex]::match($authHeader, 'authorization="(.*?)"').Groups[1].Value
Write-Host "Got Auth URI successfully"
##############################
# Get Auth Token
##############################
$uri = $authUri + "/oauth2/token"
$body = "grant_type=client_credentials"
$body += "&client_id=" + $AadClientId
$body += "&client_secret=" + [Uri]::EscapeDataString($AadClientSecret)
$body += "&resource=" + [Uri]::EscapeDataString("https://vault.azure.net")
$headers = @{}
$response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $body
$access_token = $response.access_token
Write-Host "Got Auth Token successfully"
##############################
# Get KEK info
##############################
$uri = $KeyEncryptionKey.Id + "?api-version=" + $apiversion
$headers = @{"Authorization" = "Bearer " + $access_token}
$response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers
$keyid = $response.key.kid
Write-Host "Got KEK info successfully"
##############################
# Encrypt passphrase using KEK
##############################
$passphraseB64 = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Passphrase))
$uri = $keyid + "/encrypt?api-version=" + $apiversion
$headers = @{"Authorization" = "Bearer " + $access_token; "Content-Type" = "application/json"}
$bodyObj = @{"alg" = "RSA-OAEP"; "value" = $passphraseB64}
$body = $bodyObj | ConvertTo-Json
$response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $body
$wrappedSecret = $response.value
Write-Host "Encrypted passphrase successfully"
##############################
# Store secret
##############################
$secretName = [guid]::NewGuid().ToString()
$uri = $KeyVault.VaultUri + "/secrets/" + $secretName + "?api-version=" + $apiversion
$secretAttributes = @{"enabled" = $true}
$secretTags = @{"DiskEncryptionKeyEncryptionAlgorithm" = "RSA-OAEP"; "DiskEncryptionKeyFileName" = "LinuxPassPhraseFileName"}
$headers = @{"Authorization" = "Bearer " + $access_token; "Content-Type" = "application/json"}
$bodyObj = @{"value" = $wrappedSecret; "attributes" = $secretAttributes; "tags" = $secretTags}
$body = $bodyObj | ConvertTo-Json
$response = Invoke-RestMethod -Method PUT -Uri $uri -Headers $headers -Body $body
Write-Host "Stored secret successfully"
$secretUrl = $response.id
在下一個步驟中使用 $KeyEncryptionKey
和 $secretUrl
,以便使用 KEK 連接 OS 磁碟。
連接 OS 磁碟時,指定密碼的 URL
不使用 KEK
當您在連接 OS 磁碟時,需要傳遞 $secretUrl
。 URL 已在「未使用 KEK 加密的磁碟加密密碼」一節中產生。
Set-AzVMOSDisk `
-VM $VirtualMachine `
-Name $OSDiskName `
-SourceImageUri $VhdUri `
-VhdUri $OSDiskUri `
-Linux `
-CreateOption FromImage `
-DiskEncryptionKeyVaultId $KeyVault.ResourceId `
-DiskEncryptionKeyUrl $SecretUrl
使用 KEK
連接 OS 磁碟時,傳遞 $KeyEncryptionKey
和 $secretUrl
。 URL 已在「使用 KEK 加密的磁碟加密祕密」一節中產生。
Set-AzVMOSDisk `
-VM $VirtualMachine `
-Name $OSDiskName `
-SourceImageUri $CopiedTemplateBlobUri `
-VhdUri $OSDiskUri `
-Linux `
-CreateOption FromImage `
-DiskEncryptionKeyVaultId $KeyVault.ResourceId `
-DiskEncryptionKeyUrl $SecretUrl `
-KeyEncryptionKeyVaultId $KeyVault.ResourceId `
-KeyEncryptionKeyURL $KeyEncryptionKey.Id