Kurz: Zabezpečení služby Azure Remote Rendering a úložiště modelů

V tomto kurzu se naučíte:

  • Zabezpečení služby Azure Blob Storage obsahující modely Azure Remote Rendering
  • Ověření pomocí Microsoft Entra ID pro přístup k instanci Azure Remote Renderingu
  • Použití přihlašovacích údajů Azure pro ověřování Azure Remote Rendering

Požadavky

Proč je potřeba další zabezpečení

Aktuální stav aplikace a jeho přístup k prostředkům Azure vypadá takto:

Initial security

Id účtu a klíč účtu i token SAS jsou v podstatě uloženy společně uživatelské jméno a heslo. Pokud by například došlo k zveřejnění "AccountID + AccountKey", bylo by pro útočníka triviální používat vaše prostředky ARR bez vašeho oprávnění na vaše výdaje.

Zabezpečení obsahu ve službě Azure Blob Storage

Azure Remote Rendering má zabezpečený přístup k obsahu služby Azure Blob Storage se správnou konfigurací. Viz Postupy: Propojení účtů úložiště a konfigurace instance Azure Remote Renderingu s účty úložiště objektů blob

Při použití propojeného úložiště objektů blob používáte pro načítání modelů mírně odlišné metody:

var loadModelParams = new LoadModelFromSasOptions(modelPath, modelEntity);
var task = ARRSessionService.CurrentActiveSession.Connection.LoadModelFromSasAsync(loadModelParams);

Výše uvedené řádky používají FromSas verzi parametrů a akce relace. Musí být převedeny na jiné verze než SAS:

var loadModelParams = LoadModelOptions.CreateForBlobStorage(storageAccountPath, blobName, modelPath, modelEntity);
var task = ARRSessionService.CurrentActiveSession.Connection.LoadModelAsync(loadModelParams);

Pojďme upravit RemoteRenderingCoordinator tak, aby načítá vlastní model z propojeného účtu úložiště objektů blob.

  1. Pokud jste to ještě neudělali, dokončete postupy : Propojte účty úložiště a udělte instanci ARR oprávnění pro přístup k instanci Blob Storage.

  2. Přidejte následující upravenou metodu LoadModel do RemoteRenderingCoordinator těsně pod aktuální metodu LoadModel :

    /// <summary>
    /// Loads a model from blob storage that has been linked to the ARR instance
    /// </summary>
    /// <param name="storageAccountName">The storage account name, this contains the blob containers </param>
    /// <param name="blobName">The blob container name, i.e. arroutput</param>
    /// <param name="modelPath">The relative path inside the container to the model, i.e. test/MyCustomModel.arrAsset</param>
    /// <param name="parent">The parent Transform for this remote entity</param>
    /// <param name="progress">A call back method that accepts a float progress value [0->1]</param>
    /// <returns></returns>
    public async Task<Entity> LoadModel(string storageAccountName, string blobName, string modelPath, UnityEngine.Transform parent = null, Action<float> progress = null)
    {
        //Create a root object to parent a loaded model to
        var modelEntity = ARRSessionService.CurrentActiveSession.Connection.CreateEntity();
    
        //Get the game object representation of this entity
        var modelGameObject = modelEntity.GetOrCreateGameObject(UnityCreationMode.DoNotCreateUnityComponents);
    
        //Ensure the entity will sync its transform with the server
        var sync = modelGameObject.GetComponent<RemoteEntitySyncObject>();
        sync.SyncEveryFrame = true;
    
        //Parent the new object under the defined parent
        if (parent != null)
        {
            modelGameObject.transform.SetParent(parent, false);
            modelGameObject.name = parent.name + "_Entity";
        }
    
        //Load a model that will be parented to the entity
        var loadModelParams = LoadModelOptions.CreateForBlobStorage($"{storageAccountName}.blob.core.windows.net", blobName, modelPath, modelEntity);
        var loadModelAsync = ARRSessionService.CurrentActiveSession.Connection.LoadModelAsync(loadModelParams, progress);
        var result = await loadModelAsync;
        return modelEntity;
    }
    

    Tento kód je identický s původní LoadModel metodou, ale nahradili jsme verzi SAS volání metody jinými verzemi než SAS.

    Další vstupy storageAccountName a blobName byly také přidány do argumentů. Tuto novou metodu LoadModel voláme z jiné metody podobné první metodě LoadTestModel , kterou jsme vytvořili v prvním kurzu.

  3. Přidejte následující metodu do RemoteRenderingCoordinator těsně za LoadTestModel

    private bool loadingLinkedCustomModel = false;
    
    [SerializeField]
    private string storageAccountName;
    public string StorageAccountName {
        get => storageAccountName.Trim();
        set => storageAccountName = value;
    }
    
    [SerializeField]
    private string blobContainerName;
    public string BlobContainerName {
        get => blobContainerName.Trim();
        set => blobContainerName = value;
    }
    
    [SerializeField]
    private string modelPath;
    public string ModelPath {
        get => modelPath.Trim();
        set => modelPath = value;
    }
    
    [ContextMenu("Load Linked Custom Model")]
    public async void LoadLinkedCustomModel()
    {
        if (CurrentCoordinatorState != RemoteRenderingState.RuntimeConnected)
        {
            Debug.LogError("Please wait for the runtime to connect before loading the test model. Try again later.");
            return;
        }
        if (loadingLinkedCustomModel)
        {
            Debug.Log("Linked Test model already loading or loaded!");
            return;
        }
        loadingLinkedCustomModel = true;
    
        // Create a parent object to use for positioning
        GameObject testParent = new GameObject("LinkedCustomModel");
        testParent.transform.position = new Vector3(0f, 0f, 3f);
    
        await LoadModel(StorageAccountName, BlobContainerName, ModelPath, testParent.transform, (progressValue) => Debug.Log($"Loading Test Model progress: {Math.Round(progressValue * 100, 2)}%"));
    }
    

    Tento kód přidá do komponenty RemoteRenderingCoordinator tři další řetězcové proměnné. Screenshot that highlights the Storage Account Name, Blob Container Name, and Model Path of the RemoteRenderingCoordinator component.

  4. Přidejte hodnoty do komponenty RemoteRenderingCoordinator . Po provedení rychlého startu pro převod modelu by vaše hodnoty měly být:

    • Název účtu úložiště: Název vašeho účtu úložiště, globálně jedinečný název, který zvolíte pro svůj účet úložiště. V rychlém startu to bylo arrtutorialstorage, vaše hodnota se liší.
    • Název kontejneru objektů blob: arroutput, kontejner úložiště objektů blob
    • Cesta modelu: Kombinace outputFolderPath a outputAssetFileName definované v souboru arrconfig.json . V rychlém startu to byl outputFolderPath:"converted/robot", "outputAssetFileName": "robot.arrAsset". Výsledkem by byla hodnota cesty modelu "converted/robot/robot.arrAsset", vaše hodnota se liší.

    Tip

    Pokud spustíte skript Conversion.ps1 bez argumentu -UseContainerSas, skript vypíše všechny výše uvedené hodnoty pro váš místo token SAS. Linked Model

  5. Prozatím odeberte nebo zakažte model GameObject TestModel, aby se načetl prostor pro vlastní model.

  6. Přehrajte scénu a připojte se ke vzdálené relaci.

  7. Otevřete místní nabídku na RemoteRenderingCoordinator a vyberte Načíst propojený vlastní model. Load linked model

Tyto kroky zvýšily zabezpečení aplikace odebráním tokenu SAS z místní aplikace.

Aktuální stav aplikace a jeho přístup k prostředkům Azure teď vypadá takto:

Better security

Máme ještě jedno "heslo", AccountKey, které se má odebrat z místní aplikace. To lze provést pomocí ověřování Microsoft Entra.

Ověřování Microsoft Entra

Ověřování Microsoft Entra umožňuje určit, kteří jednotlivci nebo skupiny používají ARR více kontrolovaným způsobem. ARR má integrovanou podporu pro přijímání přístupových tokenů místo použití klíče účtu. Přístupové tokeny si můžete představit jako časově omezený klíč specifický pro uživatele, který odemyká jenom určité části konkrétního prostředku, pro který byl požadován.

Vzdálený skript RemoteRenderingCoordinator má delegáta s názvem ARRCredentialGetter, který obsahuje metodu, která vrací objekt SessionConfiguration, který se používá ke konfiguraci vzdálené správy relací. K ARRCredentialGetter můžeme přiřadit jinou metodu, což nám umožní použít tok přihlášení k Azure a vygenerovat objekt SessionConfiguration, který obsahuje přístupový token Azure. Tento přístupový token bude specifický pro uživatele, který se přihlašuje.

  1. Postupujte podle postupu: Konfigurace ověřování – Ověřování pro nasazené aplikace, které zahrnuje registraci nové aplikace Microsoft Entra a konfiguraci přístupu k vaší instanci ARR.

  2. Po nakonfigurování nové aplikace Microsoft Entra zkontrolujte, jestli vaše aplikace Microsoft Entra vypadá takto:

    Aplikace Microsoft Entra –> OvěřováníApp authentication

    Aplikace Microsoft Entra –> Oprávnění rozhraní APIApp APIs

  3. Po konfiguraci účtu vzdáleného vykreslování zkontrolujte, jestli konfigurace vypadá jako na následujícím obrázku:

    ARR –> AccessControl (IAM)ARR Role

    Poznámka:

    Role Vlastník nestačí ke správě relací prostřednictvím klientské aplikace. Pro každého uživatele, kterého chcete udělit možnost spravovat relace, musíte poskytnout roli Klienta vzdáleného vykreslování. Pro každého uživatele, kterého chcete spravovat relace a převádět modely, musíte poskytnout roli vzdáleného vykreslování Správa istrator.

Na straně Azure teď potřebujeme upravit způsob, jakým se váš kód připojuje ke službě ARR. Provedeme to implementací instance BaseARRAuthentication, která vrátí nový objekt SessionConfiguration . V tomto případě jsou informace o účtu nakonfigurované pomocí přístupového tokenu Azure.

  1. Vytvořte nový skript s názvem AADAuthentication a nahraďte jeho kód následujícím kódem:

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License. See LICENSE in the project root for license information.
    
    using Microsoft.Azure.RemoteRendering;
    using Microsoft.Identity.Client;
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using UnityEngine;
    
    public class AADAuthentication : BaseARRAuthentication
    {
        [SerializeField]
        private string activeDirectoryApplicationClientID;
        public string ActiveDirectoryApplicationClientID
        {
            get => activeDirectoryApplicationClientID.Trim();
            set => activeDirectoryApplicationClientID = value;
        }
    
        [SerializeField]
        private string azureTenantID;
        public string AzureTenantID
        {
            get => azureTenantID.Trim();
            set => azureTenantID = value;
        }
    
        [SerializeField]
        private string azureRemoteRenderingDomain;
        public string AzureRemoteRenderingDomain
        {
            get => azureRemoteRenderingDomain.Trim();
            set => azureRemoteRenderingDomain = value;
        }
    
        [SerializeField]
        private string azureRemoteRenderingAccountID;
        public string AzureRemoteRenderingAccountID
        {
            get => azureRemoteRenderingAccountID.Trim();
            set => azureRemoteRenderingAccountID = value;
        }
    
        [SerializeField]
        private string azureRemoteRenderingAccountDomain;
        public string AzureRemoteRenderingAccountDomain
        {
            get => azureRemoteRenderingAccountDomain.Trim();
            set => azureRemoteRenderingAccountDomain = value;
        }    
    
        public override event Action<string> AuthenticationInstructions;
    
        string authority => "https://login.microsoftonline.com/" + AzureTenantID;
    
        string redirect_uri = "https://login.microsoftonline.com/common/oauth2/nativeclient";
    
        string[] scopes => new string[] { "https://sts.mixedreality.azure.com//.default" };
    
        public void OnEnable()
        {
            RemoteRenderingCoordinator.ARRCredentialGetter = GetARRCredentials;
            this.gameObject.AddComponent<ExecuteOnUnityThread>();
        }
    
        public async override Task<SessionConfiguration> GetARRCredentials()
        {
            var result = await TryLogin();
            if (result != null)
            {
                Debug.Log("Account signin successful " + result.Account.Username);
    
                var AD_Token = result.AccessToken;
    
                return await Task.FromResult(new SessionConfiguration(AzureRemoteRenderingAccountDomain, AzureRemoteRenderingDomain, AzureRemoteRenderingAccountID, "", AD_Token, ""));
            }
            else
            {
                Debug.LogError("Error logging in");
            }
            return default;
        }
    
        private Task DeviceCodeReturned(DeviceCodeResult deviceCodeDetails)
        {
            //Since everything in this task can happen on a different thread, invoke responses on the main Unity thread
            ExecuteOnUnityThread.Enqueue(() =>
            {
                // Display instructions to the user for how to authenticate in the browser
                Debug.Log(deviceCodeDetails.Message);
                AuthenticationInstructions?.Invoke(deviceCodeDetails.Message);
            });
    
            return Task.FromResult(0);
        }
    
        public override async Task<AuthenticationResult> TryLogin()
        {
            var clientApplication = PublicClientApplicationBuilder.Create(ActiveDirectoryApplicationClientID).WithAuthority(authority).WithRedirectUri(redirect_uri).Build();
            AuthenticationResult result = null;
            try
            {
                var accounts = await clientApplication.GetAccountsAsync();
    
                if (accounts.Any())
                {
                    result = await clientApplication.AcquireTokenSilent(scopes, accounts.First()).ExecuteAsync();
    
                    return result;
                }
                else
                {
                    try
                    {
                        result = await clientApplication.AcquireTokenWithDeviceCode(scopes, DeviceCodeReturned).ExecuteAsync(CancellationToken.None);
                        return result;
                    }
                    catch (MsalUiRequiredException ex)
                    {
                        Debug.LogError("MsalUiRequiredException");
                        Debug.LogException(ex);
                    }
                    catch (MsalServiceException ex)
                    {
                        Debug.LogError("MsalServiceException");
                        Debug.LogException(ex);
                    }
                    catch (MsalClientException ex)
                    {
                        Debug.LogError("MsalClientException");
                        Debug.LogException(ex);
                        // Mitigation: Use interactive authentication
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError("Exception");
                        Debug.LogException(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.LogError("GetAccountsAsync");
                Debug.LogException(ex);
            }
    
            return null;
        }
    }
    

Poznámka:

Tento kód není žádným způsobem úplný a není připravený pro komerční aplikaci. Například minimálně budete pravděpodobně chtít přidat i možnost odhlášení. To lze provést pomocí Task RemoveAsync(IAccount account) metody poskytované klientskou aplikací. Tento kód je určený pouze pro použití kurzu, vaše implementace bude specifická pro vaši aplikaci.

Kód se nejprve pokusí získat token bezobslužně pomocí AquireTokenSilent. To je úspěšné, pokud uživatel tuto aplikaci již dříve ověřil. Pokud to není úspěšné, přejděte na strategii, která se týká více uživatelů.

Pro tento kód používáme tok kódu zařízení k získání přístupového tokenu. Tento tok umožňuje uživateli přihlásit se ke svému účtu Azure na počítači nebo mobilním zařízení a nechat výsledný token odesílaný zpět do aplikace HoloLens.

Nejdůležitější částí této třídy z pohledu ARR je tento řádek:

return await Task.FromResult(new SessionConfiguration(AzureRemoteRenderingAccountDomain, AzureRemoteRenderingDomain, AzureRemoteRenderingAccountID, "", AD_Token, ""));

Tady vytvoříme nový objekt SessionConfiguration pomocí domény vzdáleného vykreslování, ID účtu, domény účtu a přístupového tokenu. Tento token pak služba ARR používá k dotazování, vytváření a připojování relací vzdáleného vykreslování, pokud je uživatel autorizovaný na základě dříve nakonfigurovaných oprávnění na základě rolí.

Při této změně vypadá aktuální stav aplikace a jeho přístup k prostředkům Azure takto:

Even better security

Vzhledem k tomu, že přihlašovací údaje uživatele nejsou uložené na zařízení (nebo v tomto případě dokonce zadané na zařízení), riziko jejich expozice je nízké. Zařízení teď pro přístup k ARR používá přístupový token specifický pro uživatele, který pro přístup ke službě Blob Storage používá řízení přístupu (IAM). Tyto dva kroky odebraly hesla ze zdrojového kódu a výrazně zvýšily zabezpečení. Nejedná se ale o zabezpečení, které je k dispozici, přesun modelu a správy relací do webové služby zlepší zabezpečení ještě více. Další aspekty zabezpečení jsou popsány v kapitole Komerční připravenost .

Testování ověřování Microsoft Entra

Když je v Unity Editoru aktivní ověřování Microsoft Entra, musíte se ověřit při každém spuštění aplikace. Na zařízení se krok ověřování provede poprvé a bude se vyžadovat znovu, jenom když vyprší platnost tokenu nebo je neplatný.

  1. Přidejte komponentu ověřování Microsoft Entra do objektu GameObject RemoteRenderingCoordinator .

    Microsoft Entra auth component

Poznámka:

Pokud používáte dokončený projekt z úložiště ukázek ARR, nezapomeňte povolit ověřovací komponentu Microsoft Entra kliknutím na zaškrtávací políčko vedle jeho názvu.

  1. Vyplňte hodnoty PRO ID klienta a ID tenanta. Tyto hodnoty najdete na stránce přehledu vaší registrace aplikace:

    • ID klienta aplikace služby Active Directory je ID aplikace (klienta) nalezené v registraci aplikace Microsoft Entra (viz obrázek níže).
    • ID tenanta Azure je ID adresáře (tenanta) nalezené v registraci vaší aplikace Microsoft Entra (viz obrázek níže).
    • Doména vzdáleného vykreslování Azure je stejná doména, kterou jste používali v doméně vzdáleného vykreslování RemoteRenderingCoordinator.
    • ID účtu služby Azure Remote Rendering je stejné ID účtu, které jste používali pro RemoteRenderingCoordinator.
    • Doména účtu Azure Remote Rendering je stejná doména účtu, kterou jste používali v RemoteRenderingCoordinatoru.

    Screenshot that highlights the Application (client) ID and Directory (tenant) ID.

  2. Stiskněte Tlačítko Přehrát v Unity Editoru a odsouhlaste spuštění relace. Vzhledem k tomu, že součást ověřování Microsoft Entra má kontroler zobrazení, je automaticky připojena k zobrazení výzvy po modálním panelu autorizace relace.

  3. Postupujte podle pokynů na panelu napravo od AppMenu. Mělo by se zobrazit něco podobného: Illustration that shows the instruction panel that appears to the right of the AppMenu.

    Po zadání zadaného kódu na sekundárním zařízení (nebo prohlížeči na stejném zařízení) a přihlášení pomocí přihlašovacích údajů se přístupový token vrátí do žádající aplikace, v tomto případě Unity Editor.

Po tomto bodu by všechno v aplikaci mělo normálně pokračovat. Pokud neprobíhá fáze podle očekávání, zkontrolujte, jestli konzola Unity neobsahuje žádné chyby.

Sestavení do zařízení

Pokud vytváříte aplikaci pomocí knihovny MSAL na zařízení, musíte do složky Assets projektu zahrnout soubor. To pomáhá kompilátoru správně sestavit aplikaci pomocí knihovny Microsoft.Identity.Client.dll zahrnuté v prostředcích kurzu.

  1. Přidání nového souboru do assetů s názvem link.xml

  2. Do souboru přidejte následující kód:

    <linker>
        <assembly fullname="Microsoft.Identity.Client" preserve="all"/>
        <assembly fullname="System.Runtime.Serialization" preserve="all"/>
        <assembly fullname="System.Core">
            <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
        </assembly>
    </linker>
    
  3. Uložení změn

Postupujte podle kroků uvedených v rychlém startu: Nasazení ukázky Unity do HoloLensu – sestavení ukázkového projektu pro sestavení do HoloLensu.

Další kroky

Zbývající část této sady kurzů obsahuje koncepční články pro vytvoření aplikace připravené pro produkční prostředí, která používá Azure Remote Rendering.