diff --git a/change/@azure-msal-node-5483cdd9-b571-4018-b9fd-cc85a982c443.json b/change/@azure-msal-node-5483cdd9-b571-4018-b9fd-cc85a982c443.json deleted file mode 100644 index d5bacad886..0000000000 --- a/change/@azure-msal-node-5483cdd9-b571-4018-b9fd-cc85a982c443.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "Added file-based detection for Azure Arc", - "packageName": "@azure/msal-node", - "email": "rginsburg@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts b/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts index cbe034eadd..82cb15176a 100644 --- a/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts +++ b/lib/msal-node/src/client/ManagedIdentitySources/AzureArc.ts @@ -33,36 +33,18 @@ import { RESOURCE_BODY_OR_QUERY_PARAMETER_NAME, } from "../../utils/Constants"; import { NodeStorage } from "../../cache/NodeStorage"; -import { - accessSync, - constants as fsConstants, - readFileSync, - statSync, -} from "fs"; +import { readFileSync, statSync } from "fs"; import { ManagedIdentityTokenResponse } from "../../response/ManagedIdentityTokenResponse"; import { ManagedIdentityId } from "../../config/ManagedIdentityId"; import path from "path"; export const ARC_API_VERSION: string = "2019-11-01"; -export const DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT: string = - "http://127.0.0.1:40342/metadata/identity/oauth2/token"; -const HIMDS_EXECUTABLE_HELPER_STRING = "N/A: himds executable exists"; - -type FilePathMap = { - win32: string; - linux: string; -}; -export const SUPPORTED_AZURE_ARC_PLATFORMS: FilePathMap = { +export const SUPPORTED_AZURE_ARC_PLATFORMS = { win32: `${process.env["ProgramData"]}\\AzureConnectedMachineAgent\\Tokens\\`, linux: "/var/opt/azcmagent/tokens/", }; -export const AZURE_ARC_FILE_DETECTION: FilePathMap = { - win32: `${process.env["ProgramFiles"]}\\AzureConnectedMachineAgent\\himds.exe`, - linux: "/opt/azcmagent/bin/himds", -}; - /** * Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/AzureArcManagedIdentitySource.cs */ @@ -82,38 +64,13 @@ export class AzureArc extends BaseManagedIdentitySource { } public static getEnvironmentVariables(): Array { - let identityEndpoint: string | undefined = + const identityEndpoint: string | undefined = process.env[ ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT ]; - let imdsEndpoint: string | undefined = + const imdsEndpoint: string | undefined = process.env[ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT]; - // if either of the identity or imds endpoints are undefined, check if the himds executable exists - if (!identityEndpoint || !imdsEndpoint) { - // get the expected Windows or Linux file path of the himds executable - const fileDetectionPath: string = - AZURE_ARC_FILE_DETECTION[process.platform as keyof FilePathMap]; - try { - /* - * check if the himds executable exists and its permissions allow it to be read - * returns undefined if true, throws an error otherwise - */ - accessSync( - fileDetectionPath, - fsConstants.F_OK | fsConstants.R_OK - ); - - identityEndpoint = DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT; - imdsEndpoint = HIMDS_EXECUTABLE_HELPER_STRING; - } catch (err) { - /* - * do nothing - * accessSync returns undefined on success, and throws an error on failure - */ - } - } - return [identityEndpoint, imdsEndpoint]; } @@ -127,46 +84,36 @@ export class AzureArc extends BaseManagedIdentitySource { const [identityEndpoint, imdsEndpoint] = AzureArc.getEnvironmentVariables(); - // if either of the identity or imds endpoints are undefined (even after himds file detection) + // if either of the identity or imds endpoints are undefined, this MSI provider is unavailable. if (!identityEndpoint || !imdsEndpoint) { logger.info( - `[Managed Identity] ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is unavailable through environment variables because one or both of '${ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT}' and '${ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT}' are not defined. ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is also unavailable through file detection.` + `[Managed Identity] ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is unavailable because one or both of the '${ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT}' and '${ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT}' environment variables are not defined.` ); - return null; } - // check if the imds endpoint is set to the default for file detection - if (imdsEndpoint === HIMDS_EXECUTABLE_HELPER_STRING) { - logger.info( - `[Managed Identity] ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is available through file detection. Defaulting to known ${ManagedIdentitySourceNames.AZURE_ARC} endpoint: ${DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT}. Creating ${ManagedIdentitySourceNames.AZURE_ARC} managed identity.` - ); - } else { - // otherwise, both the identity and imds endpoints are defined without file detection; validate them - - const validatedIdentityEndpoint: string = - AzureArc.getValidatedEnvVariableUrlString( - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT, - identityEndpoint, - ManagedIdentitySourceNames.AZURE_ARC, - logger - ); - // remove trailing slash - validatedIdentityEndpoint.endsWith("/") - ? validatedIdentityEndpoint.slice(0, -1) - : validatedIdentityEndpoint; - + const validatedIdentityEndpoint: string = AzureArc.getValidatedEnvVariableUrlString( - ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT, - imdsEndpoint, + ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT, + identityEndpoint, ManagedIdentitySourceNames.AZURE_ARC, logger ); + // remove trailing slash + validatedIdentityEndpoint.endsWith("/") + ? validatedIdentityEndpoint.slice(0, -1) + : validatedIdentityEndpoint; + + AzureArc.getValidatedEnvVariableUrlString( + ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT, + imdsEndpoint, + ManagedIdentitySourceNames.AZURE_ARC, + logger + ); - logger.info( - `[Managed Identity] Environment variables validation passed for ${ManagedIdentitySourceNames.AZURE_ARC} managed identity. Endpoint URI: ${validatedIdentityEndpoint}. Creating ${ManagedIdentitySourceNames.AZURE_ARC} managed identity.` - ); - } + logger.info( + `[Managed Identity] Environment variables validation passed for ${ManagedIdentitySourceNames.AZURE_ARC} managed identity. Endpoint URI: ${validatedIdentityEndpoint}. Creating ${ManagedIdentitySourceNames.AZURE_ARC} managed identity.` + ); if ( managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED @@ -239,11 +186,9 @@ export class AzureArc extends BaseManagedIdentitySource { ); } - // get the expected Windows or Linux file path + // get the expected Windows or Linux file path) const expectedSecretFilePath: string = - SUPPORTED_AZURE_ARC_PLATFORMS[ - process.platform as keyof FilePathMap - ]; + SUPPORTED_AZURE_ARC_PLATFORMS[process.platform as string]; // throw an error if the file in the file path is not a .key file const fileName: string = path.basename(secretFilePath); diff --git a/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts b/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts index 7eb3a69245..0a5107dc17 100644 --- a/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts +++ b/lib/msal-node/test/client/ManagedIdentitySources/AzureArc.spec.ts @@ -45,7 +45,6 @@ jest.mock("fs"); describe("Acquires a token successfully via an Azure Arc Managed Identity", () => { let originalPlatform: string; - let accessSyncSpy: jest.SpyInstance; beforeAll(() => { process.env[ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT] = @@ -57,14 +56,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = Object.defineProperty(process, "platform", { value: "linux", }); - - accessSyncSpy = jest - .spyOn(fs, "accessSync") - // returns undefined when the himds file exists and its permissions allow it to be read - // otherwise, throws an error - .mockImplementation(() => { - throw new Error(); - }); }); afterAll(() => { @@ -119,62 +110,6 @@ describe("Acquires a token successfully via an Azure Arc Managed Identity", () = ); }); - test("acquires a token when both/either the identityEndpoint and/or imdsEndpoint environment variables are undefined, and the himds executable exists and its permissions allow it to be read", async () => { - // delete the environment variables so the himds executable is checked - delete process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT - ]; - delete process.env[ - ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT - ]; - // delete value cached from getManagedIdentitySource() in the beforeEach - delete ManagedIdentityClient["sourceName"]; - - // MI source will not be Azure Arc yet, since the environment variables are undefined, - // and accessSyncSpy still returns an error - // (meaning either the himds file doesn't exists or its permissions don't allow it to be read) - expect( - managedIdentityApplication.getManagedIdentitySource() - ).not.toBe(ManagedIdentitySourceNames.AZURE_ARC); - // delete value cached from getManagedIdentitySource() directly above - delete ManagedIdentityClient["sourceName"]; - - // returns undefined when the himds file exists and its permissions allow it to be read - // otherwise, throws an error - accessSyncSpy.mockImplementationOnce(() => { - return undefined; - }); - - expect(managedIdentityApplication.getManagedIdentitySource()).toBe( - ManagedIdentitySourceNames.AZURE_ARC - ); - - // returns undefined when the himds file exists and its permissions allow it to be read - // otherwise, throws an error - accessSyncSpy.mockImplementationOnce(() => { - return undefined; - }); - const networkManagedIdentityResult: AuthenticationResult = - await managedIdentityApplication.acquireToken( - managedIdentityRequestParams - ); - expect(networkManagedIdentityResult.fromCache).toBe(false); - - expect(networkManagedIdentityResult.accessToken).toEqual( - DEFAULT_SYSTEM_ASSIGNED_MANAGED_IDENTITY_AUTHENTICATION_RESULT.accessToken - ); - - // one for each call to getManagedIdentitySource() + one for the acquireToken call - expect(accessSyncSpy).toHaveBeenCalledTimes(3); - - // reset the environment variables to expected values for Azure Arc tests - process.env[ - ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT - ] = "fake_IDENTITY_ENDPOINT"; - process.env[ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT] = - "fake_IMDS_ENDPOINT"; - }); - test("returns an already acquired token from the cache", async () => { const networkManagedIdentityResult: AuthenticationResult = await managedIdentityApplication.acquireToken({