Zelfstudie: Azure Remote Rendering en modelopslag beveiligen

In deze zelfstudie leert u het volgende:

  • Azure Blob Storage met Azure Remote Rendering-modellen beveiligen
  • Verifiëren met Microsoft Entra-id voor toegang tot uw Azure Remote Rendering-exemplaar
  • Azure-referenties gebruiken voor Azure Remote Rendering-verificatie

Vereisten

Waarom extra beveiliging nodig is

De huidige status van de toepassing en de toegang tot uw Azure-resources ziet er als volgt uit:

Initial security

Zowel met "AccountID + AccountKey" als de "URL + SAS Token" worden een gebruikersnaam en wachtwoord in feite samen opgeslagen. Als bijvoorbeeld "AccountID + AccountKey" zichtbaar is, zou een kwaadwillende gebruiker uw ARR-bronnen kunnen gebruiken zonder uw toestemming.

Uw inhoud beveiligen in Azure Blob Storage

Met Azure Remote Rendering kunt u veilig toegang krijgen tot de inhoud van uw Azure Blob Storage met de juiste configuratie. Zie Instructies: Koppel opslagaccounts om uw Azure Remote Rendering-exemplaar te configureren met uw blob-opslagaccounts.

Wanneer u een gekoppelde blobopslag gebruikt, gebruikt u iets andere methoden voor het laden van modellen:

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

In de bovenstaande regels wordt de FromSas-versie van de parameters en sessieactie gebruikt. Deze moeten worden geconverteerd naar de niet-SAS-versies:

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

We gaan RemoteRenderingCoordinator wijzigen om een aangepast model te laden uit een gekoppeld Blob Storage-account.

  1. Als u dat nog niet hebt gedaan, voltooit u de procedure: Koppel opslagaccounts om uw ARR-exemplaar toestemming te geven voor toegang tot uw Blob Storage-exemplaar.

  2. Voeg de volgende gewijzigde LoadModel-methode toe aan RemoteRenderingCoordinator, net onder de huidige LoadModel-methode:

    /// <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;
    }
    

    Deze code is identiek aan de oorspronkelijke LoadModel methode, maar we hebben de SAS-versie van de methode-aanroepen vervangen door de niet-SAS-versies.

    De extra invoer storageAccountName en blobName zijn ook toegevoegd aan de argumenten. We noemen deze nieuwe LoadModel-methode van een andere methode die vergelijkbaar is met de eerste LoadTestModel-methode die we in de eerste zelfstudie hebben gemaakt.

  3. Voeg de volgende methode toe aan RemoteRenderingCoordinator, net na 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)}%"));
    }
    

    Met deze code worden drie extra tekenreeksvariabelen toegevoegd aan het onderdeel RemoteRenderingCoordinator . Screenshot that highlights the Storage Account Name, Blob Container Name, and Model Path of the RemoteRenderingCoordinator component.

  4. Voeg uw waarden toe aan het onderdeel RemoteRenderingCoordinator. Als u de quickstart voor modelconversie hebt uitgevoerd, hebt u nu de volgende waarden:

    • Naam van opslagaccount: de naam van uw opslagaccount, de wereldwijd unieke naam die u kiest voor uw opslagaccount. In de quickstart is dit arrtutorialstorage, uw waarde is anders.
    • Naam van de blobcontainer: arroutput, de Blob Storage-container
    • Modelpad: de combinatie van de outputFolderPath en de outputAssetFileName die is gedefinieerd in het bestand arrconfig.json . In de quickstart was dit "outputFolderPath":"converted/robot", "outputAssetFileName": "robot.arrAsset". Wat zou resulteren in een modelpadwaarde van 'geconverteerd/robot/robot.arrAsset', uw waarde is anders.

    Tip

    Als u het script Conversion.ps1 uitvoert, zonder het argument "-UseContainerSas", worden alle bovenstaande waarden voor u uitgevoerd in plaats van het SAS-token. Linked Model

  5. U kunt het GameObject TestModel voorlopig verwijderen of uitschakelen, om ruimte te maken voor het aangepaste model dat u wilt laden.

  6. Speel de scène af en maak verbinding met een externe sessie.

  7. Open het contextmenu op RemoteRenderingCoordinator en selecteer Gekoppeld aangepast model laden. Load linked model

Deze stappen hebben de beveiliging van de toepassing verhoogd door het SAS-token uit de lokale toepassing te verwijderen.

De huidige status van de toepassing en de toegang tot uw Azure-resources ziet er nu als volgt uit:

Better security

We hebben nog een 'wachtwoord', AccountKey (accountsleutel), dat uit de lokale toepassing moet worden verwijderd. Dit kan worden gedaan met behulp van Microsoft Entra-verificatie.

Microsoft Entra-verificatie

Met Microsoft Entra-verificatie kunt u bepalen welke personen of groepen ARR op een meer gecontroleerde manier gebruiken. ARR bevat ingebouwde ondersteuning voor het accepteren van toegangstokens in plaats van een accountsleutel te gebruiken. U kunt toegangstokens beschouwen als een tijdgebonden, gebruikersspecifieke sleutel, waarmee alleen bepaalde onderdelen van de specifieke resource worden ontgrendeld waarvoor deze zijn aangevraagd.

Het Script RemoteRenderingCoordinator heeft een gemachtigde met de naam ARRCredentialGetter, die een methode bevat die een SessionConfiguration-object retourneert , dat wordt gebruikt om het beheer van externe sessies te configureren. We kunnen een andere methode toewijzen aan ARRCredentialGetter, zodat we een Azure-aanmeldingsstroom kunnen gebruiken en een SessionConfiguration-object kunnen genereren dat een Azure Access Token bevat. Dit toegangstoken is uniek voor de gebruiker die zich aanmeldt.

  1. Volg de procedure : Verificatie configureren - Verificatie voor geïmplementeerde toepassingen, waarbij u een nieuwe Microsoft Entra-toepassing registreert en toegang tot uw ARR-exemplaar configureert.

  2. Nadat u de nieuwe Microsoft Entra-toepassing hebt geconfigureerd, controleert u of uw Microsoft Entra-toepassing eruitziet als de volgende afbeeldingen:

    Microsoft Entra-toepassing -> VerificatieApp authentication

    Microsoft Entra-toepassing - API-machtigingen>App APIs

  3. Nadat u uw Remote Rendering-account hebt geconfigureerd, controleert u of uw configuratie eruitziet zoals in de volgende afbeelding:

    ARR -> AccessControl (IAM)ARR Role

    Notitie

    Een rol van Eigenaar is niet voldoende om sessies te kunnen beheren via de clienttoepassing. Aan elke gebruiker die u de mogelijkheid wilt geven om sessies te beheren, moet u de rol van Remote Rendering-client toewijzen. Aan elke gebruiker die u de mogelijkheid wilt geven om sessies te beheren en modellen te converteren, moet u de rol van Remote Rendering-beheerder toewijzen.

Nu de Azure-kant van zaken zijn geïmplementeerd, moeten we wijzigen hoe uw code verbinding maakt met de ARR-service. Dit doen we door een exemplaar van BaseARRAuthentication te implementeren, dat een nieuw SessionConfiguration-object retourneert. In dit geval worden de accountgegevens geconfigureerd met het Azure-toegangstoken.

  1. Maak een nieuw script met de naam AADAuthentication en vervang de code door het volgende:

    // 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;
        }
    }
    

Notitie

Deze code is verre van compleet en is niet geschikt voor commercieel gebruik. U wilt gebruikers waarschijnlijk ook ten minste de mogelijkheid geven om zich af te melden. U kunt dit doen met behulp van de Task RemoveAsync(IAccount account)-methode van de clienttoepassing. Deze code is alleen bedoeld voor gebruik in een zelfstudie. Uw implementatie is specifiek voor uw toepassing.

Met de code wordt eerst geprobeerd het token op de achtergrond op te halen met behulp van AquireTokenSilent. Dit is gelukt als de gebruiker deze toepassing eerder heeft geverifieerd. Als het niet lukt, moet u een methode gebruiken waarbij meer interactie van de gebruiker vereist is.

Voor deze code gebruiken we de apparaatcodestroom om een toegangstoken te verkrijgen. Met deze stroom kunnen gebruikers zich op een computer of mobiel apparaat aanmelden bij hun Azure-account en moet het resulterende token worden teruggestuurd naar de toepassing HoloLens.

Het belangrijkste deel van deze klasse vanuit het perspectief van ARR is deze regel:

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

Hier maken we een nieuw SessionConfiguration-object met behulp van het remote rendering-domein, account-id, accountdomein en toegangstoken. Dit token wordt vervolgens gebruikt door de ARR-service om Remote Rendering-sessies op te vragen, te maken en samen te voegen zolang de gebruiker is geautoriseerd op basis van de op rollen gebaseerde machtigingen die eerder zijn geconfigureerd.

Na deze wijziging ziet de huidige status van de toepassing en de toegang tot uw Azure-resources er als volgt uit:

Even better security

Omdat de gebruikersreferenties niet zijn opgeslagen op het apparaat (of in dit geval zelfs op het apparaat zijn ingevoerd), is het blootstellingsrisico laag. Het apparaat gebruikt nu een gebruikersspecifiek, tijdgebonden toegangstoken voor toegang tot ARR, dat gebruikmaakt van toegangsbeheer (IAM) om toegang te krijgen tot Blob Storage. Deze twee stappen hebben de 'wachtwoorden' uit de broncode verwijderd en de beveiliging aanzienlijk verbeterd. Dit is echter niet de beste beveiliging. De beveiliging kan nog verder worden verbeterd door het model- en sessiebeheer naar een webservice te verplaatsen. In het hoofdstuk Commerciële gereedheid worden extra beveiligingsoverwegingen besproken.

Microsoft Entra-verificatie testen

Wanneer Microsoft Entra-verificatie actief is in de Unity-editor, moet u zich telkens verifiëren wanneer u de toepassing start. Op het apparaat vindt de verificatiestap de eerste keer plaats en is deze alleen opnieuw vereist wanneer het token verloopt of ongeldig is.

  1. Voeg het Microsoft Entra-verificatieonderdeel toe aan het RemoteRenderingCoordinator GameObject.

    Microsoft Entra auth component

Notitie

Als u het voltooide project uit de opslagplaats met ARR-voorbeelden gebruikt, moet u het Microsoft Entra-verificatieonderdeel inschakelen door op het selectievakje naast de titel te klikken.

  1. Vul uw waarden voor de client-id en de tenant-id in. Deze waarden vindt u op de overzichtspagina van de app-registratie:

    • Active Directory-toepassingsclient-id is de toepassings-id (client) die is gevonden in de registratie van uw Microsoft Entra-app (zie de onderstaande afbeelding).
    • Azure Tenant ID is de directory-id (tenant) die is gevonden in de registratie van uw Microsoft Entra-app (zie de onderstaande afbeelding).
    • Azure Remote Rendering-domein is hetzelfde domein dat u hebt gebruikt in het RemoteRenderingCoordinator-domein van Remote Rendering.
    • De account-id van Azure Remote Rendering is de account-id die u hebt gebruikt voor RemoteRenderingCoordinator.
    • Azure Remote Rendering-accountdomein is hetzelfde accountdomein dat u hebt gebruikt in de RemoteRenderingCoordinator.

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

  2. Druk op Play (Afspelen) in de Unity-editor en verleen toestemming voor het uitvoeren van een sessie. Omdat het Microsoft Entra-verificatieonderdeel een weergavecontroller heeft, wordt het automatisch gekoppeld om een prompt weer te geven na het modale paneel voor sessieautorisatie.

  3. Volg de instructies in het scherm rechts van AppMenu. U ziet er ongeveer als volgt uit: Illustration that shows the instruction panel that appears to the right of the AppMenu.

    Nadat u de opgegeven code op uw secundaire apparaat (of browser op hetzelfde apparaat) hebt ingevoerd en zich hebt aangemeld met uw referenties, wordt er een toegangstoken geretourneerd naar de aanvragende toepassing, in dit geval de Unity Editor.

Hierna moet alles in de toepassing verder normaal worden uitgevoerd. Controleer de Unity-console op fouten als u de fasen niet op de verwachte wijze doorloopt.

Bouwen naar apparaat

Als u een toepassing bouwt met BEHULP van MSAL naar het apparaat, moet u een bestand opnemen in de map Assets van uw project. Dit helpt de compiler de toepassing correct te compileren met behulp van de Microsoft.Identity.Client.dll die is opgenomen in de zelfstudieassets.

  1. Een nieuwe bestand aan Assets toevoegen met de naam link.xml

  2. Voeg het volgende toe aan het bestand:

    <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. De wijzigingen opslaan

Volg de stappen in quickstart: Unity-voorbeeld implementeren in HoloLens - Het voorbeeldproject bouwen om te bouwen naar HoloLens.

Volgende stappen

De rest van deze zelfstudieset bevat conceptuele artikelen voor het maken van een toepassing die gereed is voor productie die gebruikmaakt van Azure Remote Rendering.