Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove as much of the WINDOWS_CERT compiler directives as possible #1352

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

zentron
Copy link
Contributor

@zentron zentron commented Sep 26, 2024

Background

This PR essentially moves away a bunch of #if WINDOWS_CERTIFICATE_STORE_SUPPORT compiler directives to pinpoint the code that is currently (as of this PR) unable to be compiled for .netcore.

The follow up PR takes the next leap and attempts to provide capabilities for netcore to involve the few places that are not supported as discovered by these changes.

Whats Changed

What this turns out to be is essentially just the code that deals with assigning access to windows certificates. Although now then exposes in netcore classes that uses unmanaged code (windows dlls etc), these code paths should only ever exist on tasks running on windows machines (or they would have otherwise been failing in the past anyway, who's trying to run IIS steps on linux?)

How to review

Hopefully just a sanity check, nothing functionally should really change for users.

@@ -81,7 +81,7 @@ protected virtual int Run(string[] args)
}
catch (Exception ex)
{
return ConsoleFormatter.PrintError(ConsoleLog.Instance, ex);
return ConsoleFormatter.PrintError(Log, ex);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change allows us to capture the output properly when debugging tests

@@ -115,6 +114,8 @@ public static byte[] GetCspPrivateKeySecurity(SafeCspHandle cspHandle)
return buffer;
}

// At the moment these models are not in the Calamari Solution. This is the next step.
#if WINDOWS_CERTIFICATE_STORE_SUPPORT
public static byte[] GetCngPrivateKeySecurity(SafeNCryptKeyHandle hObject)
Copy link
Contributor Author

@zentron zentron Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SafeNCryptKeyHandle structures are not currently defined in the Octopus solution like some of the others, and so this will need to be introduced as netcore does not have access to them from mscorlib like natframework does.

To keep the changes in multiple stages, this will be introduced in a followup PR.
As such we can generally still assume that for the sake of this PR, most of the cert capabilities are fundamentally still only available in netfx, we are just starting to expose and test some of the classes in netcore.

@@ -69,14 +68,7 @@ internal static extern bool CryptAcquireCertificatePrivateKey(SafeCertContextHan
[Out] out int dwKeySpec,
[Out, MarshalAs(UnmanagedType.Bool)] out bool pfCallerFreeProvOrNCryptKey);

[DllImport("Crypt32.dll", SetLastError = true)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shuffled down to put between the other unmanaged invocations that are currently only available in netfx for this PR

}

// This should only be used in tests
public WindowsX509CertificateStore(): this(ConsoleLog.Instance)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to pass through the ILog to help with tests looking for specific output, but other logs dont care and so this default constructor makes it a little easier for those scenarios.


store.Close();
}
}

public CryptoKeySecurity GetPrivateKeySecurity(string thumbprint, StoreLocation storeLocation, string storeName)
Copy link
Contributor Author

@zentron zentron Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted out into the CryptoKeySecurityAccessRules class so that we can more cleanly control the differences between netcore and netfx

using Calamari.Tests.Helpers;
using Calamari.Tests.Helpers.Certificates;
using NUnit.Framework;
using Octostache;

namespace Calamari.Tests.Fixtures.Certificates
{
[Category(TestCategory.CompatibleOS.OnlyWindows)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously this whole test wouldnt even exist in netcore, now netcore can run some of these tests so long as its on windows

@@ -53,12 +54,15 @@ public void AddingCertToStore_AddsCert()
cert.EnsureCertificateNotInStore(storeName, certificateStoreLocation);
}

#if WINDOWS_CERTIFICATE_STORE_SUPPORT
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test still relies on some of the capabilities not yet available to netcore (see above), it will be exposed again in a later PR

@@ -1,6 +1,4 @@
#if WINDOWS_CERTIFICATE_STORE_SUPPORT
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bunch of the file changes are just this, removing the #if WINDOWS_CERTIFICATE_STORE_SUPPORT compiler directive, meaning it compiles into netcore, even if it shouldnt actually be getting invoked by anything (since the Calamari.netfx flavour is used for anything on Windows that would have the IIS/WindowsCert variables provided)

namespace Calamari.Integration.Certificates
{
#if WINDOWS_CERTIFICATE_STORE_SUPPORT
public static class CryptoKeySecurityAccessRules
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is just the extraction of a bunch of CryptoKey logic that was moved out of WindowsX509CertificateStore that cannot currently compile in netcore. The functionality is essentially unchanged, with the addition of a compiler directive returning the "failing" implementation to be used by netcore (not actually used-used, but compiled for)

return;
}

throw new CryptographicException(error);
}

Log.Info($"Imported certificate '{subjectName}' into store '{storeName}'");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of these static Log methods made testing using the in-process Calamari fail (as opposed to many tests which actually invoke Calamari.exe and make debugging impossible)

@@ -406,116 +389,6 @@ static IList<SafeCertContextHandle> GetCertificatesFromPfx(byte[] pfxBytes, stri
}
}

static void AddPrivateKeyAccessRules(ICollection<PrivateKeyAccessRule> accessRules, SafeCertContextHandle certificate)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved out to CryptoKeySecurityAccessRules

CalamariResult Invoke(VariableDictionary variables)
{
using (var variablesFile = new TemporaryFile(Path.GetTempFileName()))
{
variables.Save(variablesFile.FilePath);
return Invoke(Calamari()
return InvokeInProcess(Calamari()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This simply runs the test in the same process as the test harness, making debugging possible.
I'd like to move all Calamari tests to this format now that we have a bit more of a handle on the different runtimes that we are actually running

@@ -221,12 +227,22 @@ public void ImportExistingCertificateShouldNotOverwriteExistingPrivateKeyRights(
Convert.FromBase64String(sampleCertificate.Base64Bytes()), sampleCertificate.Password,
storeLocation, storeName, sampleCertificate.HasPrivateKey);

var privateKeySecurity = new WindowsX509CertificateStore().GetPrivateKeySecurity(sampleCertificate.Thumbprint,
storeLocation, storeName);
var privateKeySecurity = CryptoKeySecurityAccessRules.GetPrivateKeySecurity(sampleCertificate.Thumbprint,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the CryptoKeySecurityAccessRule method is extracted, we can actually consolidate some of the test assertions that check for certificate access rules (See Below)

@zentron zentron marked this pull request as ready for review October 1, 2024 21:58
Copy link
Contributor

@APErebus APErebus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants