From b5e970268048bc8b6d0bcddadc351f2dbb77e0e0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 16 May 2023 09:05:27 -0500 Subject: [PATCH 1/5] DRIVERS-2416 OIDC: Automatic token acquisition for Azure Identity Provider --- source/auth/auth.rst | 23 +++++++++++++++--- source/auth/tests/mongodb-oidc.rst | 39 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index 6b16be5bc2..246b942aba 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1252,7 +1252,10 @@ mechanism mechanism_properties PROVIDER_NAME Drivers MUST allow the user to specify a name for using a service - to obtain credentials that is one of ["aws"]. + to obtain credentials that is one of ["aws", "azure"]. + TOKEN_AUDIENCE + The authorization token audience claim, needed for the "azure" + provider. REQUEST_TOKEN_CALLBACK Drivers MUST allow the user to specify a callback of the form "onRequest" (defined below), if the driver supports @@ -1349,8 +1352,8 @@ the driver MUST raise an error. Supported Service Providers ``````````````````````````` -Drivers MUST support obtaining credentials for a service for "aws", given -by the PROVIDER_NAME mechanism property. In all cases the acquired token +Drivers MUST support obtaining credentials for a service for "aws" and "azure", +given by the PROVIDER_NAME mechanism property. In all cases the acquired token will be given as the ``jwt`` argument and the JwtStepRequest MUST be made immediately, as part of speculative authentication if appropriate, skipping the PrincipalStepRequest. Drivers MUST raise an error if both a PROVIDER_NAME and username are given, since using a service will not use the username. @@ -1364,6 +1367,20 @@ interpret it as a file path. The contents of the file are read as the access token. If the path does not exist or cannot be read, or the environment variable does not exist, the driver MUST raise an error. +AZURE +_____ +When the PROVIDER_NAME mechanism property is set to "azure", the driver MUST +attempt to retrieve a credential from the Azure Instance Metadata Service (IMDS). +The driver MUST ensure that the TOKEN_AUDIENCE mechanism property is set when +the PROVIDER_NAME is "azure". + +The driver MUST use the same procedure given in :ref:_access-token-azure to +obtain an access token, with the one exception of using the TOKEN_AUDIENCE as +the ``resource`` parameter. The azure credentials MUST be cached by +TOKEN_AUDIENCE, and expire within 5 minutes of the time given by the +``expires_in`` parameter of the IMDS response. + + Caching Credentials ``````````````````` diff --git a/source/auth/tests/mongodb-oidc.rst b/source/auth/tests/mongodb-oidc.rst index 724841e0e0..98a8d52a53 100644 --- a/source/auth/tests/mongodb-oidc.rst +++ b/source/auth/tests/mongodb-oidc.rst @@ -6,6 +6,7 @@ Drivers MUST test the following scenarios: - ``Callback-Driven Auth`` - ``AWS Automatic Auth`` +- ``Azure Automatic Auth`` - ``Callback Validation`` - ``Cached Credentials`` - ``Speculative Authentication`` @@ -141,6 +142,44 @@ Allowed Hosts Ignored - Assert that a ``find`` operation succeeds. - Close the client. +Azure Automatic Auth +================== + +Drivers MUST be able to authenticate using the "azure" provider workflow, using +an Azure VM provisioned using the helper scripts in Drivers Evergreen Tools. +These tests will most likely need to be run in a separate test file from the +rest of the tests, to avoid needing to skip multiple tests. + +Connect +~~~~~~~ +- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:azure,TOKEN_AUDIENCE:``. +- Assert that a ``find`` operation succeeds. +- Close the client. + +Allowed Hosts Ignored +~~~~~~~~~~~~~~~~~~~~~ +- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:azure,TOKEN_AUDIENCE:``, and an + ``ALLOWED_HOSTS`` that is an empty list. +- Assert that a ``find`` operation succeeds. +- Close the client. + +Main Cache Not Used +~~~~~~~~~~~~~~~~~~~ +- Clear the main OIDC cache. +- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:azure,TOKEN_AUDIENCE:``. +- Assert that a ``find`` operation succeeds. +- Close the client. +- Assert that the main OIDC cache is empty. + +Azure Cache is Used +~~~~~~~~~~~~~~~~~~~ +- Clear the Azure OIDC cache. +- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:azure,TOKEN_AUDIENCE:``. +- Assert that a ``find`` operation succeeds. +- Close the client. +- Assert that the Azure OIDC cache has one entry. + + Callback Validation =================== From 4bf1b5f76e220186ef9038f1544a1bf4e1276aea Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 16 May 2023 09:06:35 -0500 Subject: [PATCH 2/5] add ref --- source/client-side-encryption/client-side-encryption.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/client-side-encryption/client-side-encryption.rst b/source/client-side-encryption/client-side-encryption.rst index ad361126a3..9b52de7dec 100644 --- a/source/client-side-encryption/client-side-encryption.rst +++ b/source/client-side-encryption/client-side-encryption.rst @@ -616,6 +616,8 @@ message. Return "access_token" as the credential. +.. _access-token-azure: + Obtaining an Access Token for Azure Key Vault ````````````````````````````````````````````` From a71272a9fcd6c9694ecb54f4a7e1b4a6843b2d74 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 17 May 2023 09:28:14 -0500 Subject: [PATCH 3/5] add azure reauth test --- source/auth/tests/mongodb-oidc.rst | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/source/auth/tests/mongodb-oidc.rst b/source/auth/tests/mongodb-oidc.rst index 98a8d52a53..4a5f4468e1 100644 --- a/source/auth/tests/mongodb-oidc.rst +++ b/source/auth/tests/mongodb-oidc.rst @@ -179,6 +179,45 @@ Azure Cache is Used - Close the client. - Assert that the Azure OIDC cache has one entry. +Reauthentication Succeeds +~~~~~~~~~~~~~~~~~~~~~~~~~ +- Clear the Azure OIDC cache. +- Create a client with an event listener. The following + assumes that the driver does not emit ``saslStart`` or ``saslContinue`` + events. If the driver does emit those events, ignore/filter them for the + purposes of this test. +- Perform a ``find`` operation that succeeds. +- Clear the listener state if possible. +- Force a reauthenication using a ``failCommand`` of the form: + +.. code:: javascript + + { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 391 + } + } + +.. note:: + + the driver MUST either use a unique ``appName`` or explicitly + remove the ``failCommand`` after the test to prevent leakage. + +- Perform another find operation that succeeds. +- Assert that the ordering of list started events is [``find``], + , ``find``. Note that if the listener stat could not be cleared then there + will and be extra ``find`` command. +- Assert that the list of command succeeded events is [``find``]. +- Assert that a ``find`` operation failed once during the command execution. +- Close the client. + Callback Validation =================== From 2d65c4ce46ad597e5e352fe4e276d33e90e9a967 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 23 May 2023 09:21:11 -0500 Subject: [PATCH 4/5] use an explicit link for azure creds --- source/auth/auth.rst | 3 ++- source/client-side-encryption/client-side-encryption.rst | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index 246b942aba..78df444fbd 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1374,12 +1374,13 @@ attempt to retrieve a credential from the Azure Instance Metadata Service (IMDS) The driver MUST ensure that the TOKEN_AUDIENCE mechanism property is set when the PROVIDER_NAME is "azure". -The driver MUST use the same procedure given in :ref:_access-token-azure to +The driver MUST use the same procedure given in `Obtaining an Access Token for Azure Key Vault`_ to obtain an access token, with the one exception of using the TOKEN_AUDIENCE as the ``resource`` parameter. The azure credentials MUST be cached by TOKEN_AUDIENCE, and expire within 5 minutes of the time given by the ``expires_in`` parameter of the IMDS response. +.. _Obtaining an Access Token for Azure Key Vault: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#obtaining-an-access-token-for-azure-key-vault Caching Credentials ``````````````````` diff --git a/source/client-side-encryption/client-side-encryption.rst b/source/client-side-encryption/client-side-encryption.rst index 9b52de7dec..ad361126a3 100644 --- a/source/client-side-encryption/client-side-encryption.rst +++ b/source/client-side-encryption/client-side-encryption.rst @@ -616,8 +616,6 @@ message. Return "access_token" as the credential. -.. _access-token-azure: - Obtaining an Access Token for Azure Key Vault ````````````````````````````````````````````` From bb11f0804a6e30120f4f58b4848c9fa74f7015b3 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 21 Jun 2023 09:50:56 -0500 Subject: [PATCH 5/5] update to include client id --- source/auth/auth.rst | 51 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index 78df444fbd..3740a49ba5 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1256,6 +1256,8 @@ mechanism_properties TOKEN_AUDIENCE The authorization token audience claim, needed for the "azure" provider. + TOKEN_CLIENT_ID + The client id to associate with a token, needed for the "azure" provider. REQUEST_TOKEN_CALLBACK Drivers MUST allow the user to specify a callback of the form "onRequest" (defined below), if the driver supports @@ -1366,21 +1368,52 @@ attempt to read the value given by the ``AWS_WEB_IDENTITY_TOKEN_FILE`` and interpret it as a file path. The contents of the file are read as the access token. If the path does not exist or cannot be read, or the environment variable does not exist, the driver MUST raise an error. - AZURE _____ When the PROVIDER_NAME mechanism property is set to "azure", the driver MUST -attempt to retrieve a credential from the Azure Instance Metadata Service (IMDS). +attempt to retrieve a credential from the Azure Instance Metadata Service (IMDS). `See this documentation for more information`__ + The driver MUST ensure that the TOKEN_AUDIENCE mechanism property is set when -the PROVIDER_NAME is "azure". +the PROVIDER_NAME is "azure". The TOKEN_AUDIENCE will typically be an encoded +Application ID URI of the form: ``api%3A%2F%2F``. + +__ https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http + +.. default-role:: math + +To obtain an Azure token, the below steps should be taken: + +1. Let `U` be a new URL, initialized from the URL string + :ts:`"http://169.254.169.254/metadata/identity/oauth2/token"` +2. Add a query parameter ``api-version=2018-02-01`` to `U`. +3. Add a query parameter ``resource=TOKEN_AUDIENCE`` to `U`. +4. If TOKEN_CLIENT_ID is given, add a query parameter ``client_id=TOKEN_CLIENT_ID`` to `U`. +5. Prepare an HTTP GET request `Req` based on `U`. + + .. note:: All query parameters on `U` should be appropriately percent-encoded + +6. Add HTTP headers ``Metadata: true`` and ``Accept: application/json`` to + `Req`. +7. Issue `Req` to the Azure IMDS server ``169.254.169.254:80``. Let `Resp` be + the response from the server. If the HTTP response is not completely received + within ten seconds, consider the request to have timed out, and return an + error instead of an access token. +8. If `Resp_{status} ≠ 200`, obtaining the access token has failed, and the HTTP + response body of `Resp` encodes information about the error that occurred. + Return an error including the HTTP response body instead of an access token. +9. Otherwise, let `J` be the JSON document encoded in the HTTP response body of + `Resp`. +10. The result access token `T` is given as the ``access_token`` string property + of `J`. Return `T` as the resulting access token. +12. The resulting "expires in" duration `d_{exp}` is a count of seconds given as + an ASCII-encoded integer string ``expires_in`` property of `J`. + +.. note:: -The driver MUST use the same procedure given in `Obtaining an Access Token for Azure Key Vault`_ to -obtain an access token, with the one exception of using the TOKEN_AUDIENCE as -the ``resource`` parameter. The azure credentials MUST be cached by -TOKEN_AUDIENCE, and expire within 5 minutes of the time given by the -``expires_in`` parameter of the IMDS response. + If JSON decoding of `Resp` fails, or the ``access_token`` property is absent + from `J`, this is a protocol error from IMDS. Indicate this error to the + requester of the access token. -.. _Obtaining an Access Token for Azure Key Vault: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#obtaining-an-access-token-for-azure-key-vault Caching Credentials ```````````````````