Skip to content

Commit b721799

Browse files
authored
CSHARP-5026: Add Kubernetes Support for OIDC (#1560)
1 parent fe331f9 commit b721799

File tree

15 files changed

+430
-82
lines changed

15 files changed

+430
-82
lines changed

evergreen/evergreen.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,26 @@ functions:
10421042
fi
10431043
./evergreen/upload-apidocs.sh
10441044
1045+
run_oidc_k8s_tests:
1046+
- command: shell.exec
1047+
params:
1048+
shell: bash
1049+
working_dir: mongo-csharp-driver
1050+
include_expansions_in_env:
1051+
- "AWS_ACCESS_KEY_ID"
1052+
- "AWS_SECRET_ACCESS_KEY"
1053+
- "AWS_SESSION_TOKEN"
1054+
script: |
1055+
${PREPARE_SHELL}
1056+
1057+
export K8S_VARIANT=${K8S_VARIANT}
1058+
export K8S_DRIVERS_TAR_FILE=/tmp/mongo-csharp-driver.tgz
1059+
export K8S_TEST_CMD="OIDC_ENV=k8s ./evergreen/run-mongodb-oidc-env-tests.sh"
1060+
1061+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/setup-pod.sh
1062+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/run-driver-test.sh
1063+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/k8s/teardown-pod.sh
1064+
10451065
pre:
10461066
- func: fetch-source
10471067
- func: prepare-resources
@@ -1289,6 +1309,27 @@ tasks:
12891309
export GCPOIDC_TEST_CMD="OIDC_ENV=gcp ./evergreen/run-mongodb-oidc-env-tests.sh"
12901310
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
12911311
1312+
- name: test-oidc-k8s
1313+
commands:
1314+
- command: shell.exec
1315+
params:
1316+
shell: bash
1317+
working_dir: mongo-csharp-driver
1318+
script: |-
1319+
${PREPARE_SHELL}
1320+
dotnet build
1321+
tar czf /tmp/mongo-csharp-driver.tgz tests/*.Tests/bin/Debug/net6.0/ ./evergreen/run-mongodb-oidc-env-tests.sh
1322+
- func: assume-ec2-role
1323+
- func: run_oidc_k8s_tests
1324+
vars:
1325+
K8S_VARIANT: eks
1326+
- func: run_oidc_k8s_tests
1327+
vars:
1328+
K8S_VARIANT: gke
1329+
- func: run_oidc_k8s_tests
1330+
vars:
1331+
K8S_VARIANT: aks
1332+
12921333
- name: test-serverless
12931334
exec_timeout_secs: 2700 # 45 minutes: 15 for setup + 30 for tests
12941335
commands:
@@ -2251,6 +2292,34 @@ task_groups:
22512292
tasks:
22522293
- test-oidc-gcp
22532294

2295+
- name: oidc-auth-k8s-task-group
2296+
setup_group_can_fail_task: true
2297+
setup_group_timeout_secs: 1800 # 30 minutes
2298+
setup_group:
2299+
- func: fetch-source
2300+
- func: prepare-resources
2301+
- func: fix-absolute-paths
2302+
- func: make-files-executable
2303+
- func: install-dotnet
2304+
- func: assume-ec2-role
2305+
- command: subprocess.exec
2306+
params:
2307+
binary: bash
2308+
include_expansions_in_env:
2309+
- "AWS_ACCESS_KEY_ID"
2310+
- "AWS_SECRET_ACCESS_KEY"
2311+
- "AWS_SESSION_TOKEN"
2312+
args:
2313+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup.sh
2314+
teardown_group:
2315+
- command: subprocess.exec
2316+
params:
2317+
binary: bash
2318+
args:
2319+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown.sh
2320+
tasks:
2321+
- test-oidc-k8s
2322+
22542323
- name: serverless-task-group
22552324
setup_group_can_fail_task: true
22562325
setup_group_timeout_secs: 1800 # 30 minutes
@@ -2448,6 +2517,13 @@ buildvariants:
24482517
tasks:
24492518
- name: oidc-auth-gcp-task-group
24502519

2520+
- matrix_name: mongodb-oidc-k8s-tests
2521+
matrix_spec: { os: [ "ubuntu-2004" ] }
2522+
display_name: "MongoDB-OIDC Auth (k8s) - ${os}"
2523+
batchtime: 20160 # 14 days
2524+
tasks:
2525+
- name: oidc-auth-k8s-task-group
2526+
24512527
- matrix_name: "ocsp-tests"
24522528
matrix_spec: { version: ["4.4", "5.0", "6.0", "7.0", "8.0", "rapid", "latest"], auth: "noauth", ssl: "ssl", topology: "standalone", os: "windows-64" }
24532529
display_name: "OCSP ${version} ${os}"

evergreen/run-mongodb-oidc-env-tests.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ DOTNET_SDK_PATH="$(pwd)/.dotnet"
77

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

@@ -17,6 +17,8 @@ if [ "$OIDC_ENV" == "azure" ]; then
1717
elif [ "$OIDC_ENV" == "gcp" ]; then
1818
source ./secrets-export.sh
1919
TOKEN_RESOURCE="$GCPOIDC_AUDIENCE"
20+
elif [ "$OIDC_ENV" == "k8s" ]; then
21+
source ./secrets-export.sh
2022
else
2123
echo "Unrecognized OIDC_ENV $OIDC_ENV"
2224
exit 1
@@ -33,4 +35,7 @@ fi
3335

3436
sleep 60 # sleep for 1 minute to let cluster make the master election
3537

38+
# need set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT to avoid "Couldn't find a valid ICU package installed on the system." error
39+
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
40+
3641
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

specifications/auth/tests/legacy/connection-string.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,26 @@
626626
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp",
627627
"valid": false,
628628
"credential": null
629+
},
630+
{
631+
"description": "should recognise the mechanism with k8s provider (MONGODB-OIDC)",
632+
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s",
633+
"valid": true,
634+
"credential": {
635+
"username": null,
636+
"password": null,
637+
"source": "$external",
638+
"mechanism": "MONGODB-OIDC",
639+
"mechanism_properties": {
640+
"ENVIRONMENT": "k8s"
641+
}
642+
}
643+
},
644+
{
645+
"description": "should throw an error for a username and password with k8s provider (MONGODB-OIDC)",
646+
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s",
647+
"valid": false,
648+
"credential": null
629649
}
630650
]
631651
}

specifications/auth/tests/legacy/connection-string.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,18 @@ tests:
454454
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp
455455
valid: false
456456
credential: null
457+
- description: should recognise the mechanism with k8s provider (MONGODB-OIDC)
458+
uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s
459+
valid: true
460+
credential:
461+
username: null
462+
password: null
463+
source: $external
464+
mechanism: MONGODB-OIDC
465+
mechanism_properties:
466+
ENVIRONMENT: k8s
467+
- description: should throw an error for a username and password with k8s provider
468+
(MONGODB-OIDC)
469+
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:k8s
470+
valid: false
471+
credential: null

src/MongoDB.Driver/Authentication/Oidc/FileOidcCallback.cs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* limitations under the License.
1414
*/
1515

16-
using System.IO;
16+
using System;
1717
using System.Threading;
1818
using System.Threading.Tasks;
1919
using MongoDB.Driver.Core.Misc;
@@ -22,35 +22,51 @@ namespace MongoDB.Driver.Authentication.Oidc
2222
{
2323
internal sealed class FileOidcCallback : IOidcCallback
2424
{
25+
private readonly IFileSystemProvider _fileSystemProvider;
26+
2527
#region static
26-
public static FileOidcCallback CreateFromEnvironmentVariable(string environmentVariableName, IEnvironmentVariableProvider environmentVariableProvider)
28+
public static FileOidcCallback CreateFromEnvironmentVariable(
29+
IEnvironmentVariableProvider environmentVariableProvider,
30+
IFileSystemProvider fileSystemProvider,
31+
string[] environmentVariableNames,
32+
string defaultPath = null)
2733
{
28-
var tokenPath = Ensure.IsNotNull(environmentVariableProvider, nameof(environmentVariableProvider)).GetEnvironmentVariable(environmentVariableName);
29-
return new FileOidcCallback(tokenPath);
34+
Ensure.IsNotNull(environmentVariableProvider, nameof(environmentVariableProvider));
35+
Ensure.IsNotNull(fileSystemProvider, nameof(fileSystemProvider));
36+
Ensure.IsNotNullOrEmpty(environmentVariableNames, nameof(environmentVariableNames));
37+
38+
string filePath = null;
39+
foreach (var variableName in environmentVariableNames)
40+
{
41+
filePath = environmentVariableProvider.GetEnvironmentVariable(variableName);
42+
if (!string.IsNullOrEmpty(filePath))
43+
{
44+
break;
45+
}
46+
}
47+
48+
filePath ??= defaultPath;
49+
return new FileOidcCallback(fileSystemProvider, filePath);
3050
}
3151
#endregion
3252

33-
private readonly string _path;
34-
35-
public FileOidcCallback(string path)
53+
public FileOidcCallback(IFileSystemProvider fileSystemProvider, string filePath)
3654
{
37-
_path = Ensure.IsNotNullOrEmpty(path, nameof(path));
55+
_fileSystemProvider = Ensure.IsNotNull(fileSystemProvider, nameof(fileSystemProvider));
56+
FilePath = Ensure.IsNotNullOrEmpty(filePath, nameof(filePath));
3857
}
3958

59+
public string FilePath { get; }
60+
4061
public OidcAccessToken GetOidcAccessToken(OidcCallbackParameters parameters, CancellationToken cancellationToken)
4162
{
42-
var accessToken = File.ReadAllText(_path);
63+
var accessToken = _fileSystemProvider.File.ReadAllText(FilePath);
4364
return new(accessToken, expiresIn: null);
4465
}
4566

4667
public async Task<OidcAccessToken> GetOidcAccessTokenAsync(OidcCallbackParameters parameters, CancellationToken cancellationToken)
4768
{
48-
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
49-
var accessToken = await File.ReadAllTextAsync(_path, cancellationToken).ConfigureAwait(false);
50-
#else
51-
using var streamReader = new StreamReader(_path, System.Text.Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
52-
var accessToken = await streamReader.ReadToEndAsync().ConfigureAwait(false); // no support for cancellationToken
53-
#endif
69+
var accessToken = await _fileSystemProvider.File.ReadAllTextAsync(FilePath).ConfigureAwait(false);
5470
return new(accessToken, expiresIn: null);
5571
}
5672
}

src/MongoDB.Driver/Authentication/Oidc/OidcCallbackAdapterFactory.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,21 @@ internal interface IOidcCallbackAdapterFactory
2626

2727
internal class OidcCallbackAdapterCachingFactory : IOidcCallbackAdapterFactory
2828
{
29-
public static readonly OidcCallbackAdapterCachingFactory Instance = new(SystemClock.Instance, EnvironmentVariableProvider.Instance);
29+
public static readonly OidcCallbackAdapterCachingFactory Instance = new(SystemClock.Instance, EnvironmentVariableProvider.Instance, FileSystemProvider.Instance);
3030

3131
private readonly IClock _clock;
3232
private readonly IEnvironmentVariableProvider _environmentVariableProvider;
33+
private readonly IFileSystemProvider _fileSystemProvider;
3334
private readonly ConcurrentDictionary<OidcConfiguration, IOidcCallbackAdapter> _cache = new();
3435

35-
public OidcCallbackAdapterCachingFactory(IClock clock, IEnvironmentVariableProvider environmentVariableProvider)
36+
public OidcCallbackAdapterCachingFactory(
37+
IClock clock,
38+
IEnvironmentVariableProvider environmentVariableProvider,
39+
IFileSystemProvider fileSystemProvider)
3640
{
37-
_clock = clock;
38-
_environmentVariableProvider = environmentVariableProvider;
41+
_clock = Ensure.IsNotNull(clock, nameof(clock));
42+
_environmentVariableProvider = Ensure.IsNotNull(environmentVariableProvider, nameof(environmentVariableProvider));
43+
_fileSystemProvider = Ensure.IsNotNull(fileSystemProvider, nameof(fileSystemProvider));
3944
}
4045

4146
public IOidcCallbackAdapter Get(OidcConfiguration configuration)
@@ -54,7 +59,15 @@ private IOidcCallbackAdapter CreateCallbackAdapter(OidcConfiguration configurati
5459
{
5560
"azure" => new AzureOidcCallback(configuration.TokenResource),
5661
"gcp" => new GcpOidcCallback(configuration.TokenResource),
57-
"test" => FileOidcCallback.CreateFromEnvironmentVariable("OIDC_TOKEN_FILE", _environmentVariableProvider),
62+
"test" => FileOidcCallback.CreateFromEnvironmentVariable(
63+
_environmentVariableProvider,
64+
_fileSystemProvider,
65+
["OIDC_TOKEN_FILE"]),
66+
"k8s" => FileOidcCallback.CreateFromEnvironmentVariable(
67+
_environmentVariableProvider,
68+
_fileSystemProvider,
69+
["AZURE_FEDERATED_TOKEN_FILE", "AWS_WEB_IDENTITY_TOKEN_FILE"],
70+
"/var/run/secrets/kubernetes.io/serviceaccount/token"),
5871
_ => throw new NotSupportedException($"Non supported {OidcConfiguration.EnvironmentMechanismPropertyName} value: {configuration.Environment}")
5972
};
6073
}

src/MongoDB.Driver/Authentication/Oidc/OidcConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ internal sealed class OidcConfiguration
2727
public const string EnvironmentMechanismPropertyName = "ENVIRONMENT";
2828
public const string TokenResourceMechanismPropertyName = "TOKEN_RESOURCE";
2929

30-
private static readonly ISet<string> __supportedEnvironments = new HashSet<string> { "test", "azure", "gcp" };
30+
private static readonly ISet<string> __supportedEnvironments = new HashSet<string> { "test", "azure", "gcp", "k8s" };
3131
private readonly int _hashCode;
3232

3333
public OidcConfiguration(

src/MongoDB.Driver/Authentication/Oidc/OidcSaslMechanism.cs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,7 @@ internal sealed class OidcSaslMechanism : ISaslMechanism
2525
public const string MechanismName = "MONGODB-OIDC";
2626

2727
public static OidcSaslMechanism Create(SaslContext context)
28-
=> Create(context, SystemClock.Instance, EnvironmentVariableProvider.Instance);
29-
30-
public static OidcSaslMechanism Create(SaslContext context, IClock clock, IEnvironmentVariableProvider environmentVariableProvider)
31-
{
32-
Ensure.IsNotNull(clock, nameof(clock));
33-
Ensure.IsNotNull(environmentVariableProvider, nameof(environmentVariableProvider));
34-
35-
var callbackAdapterFactory = OidcCallbackAdapterCachingFactory.Instance;
36-
if (clock != SystemClock.Instance || environmentVariableProvider != EnvironmentVariableProvider.Instance)
37-
{
38-
callbackAdapterFactory = new OidcCallbackAdapterCachingFactory(clock, environmentVariableProvider);
39-
}
40-
41-
return Create(context, callbackAdapterFactory);
42-
}
28+
=> Create(context, OidcCallbackAdapterCachingFactory.Instance);
4329

4430
public static OidcSaslMechanism Create(
4531
SaslContext context,

src/MongoDB.Driver/Core/Misc/FileWrapper.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System.IO;
17+
using System.Threading.Tasks;
1718

1819
namespace MongoDB.Driver.Core.Misc
1920
{
@@ -22,14 +23,27 @@ namespace MongoDB.Driver.Core.Misc
2223
/// </summary>
2324
internal interface IFile
2425
{
25-
bool Exists(string name);
26+
bool Exists(string path);
27+
28+
string ReadAllText(string path);
29+
30+
Task<string> ReadAllTextAsync(string path);
2631
}
2732

2833
internal sealed class FileWrapper : IFile
2934
{
30-
public bool Exists(string name)
35+
public bool Exists(string path) => File.Exists(path);
36+
37+
public string ReadAllText(string path)
38+
{
39+
using var streamReader = new StreamReader(path, System.Text.Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
40+
return streamReader.ReadToEnd();
41+
}
42+
43+
public async Task<string> ReadAllTextAsync(string path)
3144
{
32-
return File.Exists(name);
45+
using var streamReader = new StreamReader(path, System.Text.Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
46+
return await streamReader.ReadToEndAsync().ConfigureAwait(false);
3347
}
3448
}
3549
}

0 commit comments

Comments
 (0)