-
-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): Add certificates to Operator.Web (#756)
This adds a new CertificateGenerator to Operator.Web, and it reworks a handful of the classes in Operator.Web to support using certificates in code. I more or less re-implemented the BouncyCastle solution from the CLI in Operator.Web using the built-in .NET classes, and I went to some length to ensure the generated certificates are virtually identical to those produced by BouncyCastle.
- Loading branch information
Showing
22 changed files
with
734 additions
and
333 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
|
||
namespace KubeOps.Abstractions.Certificates | ||
{ | ||
public record CertificatePair(X509Certificate2 Certificate, AsymmetricAlgorithm Key); | ||
} |
22 changes: 22 additions & 0 deletions
22
src/KubeOps.Abstractions/Certificates/ICertificateProvider.cs
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,22 @@ | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
|
||
namespace KubeOps.Abstractions.Certificates | ||
{ | ||
/// <summary> | ||
/// Defines properties for certificate/key pair so a custom certificate/key provider may be implemented. | ||
/// The provider is used by the CertificateWebhookService to provide a caBundle to the webhooks. | ||
/// </summary> | ||
public interface ICertificateProvider : IDisposable | ||
{ | ||
/// <summary> | ||
/// The server certificate and key. | ||
/// </summary> | ||
CertificatePair Server { get; } | ||
|
||
/// <summary> | ||
/// The root certificate and key. | ||
/// </summary> | ||
CertificatePair Root { get; } | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -1,23 +1,17 @@ | ||
using KubeOps.Cli.Certificates; | ||
using KubeOps.Cli.Output; | ||
using KubeOps.Operator.Web.Certificates; | ||
|
||
namespace KubeOps.Cli.Generators; | ||
|
||
internal class CertificateGenerator(string serverName, string namespaceName) : IConfigGenerator | ||
{ | ||
public void Generate(ResultOutput output) | ||
{ | ||
var (caCert, caKey) = Certificates.CertificateGenerator.CreateCaCertificate(); | ||
using Operator.Web.CertificateGenerator generator = new(serverName, namespaceName); | ||
|
||
output.Add("ca.pem", caCert.ToPem(), OutputFormat.Plain); | ||
output.Add("ca-key.pem", caKey.ToPem(), OutputFormat.Plain); | ||
|
||
var (srvCert, srvKey) = Certificates.CertificateGenerator.CreateServerCertificate( | ||
(caCert, caKey), | ||
serverName, | ||
namespaceName); | ||
|
||
output.Add("svc.pem", srvCert.ToPem(), OutputFormat.Plain); | ||
output.Add("svc-key.pem", srvKey.ToPem(), OutputFormat.Plain); | ||
output.Add("ca.pem", generator.Root.Certificate.EncodeToPem(), OutputFormat.Plain); | ||
output.Add("ca-key.pem", generator.Root.Key.EncodeToPem(), OutputFormat.Plain); | ||
output.Add("svc.pem", generator.Server.Certificate.EncodeToPem(), OutputFormat.Plain); | ||
output.Add("svc-key.pem", generator.Server.Key.EncodeToPem(), OutputFormat.Plain); | ||
} | ||
} |
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
58 changes: 58 additions & 0 deletions
58
src/KubeOps.Operator.Web/Certificates/CertificateExtensions.cs
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,58 @@ | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Text; | ||
|
||
using KubeOps.Abstractions.Certificates; | ||
|
||
namespace KubeOps.Operator.Web.Certificates | ||
{ | ||
public static class CertificateExtensions | ||
{ | ||
/// <summary> | ||
/// Encodes the certificate in PEM format for use in Kubernetes. | ||
/// </summary> | ||
/// <param name="certificate">The certificate to encode.</param> | ||
/// <returns>The byte representation of the PEM-encoded certificate.</returns> | ||
public static byte[] EncodeToPemBytes(this X509Certificate2 certificate) => Encoding.UTF8.GetBytes(certificate.EncodeToPem()); | ||
|
||
/// <summary> | ||
/// Encodes the certificate in PEM format. | ||
/// </summary> | ||
/// <param name="certificate">The certificate to encode.</param> | ||
/// <returns>The string representation of the PEM-encoded certificate.</returns> | ||
public static string EncodeToPem(this X509Certificate2 certificate) => new(PemEncoding.Write("CERTIFICATE", certificate.RawData)); | ||
|
||
/// <summary> | ||
/// Encodes the key in PEM format. | ||
/// </summary> | ||
/// <param name="key">The key to encode.</param> | ||
/// <returns>The string representation of the PEM-encoded key.</returns> | ||
public static string EncodeToPem(this AsymmetricAlgorithm key) => new(PemEncoding.Write("PRIVATE KEY", key.ExportPkcs8PrivateKey())); | ||
|
||
/// <summary> | ||
/// Generates a new server certificate with its private key attached, and sets <see cref="X509KeyStorageFlags.PersistKeySet"/>. | ||
/// For example, this certificate can be used in development environments to configure <see cref="Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions"/>. | ||
/// </summary> | ||
/// <param name="serverPair">The cert/key tuple to attach.</param> | ||
/// <returns>An <see cref="X509Certificate2"/> with the private key attached.</returns> | ||
/// <exception cref="NotImplementedException">The <see cref="AsymmetricAlgorithm"/> not have a CopyWithPrivateKey method, or the | ||
/// method has not been implemented in this extension.</exception> | ||
public static X509Certificate2 CopyServerCertWithPrivateKey(this CertificatePair serverPair) | ||
{ | ||
const string? password = null; | ||
using X509Certificate2 temp = serverPair.Key switch | ||
{ | ||
ECDsa ecdsa => serverPair.Certificate.CopyWithPrivateKey(ecdsa), | ||
RSA rsa => serverPair.Certificate.CopyWithPrivateKey(rsa), | ||
ECDiffieHellman ecdh => serverPair.Certificate.CopyWithPrivateKey(ecdh), | ||
DSA dsa => serverPair.Certificate.CopyWithPrivateKey(dsa), | ||
_ => throw new NotImplementedException($"{serverPair.Key} is not implemented for {nameof(CopyServerCertWithPrivateKey)}"), | ||
}; | ||
|
||
return new X509Certificate2( | ||
temp.Export(X509ContentType.Pfx, password), | ||
password, | ||
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); | ||
} | ||
} | ||
} |
Oops, something went wrong.