Skip to content

Commit

Permalink
CSHARP-4610: OIDC: Automatic token acquisition for GCP Identity Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleksandr Poliakov committed May 1, 2024
1 parent 7c4216e commit 300f7c4
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 70 deletions.
58 changes: 55 additions & 3 deletions evergreen/evergreen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1222,13 +1222,33 @@ tasks:
set -o errexit
${PREPARE_SHELL}
dotnet build ./tests/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj
tar czf /tmp/mongo-csharp-driver.tgz ./tests/MongoDB.Driver.Tests/bin/Debug/net6.0 ./evergreen/run-mongodb-oidc-azure-tests.sh
dotnet build
tar czf /tmp/mongo-csharp-driver.tgz tests/*.Tests/bin/Debug/net6.0/ ./evergreen/run-mongodb-oidc-env-tests.sh
export AZUREOIDC_DRIVERS_TAR_FILE=/tmp/mongo-csharp-driver.tgz
export AZUREOIDC_TEST_CMD="./evergreen/run-mongodb-oidc-azure-tests.sh"
export AZUREOIDC_TEST_CMD="OIDC_ENV=azure ./evergreen/run-mongodb-oidc-env-tests.sh"
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
- name: test-oidc-gcp
commands:
- command: shell.exec
params:
shell: bash
working_dir: mongo-csharp-driver
script: |-
set -o errexit
${PREPARE_SHELL}
# Copy secrets-export.sh created by '${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh' script to read secrets inside the vm
cp $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/secrets-export.sh ./secrets-export.sh
dotnet build
tar czf /tmp/mongo-csharp-driver.tgz tests/*.Tests/bin/Debug/net6.0/ ./evergreen/run-mongodb-oidc-env-tests.sh ./secrets-export.sh
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-csharp-driver.tgz
export GCPOIDC_TEST_CMD="OIDC_ENV=gcp ./evergreen/run-mongodb-oidc-env-tests.sh"
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
- name: test-serverless
exec_timeout_secs: 2700 # 45 minutes: 15 for setup + 30 for tests
commands:
Expand Down Expand Up @@ -2146,6 +2166,31 @@ task_groups:
tasks:
- test-oidc-azure

- name: oidc-auth-gcp-task-group
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800 # 30 minutes
setup_group:
- func: fetch-source
- func: prepare-resources
- func: fix-absolute-paths
- func: make-files-executable
- func: install-dotnet
- command: subprocess.exec
params:
binary: bash
env:
GCPOIDC_VMNAME_PREFIX: "CSHARP_DRIVER"
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
teardown_group:
- command: subprocess.exec
params:
binary: bash
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
tasks:
- test-oidc-gcp

- name: serverless-task-group
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800 # 30 minutes
Expand Down Expand Up @@ -2306,6 +2351,13 @@ buildvariants:
tasks:
- name: oidc-auth-azure-task-group

- matrix_name: mongodb-oidc-gcp-tests
matrix_spec: { os: [ "ubuntu-2004" ] }
display_name: "MongoDB-OIDC Auth (gcp) - ${os}"
batchtime: 20160 # 14 days
tasks:
- name: oidc-auth-gcp-task-group

- matrix_name: "ocsp-tests"
matrix_spec: { version: ["4.4", "5.0", "6.0", "7.0", "rapid", "latest"], auth: "noauth", ssl: "ssl", topology: "standalone", os: "windows-64" }
display_name: "OCSP ${version} ${os}"
Expand Down
17 changes: 0 additions & 17 deletions evergreen/run-mongodb-oidc-azure-tests.sh

This file was deleted.

34 changes: 34 additions & 0 deletions evergreen/run-mongodb-oidc-env-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash

# Don't trace since the URI contains a password that shouldn't show up in the logs
set -o errexit # Exit the script with error if any of the commands fail

DOTNET_SDK_PATH="$(pwd)/.dotnet"

echo "Downloading .NET SDK installer into $DOTNET_SDK_PATH folder..."
curl -Lfo ./dotnet-install.sh https://dot.net/v1/dotnet-install.sh
echo "Installing .NET LTS SDK..."
bash ./dotnet-install.sh --channel 6.0 --install-dir "$DOTNET_SDK_PATH" --no-path
export PATH=$DOTNET_SDK_PATH:$PATH

if [ "$OIDC_ENV" == "azure" ]; then
source ./env.sh
TOKEN_RESOURCE="$AZUREOIDC_RESOURCE"
elif [ "$OIDC_ENV" == "gcp" ]; then
source ./secrets-export.sh
TOKEN_RESOURCE="$GCPOIDC_AUDIENCE"
else
echo "Unrecognized OIDC_ENV $OIDC_ENV"
exit 1
fi

if [[ "$MONGODB_URI" =~ ^mongodb:.* ]]; then
MONGODB_URI="mongodb://${OIDC_ADMIN_USER}:${OIDC_ADMIN_PWD}@${MONGODB_URI:10}?authSource=admin"
elif [[ "$MONGODB_URI" =~ ^mongodb\+srv:.* ]]; then
MONGODB_URI="mongodb+srv://${OIDC_ADMIN_USER}:${OIDC_ADMIN_PWD}@${MONGODB_URI:14}?authSource=admin"
else
echo "Unexpected MONGODB_URI format: $MONGODB_URI"
exit 1
fi

dotnet test --no-build --framework net6.0 --filter Category=MongoDbOidc -e OIDC_ENV="$OIDC_ENV" -e TOKEN_RESOURCE="$TOKEN_RESOURCE" -e MONGODB_URI="$MONGODB_URI" --results-directory ./build/test-results --logger "console;verbosity=detailed" ./tests/**/*.Tests.dll
64 changes: 62 additions & 2 deletions specifications/auth/tests/legacy/connection-string.json
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@
}
},
{
"description": "should recognise the mechanism with test integration (MONGODB-OIDC)",
"description": "should recognise the mechanism with test environment (MONGODB-OIDC)",
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test",
"valid": true,
"credential": {
Expand Down Expand Up @@ -569,6 +569,66 @@
}
}
},
{
"description": "should accept a url-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster",
"valid": true,
"credential": {
"username": "user",
"password": null,
"source": "$external",
"mechanism": "MONGODB-OIDC",
"mechanism_properties": {
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "mongodb://test-cluster"
}
}
},
{
"description": "should accept an un-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb://test-cluster",
"valid": true,
"credential": {
"username": "user",
"password": null,
"source": "$external",
"mechanism": "MONGODB-OIDC",
"mechanism_properties": {
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "mongodb://test-cluster"
}
}
},
{
"description": "should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC)",
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abc%2Cd%25ef%3Ag%26hi",
"valid": true,
"credential": {
"username": "user",
"password": null,
"source": "$external",
"mechanism": "MONGODB-OIDC",
"mechanism_properties": {
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "abc,d%ef:g&hi"
}
}
},
{
"description": "should url-encode a TOKEN_RESOURCE (MONGODB-OIDC)",
"uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:a$b",
"valid": true,
"credential": {
"username": "user",
"password": null,
"source": "$external",
"mechanism": "MONGODB-OIDC",
"mechanism_properties": {
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "a$b"
}
}
},
{
"description": "should accept a username and throw an error for a password with azure provider (MONGODB-OIDC)",
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo",
Expand Down Expand Up @@ -609,4 +669,4 @@
"credential": null
}
]
}
}
46 changes: 45 additions & 1 deletion specifications/auth/tests/legacy/connection-string.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ tests:
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test
valid: false
credential:
- description: should throw an exception if username is specified for aws (MONGODB-OIDC)
- description: should throw an exception if username is specified for test (MONGODB-OIDC)
uri: mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&ENVIRONMENT:test
valid: false
credential:
Expand Down Expand Up @@ -412,6 +412,50 @@ tests:
mechanism_properties:
ENVIRONMENT: azure
TOKEN_RESOURCE: foo
- description: should accept a url-encoded TOKEN_RESOURCE (MONGODB-OIDC)
uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster
valid: true
credential:
username: user
password: null
source: $external
mechanism: MONGODB-OIDC
mechanism_properties:
ENVIRONMENT: azure
TOKEN_RESOURCE: 'mongodb://test-cluster'
- description: should accept an un-encoded TOKEN_RESOURCE (MONGODB-OIDC)
uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:mongodb://test-cluster
valid: true
credential:
username: user
password: null
source: $external
mechanism: MONGODB-OIDC
mechanism_properties:
ENVIRONMENT: azure
TOKEN_RESOURCE: 'mongodb://test-cluster'
- description: should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC)
uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abc%2Cd%25ef%3Ag%26hi
valid: true
credential:
username: user
password: null
source: $external
mechanism: MONGODB-OIDC
mechanism_properties:
ENVIRONMENT: azure
TOKEN_RESOURCE: 'abc,d%ef:g&hi'
- description: should url-encode a TOKEN_RESOURCE (MONGODB-OIDC)
uri: mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:a$b
valid: true
credential:
username: user
password: null
source: $external
mechanism: MONGODB-OIDC
mechanism_properties:
ENVIRONMENT: azure
TOKEN_RESOURCE: a$b
- description: should accept a username and throw an error for a password with azure provider (MONGODB-OIDC)
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo
valid: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;

namespace MongoDB.Driver.Core.Authentication.Oidc
{
internal sealed class AzureOidcCallback : IOidcCallback
internal sealed class AzureOidcCallback : HttpRequestOidcCallback
{
private readonly string _tokenResource;

Expand All @@ -33,50 +31,24 @@ public AzureOidcCallback(string tokenResource)
_tokenResource = tokenResource;
}

public OidcAccessToken GetOidcAccessToken(OidcCallbackParameters parameters, CancellationToken cancellationToken)
protected override HttpWebRequest CreateMetadataRequest(OidcCallbackParameters parameters)
{
var request = CreateMetadataRequest(parameters);
using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
{
var response = request.GetResponse();
return ParseMetadataResponse((HttpWebResponse)response);
}
}

public async Task<OidcAccessToken> GetOidcAccessTokenAsync(OidcCallbackParameters parameters, CancellationToken cancellationToken)
{
var request = CreateMetadataRequest(parameters);
using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
{
var response = await request.GetResponseAsync().ConfigureAwait(false);
return ParseMetadataResponse((HttpWebResponse)response);
}
}

private HttpWebRequest CreateMetadataRequest(OidcCallbackParameters parameters)
{
var metadataUrl = $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={_tokenResource}";
var metadataUrl = $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={Uri.EscapeDataString(_tokenResource)}";
if (!string.IsNullOrEmpty(parameters.UserName))
{
metadataUrl += $"&client_id={parameters.UserName}";
metadataUrl += $"&client_id={Uri.EscapeDataString(parameters.UserName)}";
}

var request = WebRequest.CreateHttp(new Uri(metadataUrl));
request.Headers["Metadata"] = "true";
request.Accept = "application/json";
request.Method = "GET";

request.Headers.Add("Metadata", "true");
return request;
}

private OidcAccessToken ParseMetadataResponse(HttpWebResponse response)
protected override OidcAccessToken ParseMetadataResponseContent(Stream responseStream)
{
if (response.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Response status code does not indicate success {response.StatusCode}:{response.StatusDescription}");
}

using var responseReader = new StreamReader(response.GetResponseStream());
using var responseReader = new StreamReader(responseStream);
using var jsonReader = new JsonReader(responseReader);

var context = BsonDeserializationContext.CreateRoot(jsonReader);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.IO;
using System.Net;

namespace MongoDB.Driver.Core.Authentication.Oidc
{
internal sealed class GcpOidcCallback : HttpRequestOidcCallback
{
private readonly string _tokenResource;

public GcpOidcCallback(string tokenResource)
{
_tokenResource = tokenResource;
}

protected override HttpWebRequest CreateMetadataRequest(OidcCallbackParameters parameters)
{
var metadataUrl = $"http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience={Uri.EscapeDataString(_tokenResource)}";
var request = WebRequest.CreateHttp(new Uri(metadataUrl));
request.Method = "GET";
request.Headers.Add("Metadata-Flavor", "Google");
return request;
}

protected override OidcAccessToken ParseMetadataResponseContent(Stream responseStream)
{
using var responseReader = new StreamReader(responseStream);
return new OidcAccessToken(responseReader.ReadToEnd(), null);
}
}
}

0 comments on commit 300f7c4

Please sign in to comment.