Skip to content

Commit

Permalink
feat: Add support for RSA private key (RsaPrivateCrtKeyParameters) TL…
Browse files Browse the repository at this point in the history
…S authentication with protected Docker daemon sockets (#978)

Co-authored-by: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com>
  • Loading branch information
zuntio and HofmeisterAn authored Aug 28, 2023
1 parent 99ff005 commit b121dde
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 21 deletions.
20 changes: 18 additions & 2 deletions src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace DotNet.Testcontainers.Builders
using DotNet.Testcontainers.Configurations;
using JetBrains.Annotations;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
Expand Down Expand Up @@ -79,11 +80,13 @@ private static X509Certificate2 CreateFromPemFile(string certPemFilePath, string

var password = Guid.NewGuid().ToString("D");

var keyPair = (AsymmetricCipherKeyPair)new PemReader(keyPairStream).ReadObject();
var keyObject = new PemReader(keyPairStream).ReadObject();

var certificateEntry = new X509CertificateEntry(certificate);

var keyEntry = new AsymmetricKeyEntry(keyPair.Private);
var keyParameter = ResolveKeyParameter(keyObject);

var keyEntry = new AsymmetricKeyEntry(keyParameter);
store.SetKeyEntry(certificate.SubjectDN + "_key", keyEntry, new[] { certificateEntry });

using (var certificateStream = new MemoryStream())
Expand All @@ -93,5 +96,18 @@ private static X509Certificate2 CreateFromPemFile(string certPemFilePath, string
}
}
}

private static AsymmetricKeyParameter ResolveKeyParameter(object keyObject)
{
switch (keyObject)
{
case AsymmetricCipherKeyPair ackp:
return ackp.Private;
case RsaPrivateCrtKeyParameters rpckp:
return rpckp;
default:
throw new ArgumentOutOfRangeException(nameof(keyObject), $"Unsupported asymmetric key entry encountered while trying to resolve key from input object '{keyObject.GetType()}'.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ namespace DotNet.Testcontainers.Tests.Fixtures
{
using System.Collections.Generic;
using DotNet.Testcontainers.Builders;
using JetBrains.Annotations;

[UsedImplicitly]
public sealed class DockerMTlsFixture : ProtectDockerDaemonSocket
public abstract class DockerMTls : ProtectDockerDaemonSocket
{
public DockerMTlsFixture()
: base(new ContainerBuilder())
public DockerMTls(string dockerImageVersion)
: base(new ContainerBuilder(), dockerImageVersion)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed class DockerTlsFixture : ProtectDockerDaemonSocket
{
public DockerTlsFixture()
: base(new ContainerBuilder()
.WithCommand("--tlsverify=false"))
.WithCommand("--tlsverify=false"), "20.10.18")
{
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace DotNet.Testcontainers.Tests.Fixtures
{
using JetBrains.Annotations;

[UsedImplicitly]
public sealed class OpenSsl1_1_1Fixture : DockerMTls
{
public OpenSsl1_1_1Fixture() : base("20.10.18")
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace DotNet.Testcontainers.Tests.Fixtures
{
using JetBrains.Annotations;

[UsedImplicitly]
public sealed class OpenSsl3_1Fixture : DockerMTls
{
public OpenSsl3_1Fixture() : base("24.0.5")
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ namespace DotNet.Testcontainers.Tests.Fixtures
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Images;
using Org.BouncyCastle.OpenSsl;
using Xunit;

public abstract class ProtectDockerDaemonSocket : IAsyncLifetime
{
public const string DockerVersion = "20.10.18";

private const string CertsDirectoryName = "certs";

private const ushort TlsPort = 2376;
Expand All @@ -22,14 +21,12 @@ public abstract class ProtectDockerDaemonSocket : IAsyncLifetime

private readonly string _containerCertsDirectoryPath = Path.Combine("/", CertsDirectoryName);

private readonly IImage _image = new DockerImage(string.Empty, "docker", DockerVersion + "-dind");

private readonly IContainer _container;

protected ProtectDockerDaemonSocket(ContainerBuilder containerConfiguration)
protected ProtectDockerDaemonSocket(ContainerBuilder containerConfiguration, string dockerImageVersion)
{
_container = containerConfiguration
.WithImage(_image)
.WithImage(new DockerImage(string.Empty, "docker", dockerImageVersion + "-dind"))
.WithPrivileged(true)
.WithPortBinding(TlsPort, true)
.WithBindMount(_hostCertsDirectoryPath, _containerCertsDirectoryPath, AccessMode.ReadWrite)
Expand All @@ -42,17 +39,28 @@ public virtual IList<string> CustomProperties
get
{
var customProperties = new List<string>();
customProperties.Add($"docker.host={TcpEndpoint}");
customProperties.Add($"docker.host={new UriBuilder("tcp", _container.Hostname, _container.GetMappedPublicPort(TlsPort))}");
customProperties.Add($"docker.cert.path={Path.Combine(_hostCertsDirectoryPath, "client")}");
return customProperties;
}
}

private Uri TcpEndpoint
public IImage Image
{
get
{
return _container.Image;
}
}

public object TlsKey
{
get
{
return new UriBuilder("tcp", _container.Hostname, _container.GetMappedPublicPort(TlsPort)).Uri;
using (var tlsKeyStream = new StreamReader(Path.Combine(_hostCertsDirectoryPath, "client", "key.pem")))
{
return new PemReader(tlsKeyStream).ReadObject();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace DotNet.Testcontainers.Tests.Unit
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Tests.Fixtures;
using Microsoft.Extensions.Logging.Abstractions;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Xunit;

public static class ProtectDockerDaemonSocketTest
Expand All @@ -18,12 +20,43 @@ private static IDockerEndpointAuthenticationConfiguration GetAuthConfig(ProtectD
return new IDockerEndpointAuthenticationProvider[] { new MTlsEndpointAuthenticationProvider(customConfiguration), new TlsEndpointAuthenticationProvider(customConfiguration) }.First(authProvider => authProvider.IsApplicable()).GetAuthConfig();
}

public sealed class MTls : IClassFixture<DockerMTlsFixture>
public sealed class MTlsOpenSsl1_1_1 : IClassFixture<OpenSsl1_1_1Fixture>
{
private readonly ProtectDockerDaemonSocket _fixture;

private readonly IDockerEndpointAuthenticationConfiguration _authConfig;

public MTlsOpenSsl1_1_1(OpenSsl1_1_1Fixture dockerMTlsFixture)
{
_fixture = dockerMTlsFixture;
_authConfig = GetAuthConfig(dockerMTlsFixture);
}

[Fact]
public async Task GetVersionReturnsVersion()
{
// Given
var client = new TestcontainersClient(Guid.Empty, _authConfig, NullLogger.Instance);

// When
var version = await client.System.GetVersionAsync()
.ConfigureAwait(false);

// Then
Assert.StartsWith(version.Version, _fixture.Image.Tag);
Assert.IsType<AsymmetricCipherKeyPair>(_fixture.TlsKey);
}
}

public sealed class MTlsOpenSsl3_1 : IClassFixture<OpenSsl3_1Fixture>
{
private readonly ProtectDockerDaemonSocket _fixture;

private readonly IDockerEndpointAuthenticationConfiguration _authConfig;

public MTls(DockerMTlsFixture dockerMTlsFixture)
public MTlsOpenSsl3_1(OpenSsl3_1Fixture dockerMTlsFixture)
{
_fixture = dockerMTlsFixture;
_authConfig = GetAuthConfig(dockerMTlsFixture);
}

Expand All @@ -38,16 +71,20 @@ public async Task GetVersionReturnsVersion()
.ConfigureAwait(false);

// Then
Assert.Equal(ProtectDockerDaemonSocket.DockerVersion, version.Version);
Assert.StartsWith(version.Version, _fixture.Image.Tag);
Assert.IsType<RsaPrivateCrtKeyParameters>(_fixture.TlsKey);
}
}

public sealed class Tls : IClassFixture<DockerTlsFixture>
{
private readonly ProtectDockerDaemonSocket _fixture;

private readonly IDockerEndpointAuthenticationConfiguration _authConfig;

public Tls(DockerTlsFixture dockerTlsFixture)
{
_fixture = dockerTlsFixture;
_authConfig = GetAuthConfig(dockerTlsFixture);
}

Expand All @@ -62,7 +99,7 @@ public async Task GetVersionReturnsVersion()
.ConfigureAwait(false);

// Then
Assert.Equal(ProtectDockerDaemonSocket.DockerVersion, version.Version);
Assert.StartsWith(version.Version, _fixture.Image.Tag);
}
}
}
Expand Down

0 comments on commit b121dde

Please sign in to comment.