From 9f8cccb9f0cf8edce081e05e391e1829978f903f Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:32:35 -0400 Subject: [PATCH] DOCSP-43473: oidc (#382) * DOCSP-43473: oidc * vale * fix * wip * log error * dedent * emphasis * fix * MD tech review 1 (cherry picked from commit 8a67edffaf8670840d0e1ac310736760e63f7011) --- snooty.toml | 1 + source/connection-troubleshooting.txt | 2 +- source/fundamentals/enterprise-auth.txt | 287 +++++++++++++++++- .../authentication/auth-properties-commas.rst | 5 + .../authentication/azure-imds-client.go | 28 ++ .../authentication/eks-custom-callback.go | 40 +++ .../authentication/gcp-imds-client.go | 27 ++ .../includes/authentication/gke-callback.go | 40 +++ source/usage-examples/changestream.txt | 4 +- 9 files changed, 430 insertions(+), 4 deletions(-) create mode 100644 source/includes/authentication/auth-properties-commas.rst create mode 100644 source/includes/authentication/azure-imds-client.go create mode 100644 source/includes/authentication/eks-custom-callback.go create mode 100644 source/includes/authentication/gcp-imds-client.go create mode 100644 source/includes/authentication/gke-callback.go diff --git a/snooty.toml b/snooty.toml index f4c174d8..c204c53c 100644 --- a/snooty.toml +++ b/snooty.toml @@ -28,3 +28,4 @@ cmk-long = "Customer Master Key" dek-long = "Data Encryption Key" csfle-short = "CSFLE" csfle-long = "Client-side Field Level Encryption" +mdb-server = "MongoDB Server" diff --git a/source/connection-troubleshooting.txt b/source/connection-troubleshooting.txt index dc18acd7..cc9d77e4 100644 --- a/source/connection-troubleshooting.txt +++ b/source/connection-troubleshooting.txt @@ -199,7 +199,7 @@ instance. For more information about configuring the firewall, see Check the Number of Connections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Each ``MongoClient`` instance supports a maximum number of concurrent open +Each ``Client`` instance supports a maximum number of concurrent open connections in its connection pool. The configuration parameter ``maxPoolSize`` defines this value and is set to ``100`` by default. If there are already a number of open connections equal to ``maxPoolSize``, the server waits until diff --git a/source/fundamentals/enterprise-auth.txt b/source/fundamentals/enterprise-auth.txt index c967aaf2..1228e627 100644 --- a/source/fundamentals/enterprise-auth.txt +++ b/source/fundamentals/enterprise-auth.txt @@ -197,6 +197,287 @@ address of your MongoDB server: authenticates using the PLAIN Simple Authentication and Security Layer (SASL) defined in `RFC-4616 `__. +.. _golang-mongodb-oidc: + +MONGODB-OIDC +------------ + +.. important:: + + The MONGODB-OIDC authentication mechanism requires {+mdb-server+} + v7.0 or later running on a Linux platform. + +The {+driver-short+} supports OpenID Connect (**OIDC**) authentication for **workload +identities**. A workload identity is an identity you assign to a +software workload, such as an application, service, script, or +container, to authenticate and access other services and resources. + +The following sections describe how to use the MONGODB-OIDC +authentication mechanism to authenticate to various platforms. + +To learn more about the MONGODB-OIDC authentication mechanism, see +:manual:`OpenID Connect Authentication ` and +:manual:`MongoDB Server Parameters ` +in the {+mdb-server+} manual. + +.. _golang-mongodb-oidc-azure-imds: + +Azure IMDS +~~~~~~~~~~ + +If your application runs on an Azure VM, or otherwise uses the +`Azure Instance Metadata Service `__ +(IMDS), you can authenticate to MongoDB by using the {+driver-short+}'s +built-in Azure support. + +You can configure OIDC for Azure IMDS in the following ways: + +- By creating a ``Credential`` struct and passing it to the + ``SetAuth()`` method when creating a client +- By setting parameters in your connection string + +.. include:: /includes/authentication/auth-properties-commas.rst + +.. tabs:: + + .. tab:: Credential + :tabid: credential struct + + First, create a map to store your authentication + mechanism properties, as shown in the following example. Replace + the ```` placeholder with the value of the ``audience`` + parameter configured on your MongoDB deployment. + + .. code-block:: go + + props := map[string]string{ + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "", + } + + Then, set the following ``Credential`` struct fields: + + - ``Username``: If you're using an Azure managed identity, set this to the client ID + of the managed identity. If you're using a service principal to represent an + enterprise application, set this to the application ID of the service principal. + - ``AuthMechanism``: Set to ``"MONGODB-OIDC"``. + - ``AuthMechanismProperties``: Set to the ``props`` map that you + previously created. + + The following code example shows how to set these options when creating a + ``Client``: + + .. literalinclude:: /includes/authentication/azure-imds-client.go + :dedent: + :language: go + :copyable: true + :start-after: start-azure-imds-client + :end-before: end-azure-imds-client + :emphasize-lines: 9-11 + + .. tab:: Connection String + :tabid: connectionstring + + Include the following connection options in your connection string: + + - ``username``: If you're using an Azure managed identity, set this to the client ID + of the managed identity. If you're using a service principal to represent an + enterprise application, set this to the application ID of the service principal. + - ``authMechanism``: Set to ``MONGODB-OIDC``. + - ``authMechanismProperties``: Set to + ``ENVIRONMENT:azure,TOKEN_RESOURCE:``. + Replace the ```` placeholder with the + value of the ``audience`` parameter configured on your MongoDB deployment. + + The following code example shows how to set these options in + your connection string: + + .. code-block:: go + + uri := "mongodb://:/?" + + "username=" + + "&authMechanism=MONGODB-OIDC" + + "&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:" + + client, err := mongo.Connect(options.Client().ApplyURI(uri)) + if err != nil { + panic(err) + } + +.. tip:: + + If your application is running on an Azure VM, and only one managed identity is + associated with the VM, you can omit the ``username`` connection option. + +.. _golang-mongodb-oidc-gcp-imds: + +GCP IMDS +~~~~~~~~ + +If your application runs on a Google Compute Engine VM, or otherwise uses the +`GCP Instance Metadata Service `__, +you can authenticate to MongoDB by using the {+driver-short+}'s built-in GCP +support. + +You can configure OIDC for GCP IMDS in the following ways: + +- By creating a ``Credential`` struct and passing it to the + ``SetAuth()`` method when creating a client +- By setting parameters in your connection string + +.. include:: /includes/authentication/auth-properties-commas.rst + +.. tabs:: + + .. tab:: Credential + :tabid: credential struct + + First, create a map to store your authentication + mechanism properties, as shown in the following example. Replace + the ```` placeholder with the value of the ``audience`` + parameter configured on your MongoDB deployment. + + .. code-block:: go + + props := map[string]string{ + "ENVIRONMENT": "gcp", + "TOKEN_RESOURCE": "", + } + + Then, set the following ``Credential`` struct fields: + + - ``AuthMechanism``: Set to ``"MONGODB-OIDC"``. + - ``AuthMechanismProperties``: Set to the ``props`` map that you + previously created. + + The following code example shows how to set these options when creating a + ``Client``: + + .. literalinclude:: /includes/authentication/gcp-imds-client.go + :language: go + :dedent: + :copyable: true + :start-after: start-gcp-imds-client + :end-before: end-gcp-imds-client + :emphasize-lines: 9-10 + + .. tab:: Connection String + :tabid: connectionstring + + Include the following connection options in your connection string: + + - ``authMechanism``: Set to ``MONGODB-OIDC``. + - ``authMechanismProperties``: Set to + ``ENVIRONMENT:gcp,TOKEN_RESOURCE:``. + Replace the ```` placeholder with the + value of the ``audience`` parameter configured on your MongoDB deployment. + + The following code example shows how to set these options in your connection string: + + .. code-block:: go + + uri := "mongodb://:/?" + + "&authMechanism=MONGODB-OIDC" + + "&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:" + + client, err := mongo.Connect(options.Client().ApplyURI(uri)) + if err != nil { + panic(err) + } + +.. _golang-mongodb-oidc-custom-callback: + +Custom Callback +~~~~~~~~~~~~~~~ + +The {+driver-short+} doesn't offer built-in support for all platforms, +including the AWS Elastic Kubernetes Service (EKS). To authenticate +against unsupported platforms, you must define a custom callback +function to use OIDC to authenticate. In the driver, you can define an +``options.OIDCCallback`` function and set it as the value of the +``OIDCMachineCallback`` struct field in your ``Credential`` struct. + +The following example defines a custom callback for an EKS +cluster with a configured IAM OIDC provider. The access token is +read from a path set in the ``AWS_WEB_IDENTITY_TOKEN_FILE`` +environment variable: + +.. literalinclude:: /includes/authentication/eks-custom-callback.go + :language: go + :dedent: + :copyable: true + :start-after: start-custom-callback + :end-before: end-custom-callback + +Then, you can create a ``Credential`` struct that uses the EKS callback +function that you defined: + +.. literalinclude:: /includes/authentication/eks-custom-callback.go + :language: go + :dedent: + :copyable: true + :start-after: start-credential-callback + :end-before: end-credential-callback + :emphasize-lines: 6 + +.. _golang-mongodb-oidc-azure-envs: + +Other Azure Environments +~~~~~~~~~~~~~~~~~~~~~~~~ + +If your application runs on Azure Functions, App Service Environment (ASE), or Azure +Kubernetes Service (AKS), you can use the `azidentity +`__ +module to fetch authentication credentials. + +First, install the ``azidentity`` module by running the +following command: + +.. code-block:: sh + + go get -u github.com/Azure/azure-sdk-for-go/sdk/azidentity + +Your ``OIDCCallback`` function must return an ``OIDCCredential`` +instance that uses the ``AccessToken`` generated from the ``azidentity`` +package. See the preceding :ref:`golang-mongodb-oidc-custom-callback` +section for an example that implements a custom callback to retrieve an +access token and then creates a ``Credential``. + +.. _golang-mongodb-oidc-gcp-gke: + +GCP GKE +~~~~~~~ + +If your application runs on a GCP Google Kubernetes Engine (GKE) cluster with a +`configured service account +`__, +you can read the OIDC token from the standard service-account token-file location. + +First, define the ``OIDCCallback`` function. This function reads the +OIDC token and returns an ``OIDCCredential`` instance. + +The following example defines a callback function named ``gkeCallback``. +The function retrieves an OIDC token from a file in the standard +service-account token-file location: + +.. literalinclude:: /includes/authentication/gke-callback.go + :language: go + :copyable: true + :dedent: + :start-after: start-callback + :end-before: end-callback + +Then, you can create a ``Credential`` struct that uses the the GKE +callback function that you defined: + +.. literalinclude:: /includes/authentication/gke-callback.go + :language: go + :copyable: true + :dedent: + :start-after: start-credential-callback + :end-before: end-credential-callback + :emphasize-lines: 6 + Additional Information ---------------------- @@ -204,9 +485,13 @@ To learn more about the concepts in this guide, see the following documentation: - :manual:`MongoDB Server Support for Kerberos Authentication ` - :manual:`MongoDB Server Support for LDAP Proxy Authentication ` +- :atlas:`Authentication and Authorization with OIDC/OAuth 2.0 ` API Documentation ~~~~~~~~~~~~~~~~~ - `Credential <{+api+}/mongo/options#Credential>`__ type -- `SetAuth() <{+api+}/mongo/options#ClientOptions.SetAuth>`__ method \ No newline at end of file +- `SetAuth() <{+api+}/mongo/options#ClientOptions.SetAuth>`__ method + +.. TODO - `OIDCCredential <{+api+}/...>`__ type +.. TODO - `OIDCCallback <{+api+}/...>`__ function diff --git a/source/includes/authentication/auth-properties-commas.rst b/source/includes/authentication/auth-properties-commas.rst new file mode 100644 index 00000000..8d1e85ae --- /dev/null +++ b/source/includes/authentication/auth-properties-commas.rst @@ -0,0 +1,5 @@ +.. note:: + + If your ``AuthMechanismProperties`` struct field values + include a comma, you must create a ``Credential`` instance + to set your authentication options. diff --git a/source/includes/authentication/azure-imds-client.go b/source/includes/authentication/azure-imds-client.go new file mode 100644 index 00000000..42ad7735 --- /dev/null +++ b/source/includes/authentication/azure-imds-client.go @@ -0,0 +1,28 @@ +package main + +import ( + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func main() { + // start-azure-imds-client + uri := "mongodb://:" + props := map[string]string{ + "ENVIRONMENT": "azure", + "TOKEN_RESOURCE": "", + } + opts := options.Client().ApplyURI(uri) + opts.SetAuth( + options.Credential{ + Username: "", + AuthMechanism: "MONGODB-OIDC", + AuthMechanismProperties: props, + }, + ) + client, err := mongo.Connect(opts) + if err != nil { + panic(err) + } + // end-azure-imds-client +} diff --git a/source/includes/authentication/eks-custom-callback.go b/source/includes/authentication/eks-custom-callback.go new file mode 100644 index 00000000..8d403749 --- /dev/null +++ b/source/includes/authentication/eks-custom-callback.go @@ -0,0 +1,40 @@ +package main + +import ( + "context" + "os" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func main() { + // start-custom-callback + eksCallback := func(_ context.Context, + _ *options.OIDCArgs) (*options.OIDCCredential, error) { + accessToken, err := os.ReadFile( + os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE")) + if err != nil { + return nil, err + } + return &options.OIDCCredential{ + AccessToken: string(accessToken), + }, nil + } + // end-custom-callback + + // start-credential-callback + uri := "mongodb://:" + opts := options.Client().ApplyURI(uri) + opts.SetAuth( + options.Credential{ + AuthMechanism: "MONGODB-OIDC", + OIDCMachineCallback: eksCallback, + }, + ) + client, err := mongo.Connect(opts) + if err != nil { + panic(err) + } + // end-credential-callback +} diff --git a/source/includes/authentication/gcp-imds-client.go b/source/includes/authentication/gcp-imds-client.go new file mode 100644 index 00000000..f415f084 --- /dev/null +++ b/source/includes/authentication/gcp-imds-client.go @@ -0,0 +1,27 @@ +package main + +import ( + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func main() { + // start-gcp-imds-client + uri := "mongodb://:" + props := map[string]string{ + "ENVIRONMENT": "gcp", + "TOKEN_RESOURCE": "", + } + opts := options.Client().ApplyURI(uri) + opts.SetAuth( + options.Credential{ + AuthMechanism: "MONGODB-OIDC", + AuthMechanismProperties: props, + }, + ) + client, err := mongo.Connect(opts) + if err != nil { + panic(err) + } + // end-gcp-imds-client +} diff --git a/source/includes/authentication/gke-callback.go b/source/includes/authentication/gke-callback.go new file mode 100644 index 00000000..60f499c8 --- /dev/null +++ b/source/includes/authentication/gke-callback.go @@ -0,0 +1,40 @@ +package main + +import ( + "context" + "os" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +func main() { + // start-callback + gkeCallback := func(_ context.Context, + _ *options.OIDCArgs) (*options.OIDCCredential, error) { + accessToken, err := os.ReadFile( + "/var/run/secrets/kubernetes.io/serviceaccount/token") + if err != nil { + return nil, err + } + return &options.OIDCCredential{ + AccessToken: string(accessToken), + }, nil + } + // end-callback + + // start-credential-callback + uri := "mongodb://:" + opts := options.Client().ApplyURI(uri) + opts.SetAuth( + options.Credential{ + AuthMechanism: "MONGODB-OIDC", + OIDCMachineCallback: gkeCallback, + }, + ) + client, err := mongo.Connect(opts) + if err != nil { + panic(err) + } + // end-credential-callback +} diff --git a/source/usage-examples/changestream.txt b/source/usage-examples/changestream.txt index 57d07573..7d600ae0 100644 --- a/source/usage-examples/changestream.txt +++ b/source/usage-examples/changestream.txt @@ -8,8 +8,8 @@ Monitor Data Changes .. meta:: :description: Learn by example: how to monitor data changes in MongoDB by using the {+driver-long+}. -You can open a change stream on a ``MongoCollection``, -``MongoDatabase``, or ``MongoClient`` by using the ``Watch()`` method. +You can open a change stream on a ``Collection``, +``Database``, or ``Client`` by using the ``Watch()`` method. Example -------