Skip to content

Commit 3b3ebc9

Browse files
authored
Add OIDC k8s provider (#1576)
JAVA-5405
1 parent fb3f30b commit 3b3ebc9

File tree

7 files changed

+162
-14
lines changed

7 files changed

+162
-14
lines changed

.evergreen/.evg.yml

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,30 @@ functions:
798798
${PREPARE_SHELL}
799799
MONGODB_URI="${MONGODB_URI}" JAVA_VERSION="${JAVA_VERSION}" .evergreen/run-graalvm-native-image-app.sh
800800
801+
"oidc-auth-test-k8s-func":
802+
- command: shell.exec
803+
type: test
804+
params:
805+
shell: bash
806+
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
807+
script: |-
808+
set -o errexit
809+
${PREPARE_SHELL}
810+
export K8S_VARIANT=${VARIANT}
811+
cd src
812+
git add .
813+
git commit --allow-empty -m "add files"
814+
# uncompressed tar used to allow appending .git folder
815+
export K8S_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar
816+
git archive -o $K8S_DRIVERS_TAR_FILE HEAD
817+
tar -rf $K8S_DRIVERS_TAR_FILE .git
818+
export K8S_TEST_CMD="OIDC_ENV=k8s VARIANT=${VARIANT} ./.evergreen/run-mongodb-oidc-test.sh"
819+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/setup-pod.sh
820+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/run-self-test.sh
821+
source $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/secrets-export.sh
822+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/run-driver-test.sh
823+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/teardown-pod.sh
824+
801825
# Anchors
802826

803827
pre:
@@ -921,6 +945,22 @@ tasks:
921945
export GCPOIDC_TEST_CMD="OIDC_ENV=gcp ./.evergreen/run-mongodb-oidc-test.sh"
922946
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
923947
948+
- name: "oidc-auth-test-k8s"
949+
commands:
950+
- command: ec2.assume_role
951+
params:
952+
role_arn: ${aws_test_secrets_role}
953+
duration_seconds: 1800
954+
- func: "oidc-auth-test-k8s-func"
955+
vars:
956+
VARIANT: eks
957+
- func: "oidc-auth-test-k8s-func"
958+
vars:
959+
VARIANT: aks
960+
- func: "oidc-auth-test-k8s-func"
961+
vars:
962+
VARIANT: gke
963+
924964
- name: serverless-test
925965
commands:
926966
- func: "run serverless"
@@ -2011,7 +2051,7 @@ task_groups:
20112051
tasks:
20122052
- testazurekms-task
20132053

2014-
- name: testoidc_task_group
2054+
- name: test-oidc-task-group
20152055
setup_group:
20162056
- func: fetch source
20172057
- func: prepare resources
@@ -2036,7 +2076,7 @@ task_groups:
20362076
tasks:
20372077
- oidc-auth-test
20382078

2039-
- name: testazureoidc_task_group
2079+
- name: test-oidc-azure-task-group
20402080
setup_group:
20412081
- func: fetch source
20422082
- func: prepare resources
@@ -2059,7 +2099,7 @@ task_groups:
20592099
tasks:
20602100
- oidc-auth-test-azure
20612101

2062-
- name: testgcpoidc_task_group
2102+
- name: test-oidc-gcp-task-group
20632103
setup_group:
20642104
- func: fetch source
20652105
- func: prepare resources
@@ -2083,6 +2123,33 @@ task_groups:
20832123
tasks:
20842124
- oidc-auth-test-gcp
20852125

2126+
- name: test-oidc-k8s-task-group
2127+
setup_group_can_fail_task: true
2128+
setup_group_timeout_secs: 1800
2129+
teardown_task_can_fail_task: true
2130+
teardown_group_timeout_secs: 180
2131+
setup_group:
2132+
- func: fetch source
2133+
- func: prepare resources
2134+
- func: fix absolute paths
2135+
- command: ec2.assume_role
2136+
params:
2137+
role_arn: ${aws_test_secrets_role}
2138+
- command: subprocess.exec
2139+
params:
2140+
binary: bash
2141+
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
2142+
args:
2143+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup.sh
2144+
teardown_group:
2145+
- command: subprocess.exec
2146+
params:
2147+
binary: bash
2148+
args:
2149+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown.sh
2150+
tasks:
2151+
- oidc-auth-test-k8s
2152+
20862153
buildvariants:
20872154

20882155
# Test packaging and other release related routines
@@ -2254,21 +2321,28 @@ buildvariants:
22542321
display_name: "OIDC Auth"
22552322
run_on: ubuntu2204-small
22562323
tasks:
2257-
- name: testoidc_task_group
2324+
- name: test-oidc-task-group
22582325
batchtime: 20160 # 14 days
22592326

2260-
- name: testazureoidc-variant
2327+
- name: test-oidc-azure-variant
22612328
display_name: "OIDC Auth Azure"
22622329
run_on: ubuntu2204-small
22632330
tasks:
2264-
- name: testazureoidc_task_group
2331+
- name: test-oidc-azure-task-group
22652332
batchtime: 20160 # 14 days
22662333

2267-
- name: testgcpoidc-variant
2334+
- name: test-oidc-gcp-variant
22682335
display_name: "OIDC Auth GCP"
22692336
run_on: ubuntu2204-small
22702337
tasks:
2271-
- name: testgcpoidc_task_group
2338+
- name: test-oidc-gcp-task-group
2339+
batchtime: 20160 # 14 days
2340+
2341+
- name: test-oidc-k8s-variant
2342+
display_name: "OIDC Auth K8S"
2343+
run_on: ubuntu2204-small
2344+
tasks:
2345+
- name: test-oidc-k8s-task-group
22722346
batchtime: 20160 # 14 days
22732347

22742348
- matrix_name: "aws-auth-test"

.evergreen/run-mongodb-oidc-test.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ elif [ $OIDC_ENV == "azure" ]; then
1919
source ./env.sh
2020
elif [ $OIDC_ENV == "gcp" ]; then
2121
source ./secrets-export.sh
22+
elif [ $OIDC_ENV == "k8s" ]; then
23+
# Make sure K8S_VARIANT is set.
24+
if [ -z "$K8S_VARIANT" ]; then
25+
echo "Must specify K8S_VARIANT"
26+
popd
27+
exit 1
28+
fi
29+
30+
# fix for git permissions issue:
31+
git config --global --add safe.directory /tmp/test
2232
else
2333
echo "Unrecognized OIDC_ENV $OIDC_ENV"
2434
exit 1

driver-core/src/main/com/mongodb/MongoCredential.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public final class MongoCredential {
189189
/**
190190
* Mechanism property key for specifying the environment for OIDC, which is
191191
* the name of a built-in OIDC application environment integration to use
192-
* to obtain credentials. The value must be either "gcp" or "azure".
192+
* to obtain credentials. The value must be either "k8s", "gcp", or "azure".
193193
* This is an alternative to supplying a callback.
194194
* <p>
195195
* The "gcp" and "azure" environments require
@@ -199,6 +199,11 @@ public final class MongoCredential {
199199
* {@link MongoCredential#OIDC_CALLBACK_KEY} and
200200
* {@link MongoCredential#OIDC_HUMAN_CALLBACK_KEY}
201201
* must not be provided.
202+
* <p>
203+
* The "k8s" environment will check the env vars
204+
* {@code AZURE_FEDERATED_TOKEN_FILE}, and then {@code AWS_WEB_IDENTITY_TOKEN_FILE},
205+
* for the token file path, and if neither is set will then use the path
206+
* {@code /var/run/secrets/kubernetes.io/serviceaccount/token}.
202207
*
203208
* @see #createOidcCredential(String)
204209
* @see MongoCredential#TOKEN_RESOURCE_KEY
@@ -265,7 +270,7 @@ public final class MongoCredential {
265270
"*.mongodb.net", "*.mongodb-qa.net", "*.mongodb-dev.net", "*.mongodbgov.net", "localhost", "127.0.0.1", "::1"));
266271

267272
/**
268-
* Mechanism property key for specifying he URI of the target resource (sometimes called the audience),
273+
* Mechanism property key for specifying the URI of the target resource (sometimes called the audience),
269274
* used in some OIDC environments.
270275
*
271276
* <p>A TOKEN_RESOURCE with a comma character must be given as a `MongoClient` configuration and not as

driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ public final class OidcAuthenticator extends SaslAuthenticator {
7676
private static final String TEST_ENVIRONMENT = "test";
7777
private static final String AZURE_ENVIRONMENT = "azure";
7878
private static final String GCP_ENVIRONMENT = "gcp";
79+
private static final String K8S_ENVIRONMENT = "k8s";
7980
private static final List<String> IMPLEMENTED_ENVIRONMENTS = Arrays.asList(
80-
AZURE_ENVIRONMENT, GCP_ENVIRONMENT, TEST_ENVIRONMENT);
81+
AZURE_ENVIRONMENT, GCP_ENVIRONMENT, K8S_ENVIRONMENT, TEST_ENVIRONMENT);
8182
private static final List<String> USER_SUPPORTED_ENVIRONMENTS = Arrays.asList(
82-
AZURE_ENVIRONMENT, GCP_ENVIRONMENT);
83+
AZURE_ENVIRONMENT, GCP_ENVIRONMENT, K8S_ENVIRONMENT);
8384
private static final List<String> REQUIRES_TOKEN_RESOURCE = Arrays.asList(
8485
AZURE_ENVIRONMENT, GCP_ENVIRONMENT);
8586
private static final List<String> ALLOWS_USERNAME = Arrays.asList(
@@ -90,6 +91,10 @@ public final class OidcAuthenticator extends SaslAuthenticator {
9091

9192
public static final String OIDC_TOKEN_FILE = "OIDC_TOKEN_FILE";
9293

94+
private static final String K8S_FALLBACK_FILE = "/var/run/secrets/kubernetes.io/serviceaccount/token";
95+
private static final String K8S_AZURE_FILE = "AZURE_FEDERATED_TOKEN_FILE";
96+
private static final String K8S_AWS_FILE = "AWS_WEB_IDENTITY_TOKEN_FILE";
97+
9398
private static final int CALLBACK_API_VERSION_NUMBER = 1;
9499

95100
@Nullable
@@ -192,6 +197,8 @@ private OidcCallback getRequestCallback() {
192197
machine = getAzureCallback(getMongoCredential());
193198
} else if (GCP_ENVIRONMENT.equals(environment)) {
194199
machine = getGcpCallback(getMongoCredential());
200+
} else if (K8S_ENVIRONMENT.equals(environment)) {
201+
machine = getK8sCallback();
195202
} else {
196203
machine = getOidcCallbackMechanismProperty(OIDC_CALLBACK_KEY);
197204
}
@@ -206,6 +213,24 @@ private static OidcCallback getTestCallback() {
206213
};
207214
}
208215

216+
@VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
217+
static OidcCallback getK8sCallback() {
218+
return (context) -> {
219+
String azure = System.getenv(K8S_AZURE_FILE);
220+
String aws = System.getenv(K8S_AWS_FILE);
221+
String path;
222+
if (azure != null) {
223+
path = azure;
224+
} else if (aws != null) {
225+
path = aws;
226+
} else {
227+
path = K8S_FALLBACK_FILE;
228+
}
229+
String accessToken = readTokenFromFile(path);
230+
return new OidcCallbackResult(accessToken);
231+
};
232+
}
233+
209234
@VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
210235
static OidcCallback getAzureCallback(final MongoCredential credential) {
211236
return (context) -> {
@@ -499,6 +524,10 @@ private static String readTokenFromFile() {
499524
throw new MongoClientException(
500525
format("Environment variable must be specified: %s", OIDC_TOKEN_FILE));
501526
}
527+
return readTokenFromFile(path);
528+
}
529+
530+
private static String readTokenFromFile(final String path) {
502531
try {
503532
return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
504533
} catch (IOException e) {

driver-core/src/test/functional/com/mongodb/ClusterFixture.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,14 @@ static class ShutdownHook extends Thread {
261261
@Override
262262
public void run() {
263263
if (cluster != null) {
264-
new DropDatabaseOperation(getDefaultDatabaseName(), WriteConcern.ACKNOWLEDGED).execute(getBinding());
264+
try {
265+
new DropDatabaseOperation(getDefaultDatabaseName(), WriteConcern.ACKNOWLEDGED).execute(getBinding());
266+
} catch (MongoCommandException e) {
267+
// if we do not have permission to drop the database, assume it is cleaned up in some other way
268+
if (!e.getMessage().contains("Command dropDatabase requires authentication")) {
269+
throw e;
270+
}
271+
}
265272
cluster.close();
266273
}
267274
}

driver-core/src/test/resources/auth/legacy/connection-string.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@
481481
},
482482
{
483483
"description": "should throw an exception if username is specified for test (MONGODB-OIDC)",
484-
"uri": "mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&ENVIRONMENT:test",
484+
"uri": "mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test",
485485
"valid": false,
486486
"credential": null
487487
},
@@ -631,6 +631,26 @@
631631
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp",
632632
"valid": false,
633633
"credential": null
634+
},
635+
{
636+
"description": "should recognise the mechanism with k8s provider (MONGODB-OIDC)",
637+
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s",
638+
"valid": true,
639+
"credential": {
640+
"username": null,
641+
"password": null,
642+
"source": "$external",
643+
"mechanism": "MONGODB-OIDC",
644+
"mechanism_properties": {
645+
"ENVIRONMENT": "k8s"
646+
}
647+
}
648+
},
649+
{
650+
"description": "should throw an error for a username and password with k8s provider (MONGODB-OIDC)",
651+
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s",
652+
"valid": false,
653+
"credential": null
634654
}
635655
]
636656
}

driver-sync/src/test/functional/com/mongodb/internal/connection/OidcAuthenticationProseTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ private MongoClientSettings createSettings(
826826
String cleanedConnectionString = callback == null ? connectionString : connectionString
827827
.replace("ENVIRONMENT:azure,", "")
828828
.replace("ENVIRONMENT:gcp,", "")
829+
.replace("&authMechanismProperties=ENVIRONMENT:k8s", "")
829830
.replace("ENVIRONMENT:test,", "");
830831
return createSettings(cleanedConnectionString, callback, commandListener, OIDC_CALLBACK_KEY);
831832
}
@@ -1042,6 +1043,8 @@ private OidcCallbackResult callback(final OidcCallbackContext context) {
10421043
c = OidcAuthenticator.getAzureCallback(credential);
10431044
} else if (oidcEnv.contains("gcp")) {
10441045
c = OidcAuthenticator.getGcpCallback(credential);
1046+
} else if (oidcEnv.contains("k8s")) {
1047+
c = OidcAuthenticator.getK8sCallback();
10451048
} else {
10461049
c = getProseTestCallback();
10471050
}

0 commit comments

Comments
 (0)