Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
73a9caa
add `mcd_get_microseconds`
kevinAlbs Oct 30, 2025
a031652
add `mongoc_percent_encode`
kevinAlbs Oct 30, 2025
f848d34
extend `mcd-azure.h`
kevinAlbs Oct 30, 2025
373d1d0
add (failing) prose tests 5.1 and 5.2
kevinAlbs Oct 30, 2025
95df9b4
implement `mongoc_oidc_env_fn_azure`
kevinAlbs Oct 30, 2025
df70d96
apply OIDC environment callback
kevinAlbs Oct 30, 2025
435ec48
test more bad configuration cases
kevinAlbs Oct 30, 2025
d525dfe
support unified tests with Azure OIDC
kevinAlbs Oct 30, 2025
7e7678c
add Evergreen tests
kevinAlbs Oct 30, 2025
4499785
remove unnecessary `run_on`
kevinAlbs Oct 31, 2025
465f6e8
use bash arrays
kevinAlbs Nov 11, 2025
b5c446e
remove unnecessary `--parallel`
kevinAlbs Nov 11, 2025
f12e802
remove unnecessary `cd`
kevinAlbs Nov 11, 2025
5c7edae
use `-l` for consistency with `run-tests.sh`
kevinAlbs Nov 11, 2025
90d62f8
remove unnecessary 1 hour setup group timeout
kevinAlbs Nov 11, 2025
dddaad4
add `BSON_UNUSED(use_pool_void);`
kevinAlbs Nov 11, 2025
847a80f
remove unnecessary assertion
kevinAlbs Nov 11, 2025
e90adf6
apply URI to OIDC cache later
kevinAlbs Nov 11, 2025
5059177
use `const` pointers
kevinAlbs Nov 11, 2025
6f4867e
use `tmp_bson` to simplify test code
kevinAlbs Nov 11, 2025
2e24796
use `mlib_duration` for timeout argument
kevinAlbs Nov 11, 2025
58fb40a
prefer `mlib` utilities
kevinAlbs Nov 11, 2025
7382b6a
clarify cause of error when failing to get token
kevinAlbs Nov 11, 2025
7f82240
accept an `mlib_timer` in `mcd_azure_access_token_from_imds`
kevinAlbs Nov 13, 2025
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
50 changes: 47 additions & 3 deletions .evergreen/config_generator/components/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def task_groups():
name='test-oidc-task-group',
tasks=['oidc-auth-test-task'],
setup_group_can_fail_task=True,
setup_group_timeout_secs=60 * 60, # 1 hour
teardown_group_can_fail_task=True,
teardown_group_timeout_secs=180, # 3 minutes
setup_group=[
Expand All @@ -35,6 +34,29 @@ def task_groups():
script='./drivers-evergreen-tools/.evergreen/auth_oidc/teardown.sh',
)
],
),
EvgTaskGroup(
name='test-oidc-azure-task-group',
tasks=['oidc-azure-auth-test-task'],
setup_group_can_fail_task=True,
teardown_group_can_fail_task=True,
teardown_group_timeout_secs=180, # 3 minutes
setup_group=[
FetchDET.call(),
ec2_assume_role(role_arn='${aws_test_secrets_role}'),
bash_exec(
command_type=EvgCommandType.SETUP,
include_expansions_in_env=['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN'],
env={"AZUREOIDC_VMNAME_PREFIX": "CDRIVER"},
script='./drivers-evergreen-tools/.evergreen/auth_oidc/azure/create-and-setup-vm.sh',
),
],
teardown_group=[
bash_exec(
command_type=EvgCommandType.SETUP,
script='./drivers-evergreen-tools/.evergreen/auth_oidc/azure/delete-vm.sh',
)
],
)
]

Expand All @@ -59,6 +81,29 @@ def tasks():
),
RunTests.call(),
],
),
EvgTask(
name='oidc-azure-auth-test-task',
run_on=['debian11-small'], # TODO: switch to 'debian11-latest' after DEVPROD-23011 fixed.
commands=[
FetchSource.call(),
bash_exec(
working_dir="mongoc",
add_expansions_to_env=True,
command_type=EvgCommandType.TEST,
script='.evergreen/scripts/oidc-azure-compile.sh',
),
expansions_update(file="mongoc/oidc-remote-test-expansion.yml"),
bash_exec(
add_expansions_to_env=True,
command_type=EvgCommandType.TEST,
env={
"AZUREOIDC_DRIVERS_TAR_FILE": "${OIDC_TEST_TARBALL}",
"AZUREOIDC_TEST_CMD": "source ./env.sh && ./.evergreen/scripts/oidc-azure-test.sh"
},
script='./drivers-evergreen-tools/.evergreen/auth_oidc/azure/run-driver-test.sh',
),
],
)
]

Expand All @@ -68,7 +113,6 @@ def variants():
BuildVariant(
name='oidc',
display_name='OIDC',
run_on=[find_small_distro('ubuntu2404').name],
tasks=[EvgTaskRef(name='test-oidc-task-group')],
tasks=[EvgTaskRef(name='test-oidc-task-group'), EvgTaskRef(name='test-oidc-azure-task-group')],
),
]
32 changes: 31 additions & 1 deletion .evergreen/generated_configs/task_groups.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
task_groups:
- name: test-oidc-azure-task-group
setup_group:
- func: fetch-det
- command: ec2.assume_role
params:
role_arn: ${aws_test_secrets_role}
- command: subprocess.exec
type: setup
params:
binary: bash
env:
AZUREOIDC_VMNAME_PREFIX: CDRIVER
include_expansions_in_env:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_SESSION_TOKEN
args:
- -c
- ./drivers-evergreen-tools/.evergreen/auth_oidc/azure/create-and-setup-vm.sh
setup_group_can_fail_task: true
tasks:
- oidc-azure-auth-test-task
teardown_group:
- command: subprocess.exec
type: setup
params:
binary: bash
args:
- -c
- ./drivers-evergreen-tools/.evergreen/auth_oidc/azure/delete-vm.sh
teardown_group_timeout_secs: 180
- name: test-oidc-task-group
setup_group:
- func: fetch-det
Expand All @@ -17,7 +48,6 @@ task_groups:
- -c
- ./drivers-evergreen-tools/.evergreen/auth_oidc/setup.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 3600
tasks:
- oidc-auth-test-task
teardown_group:
Expand Down
28 changes: 28 additions & 0 deletions .evergreen/generated_configs/tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4221,6 +4221,34 @@ tasks:
- { key: MONGODB_VERSION, value: latest }
- { key: TOPOLOGY, value: replica_set }
- func: run-tests
- name: oidc-azure-auth-test-task
run_on:
- debian11-small
commands:
- func: fetch-source
- command: subprocess.exec
type: test
params:
binary: bash
working_dir: mongoc
add_expansions_to_env: true
args:
- -c
- .evergreen/scripts/oidc-azure-compile.sh
- command: expansions.update
params:
file: mongoc/oidc-remote-test-expansion.yml
- command: subprocess.exec
type: test
params:
binary: bash
add_expansions_to_env: true
env:
AZUREOIDC_DRIVERS_TAR_FILE: ${OIDC_TEST_TARBALL}
AZUREOIDC_TEST_CMD: source ./env.sh && ./.evergreen/scripts/oidc-azure-test.sh
args:
- -c
- ./drivers-evergreen-tools/.evergreen/auth_oidc/azure/run-driver-test.sh
- name: openssl-compat-1.0.2-shared-ubuntu2404-gcc
run_on: ubuntu2404-large
tags: [openssl-compat, openssl-1.0.2, openssl-shared, ubuntu2404, gcc]
Expand Down
3 changes: 1 addition & 2 deletions .evergreen/generated_configs/variants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,9 @@ buildvariants:
- name: mock-server-test
- name: oidc
display_name: OIDC
run_on:
- ubuntu2404-small
tasks:
- name: test-oidc-task-group
- name: test-oidc-azure-task-group
- name: openssl-compat-matrix
display_name: OpenSSL Compatibility Matrix
tasks:
Expand Down
55 changes: 55 additions & 0 deletions .evergreen/scripts/oidc-azure-compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -o errexit
set -o pipefail
set -o nounset

if [[ "${distro_id:?}" == "debian11-small" ]]; then
# Temporary workaround for lack of uv on `debian11`. TODO: remove after DEVPROD-23011 is resolved.
uv_dir="$(mktemp -d)"
python3 -m virtualenv "${uv_dir:?}"
# shellcheck source=/dev/null
(. "${uv_dir:?}/bin/activate" && python -m pip install uv)
PATH="${uv_dir:?}/bin:${PATH:-}"
command -V uv >/dev/null
fi

. .evergreen/scripts/install-build-tools.sh
install_build_tools
export CMAKE_GENERATOR="Ninja"

# Use ccache if able.
. .evergreen/scripts/find-ccache.sh
find_ccache_and_export_vars "$(pwd)" || true

echo "Compile test-libmongoc ... begin"
# Disable unnecessary dependencies. test-libmongoc is copied to a remote host for testing, which may not have all dependent libraries.
cmake_flags=(
-DENABLE_SASL=OFF
-DENABLE_SNAPPY=OFF
-DENABLE_ZSTD=OFF
-DENABLE_ZLIB=OFF
-DENABLE_SRV=OFF
-DENABLE_CLIENT_SIDE_ENCRYPTION=OFF
-DENABLE_EXAMPLES=OFF
-DENABLE_SRV=OFF
)
cmake "${cmake_flags[@]}" -Bcmake-build
cmake --build cmake-build --target test-libmongoc
echo "Compile test-libmongoc ... end"

# Create tarball for remote testing.
echo "Creating test-libmongoc tarball ... begin"

# Copy test binary and JSON test files. All JSON test files are needed to start test-libmongoc.
files=(
.evergreen/scripts/oidc-azure-test.sh
cmake-build/src/libmongoc/test-libmongoc
src/libmongoc/tests/json
src/libbson/tests/json
)
tar -czf test-libmongoc.tar.gz "${files[@]}"
echo "Creating test-libmongoc tarball ... end"

cat <<EOT > oidc-remote-test-expansion.yml
OIDC_TEST_TARBALL: $(pwd)/test-libmongoc.tar.gz
EOT
14 changes: 14 additions & 0 deletions .evergreen/scripts/oidc-azure-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -o errexit
set -o pipefail
set -o nounset

export MONGOC_TEST_OIDC="ON"
export MONGOC_TEST_USER="$OIDC_ADMIN_USER"
export MONGOC_TEST_PASSWORD="$OIDC_ADMIN_PWD"
export MONGOC_AZURE_RESOURCE="$AZUREOIDC_RESOURCE"

# Install required OpenSSL runtime library.
sudo apt install -y libssl-dev

./cmake-build/src/libmongoc/test-libmongoc -d -l '/auth/unified/*' -l '/oidc/*'
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ To run test cases with large allocations, set:
* `MONGOC_TEST_LARGE_ALLOCATIONS=on` This may result in sudden test suite termination due to allocation failure. Use with caution.

* `MONGOC_TEST_OIDC=on` to test OIDC using a test environment described [here](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/d7a7337b384392a09fbe7fc80a7244e6f1226c18/.evergreen/auth_oidc).
* `MONGOC_AZURE_RESOURCE=<resource>` to test OIDC using an Azure test environment described [here](https://github.com/mongodb-labs/drivers-evergreen-tools/blob/d7a7337b384392a09fbe7fc80a7244e6f1226c18/.evergreen/auth_oidc/azure/README.md).

All tests should pass before submitting a patch.

Expand Down
68 changes: 58 additions & 10 deletions src/libmongoc/src/mongoc/mcd-azure.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,28 @@
#include <mongoc/mongoc-util-private.h>

#include <mlib/cmp.h>
#include <mlib/time_point.h>
#include <mlib/timer.h>

#define AZURE_API_VERSION "2018-02-01"

static const char *const DEFAULT_METADATA_PATH =
"/metadata/identity/oauth2/"
"token?api-version=" AZURE_API_VERSION "&resource=https%3A%2F%2Fvault.azure.net";
static const char *const DEFAULT_METADATA_PATH = "/metadata/identity/oauth2/token?api-version=" AZURE_API_VERSION;

void
bool
mcd_azure_imds_request_init(mcd_azure_imds_request *req,
const char *token_resource,
const char *const opt_imds_host,
int opt_port,
const char *const opt_extra_headers)
const char *const opt_extra_headers,
const char *const opt_client_id)
{
BSON_ASSERT_PARAM(req);
BSON_ASSERT_PARAM(token_resource);

bool ok = false;
char *encoded_token_resource = NULL;
mcommon_string_append_t path = {0};

_mongoc_http_request_init(&req->req);
// The HTTP host of the IMDS server
req->req.host = req->_owned_host = bson_strdup(opt_imds_host ? opt_imds_host : "169.254.169.254");
Expand All @@ -50,9 +58,33 @@ mcd_azure_imds_request_init(mcd_azure_imds_request *req,
req->req.extra_headers = req->_owned_headers = bson_strdup_printf("Metadata: true\r\n"
"Accept: application/json\r\n%s",
opt_extra_headers ? opt_extra_headers : "");
// The default path is suitable. In the future, we may want to add query
// parameters to disambiguate a managed identity.
req->req.path = req->_owned_path = bson_strdup(DEFAULT_METADATA_PATH);
// Build the path with query parameters.
encoded_token_resource = mongoc_percent_encode(token_resource);
if (!encoded_token_resource) {
goto fail;
}

mcommon_string_new_as_append(&path);

if (!mcommon_string_append(&path, DEFAULT_METADATA_PATH) ||
!mcommon_string_append_printf(&path, "&resource=%s", encoded_token_resource)) {
goto fail;
}

if (opt_client_id) {
if (!mcommon_string_append_printf(&path, "&client_id=%s", opt_client_id)) {
goto fail;
}
}

req->req.path = req->_owned_path = mcommon_string_from_append_destroy_with_steal(&path);
path = (mcommon_string_append_t){0};

ok = true;
fail:
bson_free(encoded_token_resource);
mcommon_string_from_append_destroy(&path);
return ok;
}

void
Expand Down Expand Up @@ -156,11 +188,15 @@ mcd_azure_access_token_destroy(mcd_azure_access_token *c)

bool
mcd_azure_access_token_from_imds(mcd_azure_access_token *const out,
const char *token_resource,
const char *const opt_imds_host,
int opt_port,
const char *opt_extra_headers,
mlib_timer opt_timer,
const char *opt_client_id,
bson_error_t *error)
{
BSON_ASSERT_PARAM(token_resource);
BSON_ASSERT_PARAM(out);

bool okay = false;
Expand All @@ -172,9 +208,21 @@ mcd_azure_access_token_from_imds(mcd_azure_access_token *const out,
_mongoc_http_response_init(&resp);

mcd_azure_imds_request req = MCD_AZURE_IMDS_REQUEST_INIT;
mcd_azure_imds_request_init(&req, opt_imds_host, opt_port, opt_extra_headers);
if (!mcd_azure_imds_request_init(&req, token_resource, opt_imds_host, opt_port, opt_extra_headers, opt_client_id)) {
_mongoc_set_error(error, MONGOC_ERROR_AZURE, MONGOC_ERROR_KMS_SERVER_HTTP, "Failed to initialize request");
goto fail;
}

mlib_timer timer = mlib_time_cmp(opt_timer.expires_at, ==, (mlib_time_point){0})
? opt_timer
: mlib_expires_after(mlib_duration(3, s)); // Default 3 second timeout.

int timeout_ms = 0;
if (mlib_narrow(&timeout_ms, mlib_milliseconds_count(mlib_timer_remaining(timer)))) {
timeout_ms = mlib_maxof(int); // Clamp to max int.
}

if (!_mongoc_http_send(&req.req, 3 * 1000, false, NULL, &resp, error)) {
if (!_mongoc_http_send(&req.req, timeout_ms, false, NULL, &resp, error)) {
goto fail;
}

Expand Down
Loading