-
Notifications
You must be signed in to change notification settings - Fork 273
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP * Add certificate handling for auth * Add cert handling for upload.py * Remove certdeploy * Remove old constants * Add CertHelper code and build step * Move build step for CertHelper * Address PR feedback * Remove islinux * Add CertRotator to CertHelper * WIP * Fix improper list() usage * Yaml testing changes * Make RunCommand verbose * Testing * Add feature to RunCommand to not echo stdout * Remove pragma and fixup tests * Update moq version * Remove testing changes * Remove a few more testing changes * Remove last testing change * Add simple logging * Address pr feedback * Address PR feedback
- Loading branch information
1 parent
0db4c9b
commit 7b8c420
Showing
15 changed files
with
696 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Runtime.CompilerServices; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
[assembly: InternalsVisibleTo("CertHelperTests")] | ||
namespace CertHelper; | ||
internal class AssemblyInfo | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>$(PERFLAB_TARGET_FRAMEWORKS)</TargetFramework> | ||
<!-- Supported target frameworks --> | ||
<TargetFramework Condition="'$(TargetFramework)' == ''">net9.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Azure.Core" Version="1.44.1" /> | ||
<PackageReference Include="Azure.Identity" Version="1.11.4" /> | ||
<PackageReference Include="Azure.Security.KeyVault.Certificates" Version="4.7.0" /> | ||
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" /> | ||
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.12.35514.174 d17.12 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CertHelper", "CertHelper.csproj", "{165A37BD-2E9E-4D0A-8402-BB58C29A0BF4}" | ||
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CertRotatorTests", "..\CertHelperTests\CertRotatorTests.csproj", "{AEA0F93B-EC9B-4438-991E-A80C0C82B3D1}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{165A37BD-2E9E-4D0A-8402-BB58C29A0BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{165A37BD-2E9E-4D0A-8402-BB58C29A0BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{165A37BD-2E9E-4D0A-8402-BB58C29A0BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{165A37BD-2E9E-4D0A-8402-BB58C29A0BF4}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{AEA0F93B-EC9B-4438-991E-A80C0C82B3D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{AEA0F93B-EC9B-4438-991E-A80C0C82B3D1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{AEA0F93B-EC9B-4438-991E-A80C0C82B3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{AEA0F93B-EC9B-4438-991E-A80C0C82B3D1}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CertHelper; | ||
public class Constants | ||
{ | ||
public static readonly string Cert1Name = "LabCert1"; | ||
public static readonly string Cert2Name = "LabCert2"; | ||
public static readonly Uri Cert1Id = new Uri("https://test.vault.azure.net/certificates/LabCert1/07a7d98bf4884e5c40e690e02b96b3b4"); | ||
public static readonly Uri Cert2Id = new Uri("https://test.vault.azure.net/certificates/LabCert2/07a7d98bf4884e5c41e690e02b96b3b4"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CertHelper; | ||
public interface IX509Store | ||
{ | ||
X509Certificate2Collection Certificates { get; } | ||
string? Name { get; } | ||
StoreLocation Location { get; } | ||
X509Store GetX509Store(); | ||
} | ||
|
||
public class TestableX509Store : IX509Store | ||
{ | ||
public X509Certificate2Collection Certificates { get => store.Certificates; } | ||
|
||
public string? Name => store.Name; | ||
|
||
public StoreLocation Location => store.Location; | ||
|
||
private X509Store store; | ||
public TestableX509Store(OpenFlags flags = OpenFlags.ReadOnly) | ||
{ | ||
store = new X509Store(StoreName.My, StoreLocation.CurrentUser, flags); | ||
} | ||
|
||
public X509Store GetX509Store() | ||
{ | ||
return store; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using Azure; | ||
using Azure.Core; | ||
using Azure.Identity; | ||
using Azure.Security.KeyVault.Certificates; | ||
using Azure.Security.KeyVault.Secrets; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Runtime.CompilerServices; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace CertHelper; | ||
|
||
public class KeyVaultCert | ||
{ | ||
private readonly string _keyVaultUrl = "https://dotnetperfkeyvault.vault.azure.net/"; | ||
private readonly string _tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; | ||
private readonly string _clientId = "8c4b65ef-5a73-4d5a-a298-962d4a4ef7bc"; | ||
|
||
public X509Certificate2Collection KeyVaultCertificates { get; set; } | ||
public ILocalCert LocalCerts { get; set; } | ||
private TokenCredential _credential { get; set; } | ||
private CertificateClient _certClient { get; set; } | ||
private SecretClient _secretClient { get; set; } | ||
|
||
public KeyVaultCert(TokenCredential? cred = null, CertificateClient? certClient = null, SecretClient? secretClient = null, ILocalCert? localCerts = null) | ||
{ | ||
LocalCerts = localCerts ?? new LocalCert(); | ||
_credential = cred ?? GetCertifcateCredentialAsync(_tenantId, _clientId, LocalCerts.Certificates).Result; | ||
_certClient = certClient ?? new CertificateClient(new Uri(_keyVaultUrl), _credential); | ||
_secretClient = secretClient ?? new SecretClient(new Uri(_keyVaultUrl), _credential); | ||
KeyVaultCertificates = new X509Certificate2Collection(); | ||
} | ||
|
||
public async Task LoadKeyVaultCertsAsync() | ||
{ | ||
KeyVaultCertificates.Add(await FindCertificateInKeyVaultAsync(Constants.Cert1Name)); | ||
KeyVaultCertificates.Add(await FindCertificateInKeyVaultAsync(Constants.Cert2Name)); | ||
|
||
if (KeyVaultCertificates.Where(c => c == null).Count() > 0) | ||
{ | ||
throw new Exception("One or more certificates not found"); | ||
} | ||
} | ||
|
||
private async Task<ClientCertificateCredential> GetCertifcateCredentialAsync(string tenantId, string clientId, X509Certificate2Collection certCollection) | ||
{ | ||
ClientCertificateCredential? ccc = null; | ||
Exception? exception = null; | ||
foreach (var cert in certCollection) | ||
{ | ||
try | ||
{ | ||
ccc = new ClientCertificateCredential(tenantId, clientId, cert); | ||
await ccc.GetTokenAsync(new TokenRequestContext(new string[] { "https://vault.azure.net/.default" })); | ||
break; | ||
} | ||
catch (Exception ex) | ||
{ | ||
ccc = null; | ||
exception = ex; | ||
} | ||
} | ||
if(ccc == null) | ||
{ | ||
throw new Exception("Both certificates failed to authenticate", exception); | ||
} | ||
return ccc; | ||
} | ||
|
||
private async Task<X509Certificate2> FindCertificateInKeyVaultAsync(string certName) | ||
{ | ||
var keyVaultCert = await _certClient.GetCertificateAsync(certName); | ||
if(keyVaultCert.Value == null) | ||
{ | ||
throw new Exception("Certificate not found in Key Vault"); | ||
} | ||
var secret = await _secretClient.GetSecretAsync(keyVaultCert.Value.Name, keyVaultCert.Value.SecretId.Segments.Last()); | ||
if(secret.Value == null) | ||
{ | ||
throw new Exception("Certificate secret not found in Key Vault"); | ||
} | ||
var certBytes = Convert.FromBase64String(secret.Value.Value); | ||
#if NET9_0_OR_GREATER | ||
var cert = X509CertificateLoader.LoadPkcs12(certBytes, "", X509KeyStorageFlags.Exportable); | ||
#else | ||
var cert = new X509Certificate2(certBytes, "", X509KeyStorageFlags.Exportable); | ||
#endif | ||
return cert; | ||
} | ||
|
||
public bool ShouldRotateCerts() | ||
{ | ||
var keyVaultThumbprints = new HashSet<string>(); | ||
foreach (var cert in KeyVaultCertificates) | ||
{ | ||
keyVaultThumbprints.Add(cert.Thumbprint); | ||
} | ||
foreach(var cert in LocalCerts.Certificates) | ||
{ | ||
if (!keyVaultThumbprints.Contains(cert.Thumbprint)) | ||
{ | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
} |
Oops, something went wrong.