-
-
Notifications
You must be signed in to change notification settings - Fork 288
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Co-authored-by: Andre Hofmeister <[email protected]>
- Loading branch information
1 parent
aba9f75
commit 480fc82
Showing
18 changed files
with
512 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
95 changes: 95 additions & 0 deletions
95
src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.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,95 @@ | ||
namespace DotNet.Testcontainers.Builders | ||
{ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Security.Cryptography.X509Certificates; | ||
using Docker.DotNet.X509; | ||
using DotNet.Testcontainers.Configurations; | ||
using Org.BouncyCastle.Crypto; | ||
using Org.BouncyCastle.OpenSsl; | ||
using Org.BouncyCastle.Pkcs; | ||
using Org.BouncyCastle.Security; | ||
using Org.BouncyCastle.X509; | ||
|
||
/// <inheritdoc cref="IDockerRegistryAuthenticationProvider" /> | ||
internal sealed class MTlsEndpointAuthenticationProvider : TlsEndpointAuthenticationProvider | ||
{ | ||
private static readonly X509CertificateParser CertificateParser = new X509CertificateParser(); | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MTlsEndpointAuthenticationProvider" /> class. | ||
/// </summary> | ||
public MTlsEndpointAuthenticationProvider() | ||
: this(PropertiesFileConfiguration.Instance, EnvironmentConfiguration.Instance) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MTlsEndpointAuthenticationProvider" /> class. | ||
/// </summary> | ||
/// <param name="customConfigurations">A list of custom configurations.</param> | ||
public MTlsEndpointAuthenticationProvider(params ICustomConfiguration[] customConfigurations) | ||
: base(customConfigurations) | ||
{ | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override bool IsApplicable() | ||
{ | ||
var certificatesFiles = new[] { ClientCertificateFileName, ClientCertificateKeyFileName }; | ||
return this.TlsEnabled && this.TlsVerifyEnabled && certificatesFiles.Select(fileName => Path.Combine(this.CertificatesDirectoryPath, fileName)).All(File.Exists); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override IDockerEndpointAuthenticationConfiguration GetAuthConfig() | ||
{ | ||
var credentials = new CertificateCredentials(this.GetClientCertificate()); | ||
credentials.ServerCertificateValidationCallback = this.ServerCertificateValidationCallback; | ||
return new DockerEndpointAuthenticationConfiguration(this.DockerEngine, credentials); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override X509Certificate2 GetClientCertificate() | ||
{ | ||
var clientCertificateFilePath = Path.Combine(this.CertificatesDirectoryPath, ClientCertificateFileName); | ||
var clientCertificateKeyFilePath = Path.Combine(this.CertificatesDirectoryPath, ClientCertificateKeyFileName); | ||
return CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath); | ||
} | ||
|
||
private static X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath) | ||
{ | ||
if (!File.Exists(certPemFilePath)) | ||
{ | ||
throw new FileNotFoundException(certPemFilePath); | ||
} | ||
|
||
if (!File.Exists(keyPemFilePath)) | ||
{ | ||
throw new FileNotFoundException(keyPemFilePath); | ||
} | ||
|
||
using (var keyPairStream = new StreamReader(keyPemFilePath)) | ||
{ | ||
var store = new Pkcs12StoreBuilder().Build(); | ||
|
||
var certificate = CertificateParser.ReadCertificate(File.ReadAllBytes(certPemFilePath)); | ||
|
||
var password = Guid.NewGuid().ToString("D"); | ||
|
||
var keyPair = (AsymmetricCipherKeyPair)new PemReader(keyPairStream).ReadObject(); | ||
|
||
var certificateEntry = new X509CertificateEntry(certificate); | ||
|
||
var keyEntry = new AsymmetricKeyEntry(keyPair.Private); | ||
store.SetKeyEntry(certificate.SubjectDN + "_key", keyEntry, new[] { certificateEntry }); | ||
|
||
using (var certificateStream = new MemoryStream()) | ||
{ | ||
store.Save(certificateStream, password.ToCharArray(), new SecureRandom()); | ||
return new X509Certificate2(Pkcs12Utilities.ConvertToDefiniteLength(certificateStream.ToArray()), password); | ||
} | ||
} | ||
} | ||
} | ||
} |
115 changes: 115 additions & 0 deletions
115
src/Testcontainers/Builders/TlsEndpointAuthenticationProvider.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,115 @@ | ||
namespace DotNet.Testcontainers.Builders | ||
{ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.Security; | ||
using System.Security.Cryptography.X509Certificates; | ||
using DotNet.Testcontainers.Configurations; | ||
|
||
/// <inheritdoc cref="IDockerRegistryAuthenticationProvider" /> | ||
internal class TlsEndpointAuthenticationProvider : DockerEndpointAuthenticationProvider | ||
{ | ||
protected const string CaCertificateFileName = "ca.pem"; | ||
|
||
protected const string ClientCertificateFileName = "cert.pem"; | ||
|
||
protected const string ClientCertificateKeyFileName = "key.pem"; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TlsEndpointAuthenticationProvider" /> class. | ||
/// </summary> | ||
public TlsEndpointAuthenticationProvider() | ||
: this(PropertiesFileConfiguration.Instance, EnvironmentConfiguration.Instance) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TlsEndpointAuthenticationProvider" /> class. | ||
/// </summary> | ||
/// <param name="customConfigurations">A list of custom configurations.</param> | ||
public TlsEndpointAuthenticationProvider(params ICustomConfiguration[] customConfigurations) | ||
: this(customConfigurations | ||
.OrderByDescending(item => item.GetDockerTlsVerify()) | ||
.ThenByDescending(item => item.GetDockerTls()) | ||
.DefaultIfEmpty(new PropertiesFileConfiguration(Array.Empty<string>())) | ||
.First()) | ||
{ | ||
} | ||
|
||
private TlsEndpointAuthenticationProvider(ICustomConfiguration customConfiguration) | ||
{ | ||
this.TlsEnabled = customConfiguration.GetDockerTls() || customConfiguration.GetDockerTlsVerify(); | ||
this.TlsVerifyEnabled = customConfiguration.GetDockerTlsVerify(); | ||
this.CertificatesDirectoryPath = customConfiguration.GetDockerCertPath() ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".docker"); | ||
this.DockerEngine = customConfiguration.GetDockerHost() ?? new Uri("tcp://localhost:2376"); | ||
} | ||
|
||
protected bool TlsEnabled { get; } | ||
|
||
protected bool TlsVerifyEnabled { get; } | ||
|
||
protected string CertificatesDirectoryPath { get; } | ||
|
||
protected Uri DockerEngine { get; } | ||
|
||
/// <inheritdoc /> | ||
public override bool IsApplicable() | ||
{ | ||
return this.TlsEnabled; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override IDockerEndpointAuthenticationConfiguration GetAuthConfig() | ||
{ | ||
var credentials = new TlsCredentials(); | ||
credentials.ServerCertificateValidationCallback = this.ServerCertificateValidationCallback; | ||
return new DockerEndpointAuthenticationConfiguration(this.DockerEngine, credentials); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the root certificate authority (CA). | ||
/// </summary> | ||
/// <returns>The root certificate authority (CA).</returns> | ||
protected virtual X509Certificate2 GetCaCertificate() | ||
{ | ||
return new X509Certificate2(Path.Combine(this.CertificatesDirectoryPath, CaCertificateFileName)); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the client certificate. | ||
/// </summary> | ||
/// <returns>The client certificate.</returns> | ||
protected virtual X509Certificate2 GetClientCertificate() | ||
{ | ||
return null; | ||
} | ||
|
||
/// <inheritdoc cref="ServicePointManager.ServerCertificateValidationCallback" /> | ||
protected virtual bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) | ||
{ | ||
switch (sslPolicyErrors) | ||
{ | ||
case SslPolicyErrors.None: | ||
return true; | ||
case SslPolicyErrors.RemoteCertificateNameMismatch: | ||
case SslPolicyErrors.RemoteCertificateNotAvailable: | ||
return false; | ||
case SslPolicyErrors.RemoteCertificateChainErrors: | ||
default: | ||
using (var caCertificate = this.GetCaCertificate()) | ||
{ | ||
var validationChain = new X509Chain(); | ||
validationChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; | ||
validationChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; | ||
validationChain.ChainPolicy.ExtraStore.Add(caCertificate); | ||
validationChain.ChainPolicy.ExtraStore.AddRange(chain.ChainElements.OfType<X509ChainElement>().Select(element => element.Certificate).ToArray()); | ||
var isVerified = validationChain.Build(new X509Certificate2(certificate)); | ||
var isSignedByExpectedRoot = validationChain.ChainElements[validationChain.ChainElements.Count - 1].Certificate.RawData.SequenceEqual(caCertificate.RawData); | ||
return isVerified && isSignedByExpectedRoot; | ||
} | ||
} | ||
} | ||
} | ||
} |
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
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,27 @@ | ||
namespace DotNet.Testcontainers.Configurations | ||
{ | ||
using System.Net; | ||
using System.Net.Http; | ||
using Docker.DotNet.X509; | ||
using Microsoft.Net.Http.Client; | ||
|
||
internal sealed class TlsCredentials : CertificateCredentials | ||
{ | ||
public TlsCredentials() | ||
: base(null) | ||
{ | ||
} | ||
|
||
public override bool IsTlsCredentials() | ||
{ | ||
return true; | ||
} | ||
|
||
public override HttpMessageHandler GetHandler(HttpMessageHandler innerHandler) | ||
{ | ||
var handler = (ManagedHandler)innerHandler; | ||
handler.ServerCertificateValidationCallback = this.ServerCertificateValidationCallback ?? ServicePointManager.ServerCertificateValidationCallback; | ||
return handler; | ||
} | ||
} | ||
} |
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
27 changes: 27 additions & 0 deletions
27
tests/Testcontainers.Tests/Fixtures/Containers/Unix/DockerMTlsFixture.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,27 @@ | ||
namespace DotNet.Testcontainers.Tests.Fixtures | ||
{ | ||
using System.Collections.Generic; | ||
using DotNet.Testcontainers.Builders; | ||
using DotNet.Testcontainers.Containers; | ||
using JetBrains.Annotations; | ||
|
||
[UsedImplicitly] | ||
public sealed class DockerMTlsFixture : ProtectDockerDaemonSocket | ||
{ | ||
public DockerMTlsFixture() | ||
: base(new TestcontainersBuilder<TestcontainersContainer>()) | ||
{ | ||
} | ||
|
||
public override IList<string> CustomProperties | ||
{ | ||
get | ||
{ | ||
var customProperties = base.CustomProperties; | ||
customProperties.Add("docker.tls=false"); | ||
customProperties.Add("docker.tls.verify=true"); | ||
return customProperties; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.