Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRIVERS-2416 OIDC: Automatic token acquisition for Azure Identity Provider #1421

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions source/auth/auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,12 @@ 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
durran marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -1349,8 +1354,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.
Expand All @@ -1363,6 +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). `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 TOKEN_AUDIENCE will typically be an encoded
Application ID URI of the form: ``api%3A%2F%2F<application-id>``.

__ 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::

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.


Caching Credentials
```````````````````
Expand Down
78 changes: 78 additions & 0 deletions source/auth/tests/mongodb-oidc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down Expand Up @@ -141,6 +142,83 @@ 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's worth noting here that the URI for all tests is the same, and must be equal to:

mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:azure,TOKEN_AUDIENCE:api://${AZUREOIDC_CLIENTID}

Then each individual test can reuse the same URI. Thoughts? I did the same as Python and just set it as the MONGODB_URI and had all the clients use that.


Connect
~~~~~~~
- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:azure,TOKEN_AUDIENCE:<foo>``.
- 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:<foo>``, 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:<foo>``.
- 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:<foo>``.
- Assert that a ``find`` operation succeeds.
- Close the client.
- Assert that the Azure OIDC cache has one entry.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not combine the Main Cache Not Used and Azure Cache is Used tests? Clear both caches, perform the operation, and check that the main cache remains empty and the Azure one has a single 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
===================

Expand Down