Skip to content
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

Commit 7a8fad8

Browse files
authored
Merge pull request #414 from NuGet/agr-kv-lib-upgrade3
Transition from Microsoft.Azure.KeyVault to Azure.Security.KeyVault
2 parents 344f549 + 7faae4a commit 7a8fad8

7 files changed

Lines changed: 75 additions & 58 deletions

File tree

src/NuGet.Services.Configuration/ConfigurationRootSecretReaderFactory.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class ConfigurationRootSecretReaderFactory : ISecretReaderFactory
1212
{
1313
private string _vaultName;
1414
private bool _useManagedIdentity;
15+
private string _tenantId;
1516
private string _clientId;
1617
private string _certificateThumbprint;
1718
private string _storeName;
@@ -34,6 +35,7 @@ public ConfigurationRootSecretReaderFactory(IConfigurationRoot config)
3435
_useManagedIdentity = bool.Parse(useManagedIdentity);
3536
}
3637

38+
_tenantId = config[Constants.KeyVaultTenantIdKey];
3739
_clientId = config[Constants.KeyVaultClientIdKey];
3840
_certificateThumbprint = config[Constants.KeyVaultCertificateThumbprintKey];
3941
if (_useManagedIdentity && IsCertificateConfigurationProvided())
@@ -68,7 +70,7 @@ public ISecretReader CreateSecretReader()
6870

6971
if (_useManagedIdentity)
7072
{
71-
keyVaultConfiguration = new KeyVaultConfiguration(_vaultName);
73+
keyVaultConfiguration = new KeyVaultConfiguration(_vaultName, _clientId);
7274
}
7375
else
7476
{
@@ -83,7 +85,8 @@ public ISecretReader CreateSecretReader()
8385
_validateCertificate);
8486

8587
keyVaultConfiguration = new KeyVaultConfiguration(
86-
_vaultName,
88+
_vaultName,
89+
_tenantId,
8790
_clientId,
8891
certificate,
8992
_sendX5c);
@@ -99,8 +102,8 @@ public ISecretInjector CreateSecretInjector(ISecretReader secretReader)
99102

100103
private bool IsCertificateConfigurationProvided()
101104
{
102-
return !string.IsNullOrEmpty(_clientId)
103-
|| !string.IsNullOrEmpty(_certificateThumbprint);
105+
return !string.IsNullOrEmpty(_certificateThumbprint)
106+
|| !string.IsNullOrEmpty(_tenantId);
104107
}
105108
}
106109
}

src/NuGet.Services.Configuration/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public static class Constants
77
{
88
public static string KeyVaultVaultNameKey = "KeyVault_VaultName";
99
public static string KeyVaultUseManagedIdentity = "KeyVault_UseManagedIdentity";
10+
public static string KeyVaultTenantIdKey = "KeyVault_TenantId";
1011
public static string KeyVaultClientIdKey = "KeyVault_ClientId";
1112
public static string KeyVaultCertificateThumbprintKey = "KeyVault_CertificateThumbprint";
1213
public static string KeyVaultValidateCertificateKey = "KeyVault_ValidateCertificate";

src/NuGet.Services.KeyVault/KeyVaultConfiguration.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class KeyVaultConfiguration
1010
{
1111
public string VaultName { get; }
1212
public bool UseManagedIdentity { get; }
13+
public string TenantId { get; }
1314
public string ClientId { get; }
1415
public X509Certificate2 Certificate { get; }
1516
public bool SendX5c { get; }
@@ -18,6 +19,13 @@ public class KeyVaultConfiguration
1819
/// The constructor for keyvault configuration when using managed identities
1920
/// </summary>
2021
public KeyVaultConfiguration(string vaultName)
22+
: this(vaultName, clientId : null)
23+
{ }
24+
25+
/// <summary>
26+
/// The constructor for keyvault configuration when using managed identities
27+
/// </summary>
28+
public KeyVaultConfiguration(string vaultName, string clientId)
2129
{
2230
if (string.IsNullOrWhiteSpace(vaultName))
2331
{
@@ -26,16 +34,18 @@ public KeyVaultConfiguration(string vaultName)
2634

2735
VaultName = vaultName;
2836
UseManagedIdentity = true;
37+
ClientId = clientId;
2938
}
3039

3140
/// <summary>
3241
/// The constructor for keyvault configuration when using the certificate
3342
/// </summary>
3443
/// <param name="vaultName">The name of the keyvault</param>
44+
/// <param name="tenantId">AAD tenant ID where respective client app is registered.</param>
3545
/// <param name="clientId">Keyvault client id</param>
3646
/// <param name="certificate">Certificate required to access the keyvault</param>
3747
/// <param name="sendX5c">SendX5c property</param>
38-
public KeyVaultConfiguration(string vaultName, string clientId, X509Certificate2 certificate, bool sendX5c = false)
48+
public KeyVaultConfiguration(string vaultName, string tenantId, string clientId, X509Certificate2 certificate, bool sendX5c = false)
3949
{
4050
if (string.IsNullOrWhiteSpace(vaultName))
4151
{
@@ -48,6 +58,7 @@ public KeyVaultConfiguration(string vaultName, string clientId, X509Certificate2
4858
}
4959

5060
UseManagedIdentity = false;
61+
TenantId = tenantId;
5162
VaultName = vaultName;
5263
ClientId = clientId;
5364
Certificate = certificate ?? throw new ArgumentNullException(nameof(certificate));

src/NuGet.Services.KeyVault/KeyVaultReader.cs

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44
using System;
55
using System.Threading.Tasks;
6-
using Microsoft.Azure.KeyVault;
7-
using Microsoft.Azure.Services.AppAuthentication;
6+
using Azure.Core;
7+
using Azure.Identity;
8+
using Azure.Security.KeyVault.Secrets;
89
using Microsoft.Extensions.Logging;
9-
using Microsoft.IdentityModel.Clients.ActiveDirectory;
10+
using AzureSecurityKeyVaultSecret = Azure.Security.KeyVault.Secrets.KeyVaultSecret;
1011

1112
namespace NuGet.Services.KeyVault
1213
{
@@ -17,12 +18,9 @@ namespace NuGet.Services.KeyVault
1718
public class KeyVaultReader : ISecretReader
1819
{
1920
private readonly KeyVaultConfiguration _configuration;
20-
private readonly string _vault;
21-
private readonly Lazy<KeyVaultClient> _keyVaultClient;
22-
private ClientAssertionCertificate _clientAssertionCertificate;
21+
private readonly Lazy<SecretClient> _keyVaultClient;
2322

24-
protected string VaultBaseUrl => _vault;
25-
protected KeyVaultClient KeyVaultClient => _keyVaultClient.Value;
23+
protected SecretClient KeyVaultClient => _keyVaultClient.Value;
2624

2725
public KeyVaultReader(KeyVaultConfiguration configuration)
2826
{
@@ -32,8 +30,7 @@ public KeyVaultReader(KeyVaultConfiguration configuration)
3230
}
3331

3432
_configuration = configuration;
35-
_vault = $"https://{_configuration.VaultName}.vault.azure.net/";
36-
_keyVaultClient = new Lazy<KeyVaultClient>(InitializeClient);
33+
_keyVaultClient = new Lazy<SecretClient>(InitializeClient);
3734
}
3835

3936
public async Task<string> GetSecretAsync(string secretName)
@@ -43,7 +40,7 @@ public async Task<string> GetSecretAsync(string secretName)
4340

4441
public async Task<string> GetSecretAsync(string secretName, ILogger logger)
4542
{
46-
var secret = await _keyVaultClient.Value.GetSecretAsync(_vault, secretName);
43+
AzureSecurityKeyVaultSecret secret = await _keyVaultClient.Value.GetSecretAsync(secretName);
4744
return secret.Value;
4845
}
4946

@@ -54,36 +51,36 @@ public async Task<ISecret> GetSecretObjectAsync(string secretName)
5451

5552
public async Task<ISecret> GetSecretObjectAsync(string secretName, ILogger logger)
5653
{
57-
var secret = await _keyVaultClient.Value.GetSecretAsync(_vault, secretName);
58-
return new KeyVaultSecret(secretName, secret.Value, secret.Attributes.Expires);
54+
AzureSecurityKeyVaultSecret secret = await _keyVaultClient.Value.GetSecretAsync(secretName);
55+
return new KeyVaultSecret(secretName, secret.Value, secret.Properties.ExpiresOn);
5956
}
6057

61-
private KeyVaultClient InitializeClient()
58+
private SecretClient InitializeClient()
6259
{
60+
TokenCredential credential = null;
61+
6362
if (_configuration.UseManagedIdentity)
6463
{
65-
var azureServiceTokenProvider = new AzureServiceTokenProvider();
66-
return new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
64+
if (string.IsNullOrEmpty(_configuration.ClientId))
65+
{
66+
credential = new DefaultAzureCredential();
67+
}
68+
else
69+
{
70+
credential = new ManagedIdentityCredential(_configuration.ClientId);
71+
}
6772
}
6873
else
6974
{
70-
_clientAssertionCertificate = new ClientAssertionCertificate(_configuration.ClientId, _configuration.Certificate);
71-
return new KeyVaultClient(GetTokenAsync);
75+
credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate);
7276
}
77+
return new SecretClient(GetKeyVaultUri(_configuration), credential);
7378
}
7479

75-
private async Task<string> GetTokenAsync(string authority, string resource, string scope)
80+
private Uri GetKeyVaultUri(KeyVaultConfiguration keyVaultConfiguration)
7681
{
77-
var authContext = new AuthenticationContext(authority);
78-
var result = await authContext.AcquireTokenAsync(resource, _clientAssertionCertificate, _configuration.SendX5c);
79-
80-
if (result == null)
81-
{
82-
throw new InvalidOperationException("Bearer token acquisition needed to call the KeyVault service failed");
83-
}
84-
85-
return result.AccessToken;
82+
var uriString = $"https://{keyVaultConfiguration.VaultName}.vault.azure.net/";
83+
return new Uri(uriString);
8684
}
8785
}
88-
8986
}

src/NuGet.Services.KeyVault/KeyVaultWriter.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
using System;
55
using System.Threading.Tasks;
6-
using Microsoft.Azure.KeyVault;
7-
using Microsoft.Azure.KeyVault.Models;
6+
using Azure.Core;
7+
using Azure.Identity;
8+
using Azure.Security.KeyVault.Secrets;
89

910
namespace NuGet.Services.KeyVault
1011
{
@@ -19,16 +20,10 @@ public async Task SetSecretAsync(
1920
string secretValue,
2021
DateTimeOffset? expiration = null)
2122
{
22-
SecretAttributes attributes = null;
23-
if (expiration.HasValue)
24-
{
25-
attributes = new SecretAttributes
26-
{
27-
Expires = expiration.Value.UtcDateTime,
28-
};
29-
}
23+
var secret = new Azure.Security.KeyVault.Secrets.KeyVaultSecret(secretName, secretValue);
24+
secret.Properties.ExpiresOn = expiration;
3025

31-
await KeyVaultClient.SetSecretAsync(VaultBaseUrl, secretName, secretValue, secretAttributes: attributes);
26+
await KeyVaultClient.SetSecretAsync(secret);
3227
}
3328
}
3429
}

src/NuGet.Services.KeyVault/NuGet.Services.KeyVault.csproj

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Microsoft.Azure.KeyVault">
10-
<Version>3.0.5</Version>
11-
</PackageReference>
12-
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication">
13-
<Version>1.4.0</Version>
14-
</PackageReference>
9+
<PackageReference Include="Azure.Identity" Version="1.8.0" />
10+
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.4.0" />
1511
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions">
1612
<Version>2.2.0</Version>
1713
</PackageReference>

tools/AzureSqlConnectionTest/TestApplication.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class TestApplication : CommandLineApplication
1717
public CommandOption Help { get; }
1818

1919
public CommandOption KeyVaultName { get; }
20+
public CommandOption KeyVaultManaged { get; }
21+
public CommandOption KeyVaultTenantId { get; }
2022

2123
public CommandOption KeyVaultClientId { get; }
2224

@@ -41,6 +43,8 @@ public TestApplication()
4143
Help = HelpOption("-? | -h | --help");
4244

4345
KeyVaultName = Option("-kv | --keyVaultName", "KeyVault name", CommandOptionType.SingleValue);
46+
KeyVaultManaged = Option("-kvm | --keyVaultUseMsi", "Use managed identity for KV authentication", CommandOptionType.NoValue);
47+
KeyVaultTenantId = Option("-kvtid | --keyVaultTenantId", "KeyVault tenant id", CommandOptionType.SingleValue);
4448
KeyVaultClientId = Option("-kvcid | --keyVaultClientId", "KeyVault client id", CommandOptionType.SingleValue);
4549
KeyVaultCertificateThumbprint = Option("-kvct | --keyVaultCertThumbprint", "KeyVault certificate thumbprint", CommandOptionType.SingleValue);
4650

@@ -74,15 +78,25 @@ public async Task<int> ExecuteAsync()
7478
var useAdalOnly = UseAdalOnly.HasValue();
7579

7680
if (KeyVaultName.HasValue()
77-
&& KeyVaultClientId.HasValue()
78-
&& KeyVaultCertificateThumbprint.HasValue())
81+
&& ((KeyVaultClientId.HasValue()
82+
&& KeyVaultCertificateThumbprint.HasValue()) || KeyVaultManaged.HasValue()))
7983
{
80-
using (var kvCertificate = GetKeyVaultCertificate(KeyVaultCertificateThumbprint.Value()))
84+
using (var kvCertificate = KeyVaultManaged.HasValue() ? null : GetKeyVaultCertificate(KeyVaultCertificateThumbprint.Value()))
8185
{
82-
var keyVaultConfig = new KeyVaultConfiguration(
83-
KeyVaultName.Value(),
84-
KeyVaultClientId.Value(),
85-
kvCertificate);
86+
KeyVaultConfiguration keyVaultConfig;
87+
if (KeyVaultManaged.HasValue())
88+
{
89+
keyVaultConfig = new KeyVaultConfiguration(KeyVaultName.Value());
90+
}
91+
else
92+
{
93+
keyVaultConfig = new KeyVaultConfiguration(
94+
KeyVaultName.Value(),
95+
KeyVaultTenantId.Value(),
96+
KeyVaultClientId.Value(),
97+
kvCertificate,
98+
sendX5c: true);
99+
}
86100

87101
var runner = new TestRunner(
88102
ConnectionString.Value(),

0 commit comments

Comments
 (0)