適用於 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

加密或解密具有 Microsoft Entra 應用程式 (舊版) 的 VM

在執行中的 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 一次。

步驟

  1. 使用先前指定的其中一個散發套件來建立 VM。

  2. 根據您的需求設定 VM。 如果您要加密所有 (OS + 資料) 磁碟機,必須可從 /etc/fstab 指定和掛接資料磁碟機。

    注意

    使用 UUID=... 來指定 /etc/fstab 中的資料磁碟機,而不是指定區塊裝置名稱 (例如 /dev/sdb1)。 加密期間,VM 上的磁碟機順序會變更。 如果 VM 依賴特定的區塊裝置順序,則無法在加密後掛接裝置。

  3. 登出 SSH 工作階段。

  4. 若要加密 OS,請在啟用加密時將 volumeType 指定為 AllOS

    注意

    未以 systemd 服務的形式執行的所有使用者空間程序皆應使用 SIGKILL 來終止。 重新啟動 VM。 當您在執行中的 VM 上啟用 OS 磁碟加密時,請規劃 VM 停機時間。

  5. 使用下一節的指示定期監視加密進度。

  6. 在 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 號問題,某些散發套件的 OsVolumeEncryptedDataVolumesEncrypted 會顯示為 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 中,向下捲動以查看磁碟機的加密狀態。

    VM 實例檢視

  • 查看開機診斷。 來自 ADE 擴充功能的訊息應該會在前面加上 [AzureDiskEncryption]

  • 透過 SSH 登入 VM,並從下列位置取得擴充功能記錄檔:

    /var/log/azure/Microsoft.Azure.Security.AzureDiskEncryptionForLinux

    不建議在 OS 加密進行時登入 VM。 其他兩個方法都失敗時,只複製記錄。

準備已預先加密的 Linux VHD

準備預先加密的 VHD 會根據散發套件而有所不同。 有關於準備 Ubuntu、openSUSE 和 CentOS 7 的範例可用。

在發佈安裝期間執行下列步驟以設定加密︰

  1. 在分割磁碟時選取 [設定加密的磁碟區]

    Ubuntu 16.04 安裝程式 - 設定加密的磁碟區

  2. 另外建立一個不得加密的開機磁碟機。 加密根磁碟機。

    Ubuntu 16.04 安裝程式 - 選取要加密的裝置

  3. 提供複雜密碼。 這是您上傳至金鑰保存庫的複雜密碼。

    Ubuntu 16.04 安裝程式 - 提供複雜密碼

  4. 完成分割。

    Ubuntu 16.04 安裝程式 - 完成數據分割

  5. 啟動 VM 時會要求輸入複雜密碼,請使用步驟 3 中提供的複雜密碼。

    Ubuntu 16.04 安裝程式 - 在開機時提供複雜密碼

  6. 使用這些指示準備要上傳到 Azure 的 VM。 還不要執行最後一個步驟 (解除佈建 VM)。

設定加密來與 Azure 搭配運作,請執行下列步驟:

  1. 使用下列指令碼中的內容,在 /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
    
  2. /etc/crypttab 中變更 crypt 設定。 其看起來應該如下:

     xxx_crypt uuid=xxxxxxxxxxxxxxxxxxxxx none luks,discard,keyscript=/usr/local/sbin/azure_crypt_key.sh
    
  3. 在指令碼中新增可執行檔權限︰

     sudo chmod +x /usr/local/sbin/azure_crypt_key.sh
    
  4. 透過附加行來編輯 /etc/initramfs-tools/modules

     vfat
     ntfs
     nls_cp437
     nls_utf8
     nls_iso8859-1
    
  5. 執行 update-initramfs -u -k all 來更新 initramfs,以讓 keyscript 生效。

  6. 現在您可以取消佈建 VM。

    Ubuntu 16.04 安裝程式 - update-initramfs

  7. 繼續進行下一個步驟,並將 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