Skip to content

Commit ac5917f

Browse files
CSHARP-4168: Obtain AWS credentials for CSFLE in the same way as for MONGODB-AWS. (#862)
1 parent 27c96fd commit ac5917f

File tree

14 files changed

+321
-127
lines changed

14 files changed

+321
-127
lines changed

evergreen/set-temp-fle-aws-creds.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ echo "Triggering temporary CSFLE credentials"
2929

3030
get_creds() {
3131
$PYTHON - "$@" << 'EOF'
32+
import sys
3233
import boto3
3334
client = boto3.client("sts")
3435
credentials = client.get_session_token()["Credentials"]
35-
print (credentials["AccessKeyId"] + " " + credentials["SecretAccessKey"] + " " + credentials["SessionToken"])
36+
sys.stdout.write(credentials["AccessKeyId"] + " " + credentials["SecretAccessKey"] + " " + credentials["SessionToken"])
3637
EOF
3738
}
3839

src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs

Lines changed: 77 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
using MongoDB.Bson.Serialization;
2424
using MongoDB.Driver.Core.Connections;
2525
using MongoDB.Driver.Core.Misc;
26-
using MongoDB.Shared;
2726

2827
namespace MongoDB.Driver.Core.Authentication
2928
{
@@ -32,6 +31,73 @@ namespace MongoDB.Driver.Core.Authentication
3231
/// </summary>
3332
public class MongoAWSAuthenticator : SaslAuthenticator
3433
{
34+
internal static class ExternalAuthenticator
35+
{
36+
public static AwsCredentials CreateAwsCredentialsFromExternalSource(string subject = "MONGODB-AWS authentication") =>
37+
CreateAwsCredentialsFromExternalSourceAsync(subject).GetAwaiter().GetResult();
38+
39+
public static async Task<AwsCredentials> CreateAwsCredentialsFromExternalSourceAsync(string subject = "MONGODB-AWS authentication") =>
40+
CreateAwsCredentialsFromEnvironmentVariables(subject) ??
41+
(await CreateAwsCredentialsFromEcsResponseAsync().ConfigureAwait(false)) ??
42+
(await CreateAwsCredentialsFromEc2ResponseAsync().ConfigureAwait(false)) ??
43+
throw new InvalidOperationException($"Unable to find credentials for {subject}.");
44+
45+
// private methods
46+
private static AwsCredentials CreateAwsCredentialsFromEnvironmentVariables(string subject)
47+
{
48+
var accessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID");
49+
var secretAccessKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY");
50+
var sessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN");
51+
52+
if (accessKeyId == null && secretAccessKey == null && sessionToken == null)
53+
{
54+
return null;
55+
}
56+
if (secretAccessKey != null && accessKeyId == null)
57+
{
58+
throw new InvalidOperationException($"When using {subject} if a secret access key is provided via environment variables then an access key ID must be provided also.");
59+
}
60+
if (accessKeyId != null && secretAccessKey == null)
61+
{
62+
throw new InvalidOperationException($"When using {subject} if an access key ID is provided via environment variables then a secret access key must be provided also.");
63+
}
64+
if (sessionToken != null && (accessKeyId == null || secretAccessKey == null))
65+
{
66+
throw new InvalidOperationException($"When using {subject} if a session token is provided via environment variables then an access key ID and a secret access key must be provided also.");
67+
}
68+
69+
return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken);
70+
}
71+
72+
private static async Task<AwsCredentials> CreateAwsCredentialsFromEcsResponseAsync()
73+
{
74+
var relativeUri = Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
75+
if (relativeUri == null)
76+
{
77+
return null;
78+
}
79+
80+
var response = await AwsHttpClientHelper.GetECSResponseAsync(relativeUri).ConfigureAwait(false);
81+
return CreateAwsCreadentialsFromAwsResponse(response);
82+
}
83+
84+
private static async Task<AwsCredentials> CreateAwsCredentialsFromEc2ResponseAsync()
85+
{
86+
var response = await AwsHttpClientHelper.GetEC2ResponseAsync().ConfigureAwait(false);
87+
return CreateAwsCreadentialsFromAwsResponse(response);
88+
}
89+
90+
private static AwsCredentials CreateAwsCreadentialsFromAwsResponse(string awsResponse)
91+
{
92+
var parsedResponse = BsonDocument.Parse(awsResponse);
93+
var accessKeyId = parsedResponse.GetValue("AccessKeyId", null)?.AsString;
94+
var secretAccessKey = parsedResponse.GetValue("SecretAccessKey", null)?.AsString;
95+
var sessionToken = parsedResponse.GetValue("Token", null)?.AsString;
96+
97+
return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken);
98+
}
99+
}
100+
35101
// constants
36102
private const int ClientNonceLength = 32;
37103

@@ -72,14 +138,7 @@ private static MongoAWSMechanism CreateMechanism(
72138
{
73139
var awsCredentials =
74140
CreateAwsCredentialsFromMongoCredentials(username, password, properties) ??
75-
CreateAwsCredentialsFromEnvironmentVariables() ??
76-
CreateAwsCredentialsFromEcsResponse() ??
77-
CreateAwsCredentialsFromEc2Response();
78-
79-
if (awsCredentials == null)
80-
{
81-
throw new InvalidOperationException("Unable to find credentials for MONGODB-AWS authentication.");
82-
}
141+
ExternalAuthenticator.CreateAwsCredentialsFromExternalSource();
83142

84143
return new MongoAWSMechanism(awsCredentials, randomByteGenerator, clock);
85144
}
@@ -109,60 +168,6 @@ private static AwsCredentials CreateAwsCredentialsFromMongoCredentials(string us
109168
return new AwsCredentials(accessKeyId: username, secretAccessKey: password, sessionToken);
110169
}
111170

112-
private static AwsCredentials CreateAwsCredentialsFromEnvironmentVariables()
113-
{
114-
var accessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID");
115-
var secretAccessKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY");
116-
var sessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN");
117-
118-
if (accessKeyId == null && secretAccessKey == null && sessionToken == null)
119-
{
120-
return null;
121-
}
122-
if (secretAccessKey != null && accessKeyId == null)
123-
{
124-
throw new InvalidOperationException("When using MONGODB-AWS authentication if a secret access key is provided via environment variables then an access key ID must be provided also.");
125-
}
126-
if (accessKeyId != null && secretAccessKey == null)
127-
{
128-
throw new InvalidOperationException("When using MONGODB-AWS authentication if an access key ID is provided via environment variables then a secret access key must be provided also.");
129-
}
130-
if (sessionToken != null && (accessKeyId == null || secretAccessKey == null))
131-
{
132-
throw new InvalidOperationException("When using MONGODB-AWS authentication if a session token is provided via environment variables then an access key ID and a secret access key must be provided also.");
133-
}
134-
135-
return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken);
136-
}
137-
138-
private static AwsCredentials CreateAwsCredentialsFromEcsResponse()
139-
{
140-
var relativeUri = Environment.GetEnvironmentVariable("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
141-
if (relativeUri == null)
142-
{
143-
return null;
144-
}
145-
146-
var response = AwsHttpClientHelper.GetECSResponseAsync(relativeUri).GetAwaiter().GetResult();
147-
var parsedResponse = BsonDocument.Parse(response);
148-
var accessKeyId = parsedResponse.GetValue("AccessKeyId", null)?.AsString;
149-
var secretAccessKey = parsedResponse.GetValue("SecretAccessKey", null)?.AsString;
150-
var sessionToken = parsedResponse.GetValue("Token", null)?.AsString;
151-
152-
return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken);
153-
}
154-
155-
private static AwsCredentials CreateAwsCredentialsFromEc2Response()
156-
{
157-
var response = AwsHttpClientHelper.GetEC2ResponseAsync().GetAwaiter().GetResult();
158-
var parsedResponse = BsonDocument.Parse(response);
159-
var accessKeyId = parsedResponse.GetValue("AccessKeyId", null)?.AsString;
160-
var secretAccessKey = parsedResponse.GetValue("SecretAccessKey", null)?.AsString;
161-
var sessionToken = parsedResponse.GetValue("Token", null)?.AsString;
162-
163-
return new AwsCredentials(accessKeyId, SecureStringHelper.ToSecureString(secretAccessKey), sessionToken);
164-
}
165-
166171
private static string ExtractSessionTokenFromMechanismProperties(IEnumerable<KeyValuePair<string, string>> properties)
167172
{
168173
if (properties != null)
@@ -272,7 +277,7 @@ public override string DatabaseName
272277
}
273278

274279
// nested classes
275-
private class AwsCredentials
280+
internal class AwsCredentials
276281
{
277282
private readonly string _accessKeyId;
278283
private readonly SecureString _secretAccessKey;
@@ -288,6 +293,14 @@ public AwsCredentials(string accessKeyId, SecureString secretAccessKey, string s
288293
public string AccessKeyId => _accessKeyId;
289294
public SecureString SecretAccessKey => _secretAccessKey;
290295
public string SessionToken => _sessionToken;
296+
297+
public BsonDocument ConvertToKmsCredentials() =>
298+
new BsonDocument
299+
{
300+
{ "accessKeyId", _accessKeyId },
301+
{ "secretAccessKey", SecureStringHelper.ToInsecureString(_secretAccessKey) },
302+
{ "sessionToken", _sessionToken, _sessionToken != null }
303+
};
291304
}
292305

293306
private static class AwsHttpClientHelper

src/MongoDB.Driver.Core/MongoDB.Driver.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
<ItemGroup>
5757
<PackageReference Include="DnsClient" Version="1.6.1" />
5858
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.2" PrivateAssets="All" />
59+
<PackageReference Include="MongoDB.Libmongocrypt" Version="1.6.0" />
5960
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
60-
<PackageReference Include="MongoDB.Libmongocrypt" Version="1.5.5" />
6161
<PackageReference Include="SharpCompress" Version="0.30.1" />
6262
<PackageReference Include="Snappier" Version="1.0.0" />
6363
<PackageReference Include="ZstdSharp.Port" Version="0.6.2" />

src/MongoDB.Driver/Encryption/AutoEncryptionLibMongoController.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
using System.Threading;
2020
using System.Threading.Tasks;
2121
using MongoDB.Bson;
22-
using MongoDB.Driver.Core.Misc;
2322
using MongoDB.Driver.Core.Servers;
2423
using MongoDB.Driver.Core.WireProtocol;
2524
using MongoDB.Libmongocrypt;
@@ -66,11 +65,7 @@ private AutoEncryptionLibMongoCryptController(
6665
IMongoClient metadataClient,
6766
CryptClient cryptClient,
6867
AutoEncryptionOptions autoEncryptionOptions)
69-
: base(
70-
Ensure.IsNotNull(cryptClient, nameof(cryptClient)),
71-
Ensure.IsNotNull(keyVaultClient, nameof(keyVaultClient)),
72-
Ensure.IsNotNull(Ensure.IsNotNull(autoEncryptionOptions, nameof(autoEncryptionOptions)).KeyVaultNamespace, nameof(autoEncryptionOptions.KeyVaultNamespace)),
73-
Ensure.IsNotNull(Ensure.IsNotNull(autoEncryptionOptions, nameof(autoEncryptionOptions)).TlsOptions, nameof(autoEncryptionOptions.TlsOptions)))
68+
: base(cryptClient, keyVaultClient, autoEncryptionOptions)
7469
{
7570
_internalClient = internalClient; // can be null
7671
_metadataClient = metadataClient; // can be null

src/MongoDB.Driver/Encryption/AutoEncryptionOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace MongoDB.Driver.Encryption
3030
/// <summary>
3131
/// Auto encryption options.
3232
/// </summary>
33-
public class AutoEncryptionOptions
33+
public class AutoEncryptionOptions : IEncryptionOptions
3434
{
3535
// private fields
3636
private readonly bool _bypassAutoEncryption;

src/MongoDB.Driver/Encryption/ClientEncryptionOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace MongoDB.Driver.Encryption
2121
/// <summary>
2222
/// Client encryption options.
2323
/// </summary>
24-
public class ClientEncryptionOptions
24+
public class ClientEncryptionOptions : IEncryptionOptions
2525
{
2626
// private fields
2727
private readonly IMongoClient _keyVaultClient;

src/MongoDB.Driver/Encryption/ExplicitEncryptionLibMongoCryptController.cs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
using System.Threading;
2020
using System.Threading.Tasks;
2121
using MongoDB.Bson;
22-
using MongoDB.Bson.IO;
2322
using MongoDB.Bson.Serialization;
2423
using MongoDB.Driver.Core.Misc;
2524
using MongoDB.Libmongocrypt;
@@ -32,11 +31,9 @@ internal sealed class ExplicitEncryptionLibMongoCryptController : LibMongoCryptC
3231
public ExplicitEncryptionLibMongoCryptController(
3332
CryptClient cryptClient,
3433
ClientEncryptionOptions clientEncryptionOptions)
35-
: base(
36-
Ensure.IsNotNull(cryptClient, nameof(cryptClient)),
34+
: base(cryptClient,
3735
Ensure.IsNotNull(Ensure.IsNotNull(clientEncryptionOptions, nameof(clientEncryptionOptions)).KeyVaultClient, nameof(clientEncryptionOptions.KeyVaultClient)),
38-
Ensure.IsNotNull(Ensure.IsNotNull(clientEncryptionOptions, nameof(clientEncryptionOptions)).KeyVaultNamespace, nameof(clientEncryptionOptions.KeyVaultNamespace)),
39-
Ensure.IsNotNull(Ensure.IsNotNull(clientEncryptionOptions, nameof(clientEncryptionOptions)).TlsOptions, nameof(clientEncryptionOptions.TlsOptions)))
36+
clientEncryptionOptions)
4037
{
4138
}
4239

@@ -547,23 +544,6 @@ private BsonValue RenderFilter(FilterDefinition<BsonDocument> filter)
547544
return filter.Render(serializer, registry);
548545
}
549546

550-
private byte[] ToBsonIfNotNull(BsonValue value)
551-
{
552-
if (value != null)
553-
{
554-
var writerSettings = BsonBinaryWriterSettings.Defaults.Clone();
555-
#pragma warning disable 618
556-
if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
557-
{
558-
writerSettings.GuidRepresentation = GuidRepresentation.Unspecified;
559-
}
560-
#pragma warning restore 618
561-
return value.ToBson(writerSettings: writerSettings);
562-
}
563-
564-
return null;
565-
}
566-
567547
private Guid UnwrapKeyId(RawBsonDocument wrappedKeyDocument)
568548
{
569549
var keyId = wrappedKeyDocument["_id"].AsBsonBinaryData;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Collections.Generic;
17+
18+
namespace MongoDB.Driver.Encryption
19+
{
20+
internal interface IEncryptionOptions
21+
{
22+
CollectionNamespace KeyVaultNamespace { get; }
23+
24+
IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> KmsProviders { get; }
25+
26+
IReadOnlyDictionary<string, SslSettings> TlsOptions { get; }
27+
}
28+
}

0 commit comments

Comments
 (0)