Skip to content

Commit 87a54fb

Browse files
committed
Add OIDC k8s provider
1 parent 241ef1c commit 87a54fb

File tree

7 files changed

+157
-6
lines changed

7 files changed

+157
-6
lines changed

.evergreen/.evg.yml

+74
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,30 @@ functions:
837837
${PREPARE_SHELL}
838838
MONGODB_URI="${MONGODB_URI}" JAVA_VERSION="${JAVA_VERSION}" .evergreen/run-graalvm-native-image-app.sh
839839
840+
"oidc-auth-test-k8s-func":
841+
- command: shell.exec
842+
type: test
843+
params:
844+
shell: bash
845+
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
846+
script: |-
847+
set -o errexit
848+
${PREPARE_SHELL}
849+
export K8S_VARIANT=${VARIANT}
850+
cd src
851+
git add .
852+
git commit --allow-empty -m "add files"
853+
# uncompressed tar used to allow appending .git folder
854+
export K8S_DRIVERS_TAR_FILE=/tmp/mongo-java-driver.tar
855+
git archive -o $K8S_DRIVERS_TAR_FILE HEAD
856+
tar -rf $K8S_DRIVERS_TAR_FILE .git
857+
export K8S_TEST_CMD="OIDC_ENV=k8s ./.evergreen/run-mongodb-oidc-test.sh"
858+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/setup-pod.sh
859+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/run-self-test.sh
860+
source $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/secrets-export.sh
861+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/run-driver-test.sh
862+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/teardown-pod.sh
863+
840864
# Anchors
841865

842866
pre:
@@ -960,6 +984,22 @@ tasks:
960984
export GCPOIDC_TEST_CMD="OIDC_ENV=gcp ./.evergreen/run-mongodb-oidc-test.sh"
961985
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
962986
987+
- name: "oidc-auth-test-k8s"
988+
commands:
989+
- command: ec2.assume_role
990+
params:
991+
role_arn: ${aws_test_secrets_role}
992+
duration_seconds: 1800
993+
- func: "oidc-auth-test-k8s-func"
994+
vars:
995+
VARIANT: eks
996+
# - func: "oidc-auth-test-k8s-func" TODO disabled, memory issue, consider forking and increasing here: https://github.com/mongodb-labs/drivers-evergreen-tools/commit/4bc3e500b6f0e8ab01f052c4a1bfb782d6a29b4e
997+
# vars:
998+
# VARIANT: gke
999+
- func: "oidc-auth-test-k8s-func"
1000+
vars:
1001+
VARIANT: aks
1002+
9631003
- name: serverless-test
9641004
commands:
9651005
- func: "run serverless"
@@ -2122,6 +2162,33 @@ task_groups:
21222162
tasks:
21232163
- oidc-auth-test-gcp
21242164

2165+
- name: test_oidc_k8s_task_group
2166+
setup_group_can_fail_task: true
2167+
setup_group_timeout_secs: 1800
2168+
teardown_task_can_fail_task: true
2169+
teardown_group_timeout_secs: 180
2170+
setup_group:
2171+
- func: fetch source
2172+
- func: prepare resources
2173+
- func: fix absolute paths
2174+
- command: ec2.assume_role
2175+
params:
2176+
role_arn: ${aws_test_secrets_role}
2177+
- command: subprocess.exec
2178+
params:
2179+
binary: bash
2180+
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
2181+
args:
2182+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup.sh
2183+
teardown_group:
2184+
- command: subprocess.exec
2185+
params:
2186+
binary: bash
2187+
args:
2188+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown.sh
2189+
tasks:
2190+
- oidc-auth-test-k8s
2191+
21252192
buildvariants:
21262193

21272194
# Test packaging and other release related routines
@@ -2322,6 +2389,13 @@ buildvariants:
23222389
- name: testgcpoidc_task_group
23232390
batchtime: 20160 # 14 days
23242391

2392+
- name: testk8soidc-variant
2393+
display_name: "OIDC Auth K8S"
2394+
run_on: ubuntu2204-small
2395+
tasks:
2396+
- name: test_oidc_k8s_task_group
2397+
batchtime: 20160 # 14 days
2398+
23252399
- matrix_name: "aws-auth-test"
23262400
matrix_spec: { ssl: "nossl", jdk: ["jdk8", "jdk17", "jdk21"], version: ["4.4", "5.0", "6.0", "7.0", "8.0", "latest"], os: "ubuntu",
23272401
aws-credential-provider: "*" }

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

+9
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ 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+
# fix for git permissions issue:
30+
git config --global --add safe.directory /tmp/test
2231
else
2332
echo "Unrecognized OIDC_ENV $OIDC_ENV"
2433
exit 1

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

+7-2
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

+33-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.bson.BsonDocument;
3939
import org.bson.BsonString;
4040
import org.bson.RawBsonDocument;
41+
import org.jetbrains.annotations.NotNull;
4142

4243
import javax.security.sasl.SaslClient;
4344
import java.io.IOException;
@@ -76,10 +77,11 @@ public final class OidcAuthenticator extends SaslAuthenticator {
7677
private static final String TEST_ENVIRONMENT = "test";
7778
private static final String AZURE_ENVIRONMENT = "azure";
7879
private static final String GCP_ENVIRONMENT = "gcp";
80+
private static final String K8S_ENVIRONMENT = "k8s";
7981
private static final List<String> IMPLEMENTED_ENVIRONMENTS = Arrays.asList(
80-
AZURE_ENVIRONMENT, GCP_ENVIRONMENT, TEST_ENVIRONMENT);
82+
AZURE_ENVIRONMENT, GCP_ENVIRONMENT, K8S_ENVIRONMENT, TEST_ENVIRONMENT);
8183
private static final List<String> USER_SUPPORTED_ENVIRONMENTS = Arrays.asList(
82-
AZURE_ENVIRONMENT, GCP_ENVIRONMENT);
84+
AZURE_ENVIRONMENT, GCP_ENVIRONMENT, K8S_ENVIRONMENT);
8385
private static final List<String> REQUIRES_TOKEN_RESOURCE = Arrays.asList(
8486
AZURE_ENVIRONMENT, GCP_ENVIRONMENT);
8587
private static final List<String> ALLOWS_USERNAME = Arrays.asList(
@@ -90,6 +92,10 @@ public final class OidcAuthenticator extends SaslAuthenticator {
9092

9193
public static final String OIDC_TOKEN_FILE = "OIDC_TOKEN_FILE";
9294

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

95101
@Nullable
@@ -192,6 +198,8 @@ private OidcCallback getRequestCallback() {
192198
machine = getAzureCallback(getMongoCredential());
193199
} else if (GCP_ENVIRONMENT.equals(environment)) {
194200
machine = getGcpCallback(getMongoCredential());
201+
} else if (K8S_ENVIRONMENT.equals(environment)) {
202+
machine = getK8sCallback();
195203
} else {
196204
machine = getOidcCallbackMechanismProperty(OIDC_CALLBACK_KEY);
197205
}
@@ -206,6 +214,24 @@ private static OidcCallback getTestCallback() {
206214
};
207215
}
208216

217+
@VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
218+
static OidcCallback getK8sCallback() {
219+
return (context) -> {
220+
String azure = System.getenv(K8S_AZURE_FILE);
221+
String aws = System.getenv(K8S_AWS_FILE);
222+
String path;
223+
if (azure != null) {
224+
path = azure;
225+
} else if (aws != null) {
226+
path = aws;
227+
} else {
228+
path = K8S_FALLBACK_FILE;
229+
}
230+
String accessToken = readTokenFromFile(path);
231+
return new OidcCallbackResult(accessToken);
232+
};
233+
}
234+
209235
@VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
210236
static OidcCallback getAzureCallback(final MongoCredential credential) {
211237
return (context) -> {
@@ -499,6 +525,11 @@ private static String readTokenFromFile() {
499525
throw new MongoClientException(
500526
format("Environment variable must be specified: %s", OIDC_TOKEN_FILE));
501527
}
528+
return readTokenFromFile(path);
529+
}
530+
531+
@NotNull
532+
private static String readTokenFromFile(final String path) {
502533
try {
503534
return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
504535
} catch (IOException e) {

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,16 @@ 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+
System.out.println("Failed to drop database: " + e.getMessage());
270+
} else {
271+
throw e;
272+
}
273+
}
265274
cluster.close();
266275
}
267276
}

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

+21-1
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

+3
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)