Skip to content

Commit b121dde

Browse files
zuntioHofmeisterAn
andauthored
feat: Add support for RSA private key (RsaPrivateCrtKeyParameters) TLS authentication with protected Docker daemon sockets (#978)
Co-authored-by: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com>
1 parent 99ff005 commit b121dde

File tree

7 files changed

+104
-21
lines changed

7 files changed

+104
-21
lines changed

src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs

+18-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace DotNet.Testcontainers.Builders
88
using DotNet.Testcontainers.Configurations;
99
using JetBrains.Annotations;
1010
using Org.BouncyCastle.Crypto;
11+
using Org.BouncyCastle.Crypto.Parameters;
1112
using Org.BouncyCastle.OpenSsl;
1213
using Org.BouncyCastle.Pkcs;
1314
using Org.BouncyCastle.Security;
@@ -79,11 +80,13 @@ private static X509Certificate2 CreateFromPemFile(string certPemFilePath, string
7980

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

82-
var keyPair = (AsymmetricCipherKeyPair)new PemReader(keyPairStream).ReadObject();
83+
var keyObject = new PemReader(keyPairStream).ReadObject();
8384

8485
var certificateEntry = new X509CertificateEntry(certificate);
8586

86-
var keyEntry = new AsymmetricKeyEntry(keyPair.Private);
87+
var keyParameter = ResolveKeyParameter(keyObject);
88+
89+
var keyEntry = new AsymmetricKeyEntry(keyParameter);
8790
store.SetKeyEntry(certificate.SubjectDN + "_key", keyEntry, new[] { certificateEntry });
8891

8992
using (var certificateStream = new MemoryStream())
@@ -93,5 +96,18 @@ private static X509Certificate2 CreateFromPemFile(string certPemFilePath, string
9396
}
9497
}
9598
}
99+
100+
private static AsymmetricKeyParameter ResolveKeyParameter(object keyObject)
101+
{
102+
switch (keyObject)
103+
{
104+
case AsymmetricCipherKeyPair ackp:
105+
return ackp.Private;
106+
case RsaPrivateCrtKeyParameters rpckp:
107+
return rpckp;
108+
default:
109+
throw new ArgumentOutOfRangeException(nameof(keyObject), $"Unsupported asymmetric key entry encountered while trying to resolve key from input object '{keyObject.GetType()}'.");
110+
}
111+
}
96112
}
97113
}

tests/Testcontainers.Tests/Fixtures/Containers/Unix/DockerMTlsFixture.cs tests/Testcontainers.Tests/Fixtures/Containers/Unix/DockerMTls.cs

+3-5
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ namespace DotNet.Testcontainers.Tests.Fixtures
22
{
33
using System.Collections.Generic;
44
using DotNet.Testcontainers.Builders;
5-
using JetBrains.Annotations;
65

7-
[UsedImplicitly]
8-
public sealed class DockerMTlsFixture : ProtectDockerDaemonSocket
6+
public abstract class DockerMTls : ProtectDockerDaemonSocket
97
{
10-
public DockerMTlsFixture()
11-
: base(new ContainerBuilder())
8+
public DockerMTls(string dockerImageVersion)
9+
: base(new ContainerBuilder(), dockerImageVersion)
1210
{
1311
}
1412

tests/Testcontainers.Tests/Fixtures/Containers/Unix/DockerTlsFixture.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public sealed class DockerTlsFixture : ProtectDockerDaemonSocket
99
{
1010
public DockerTlsFixture()
1111
: base(new ContainerBuilder()
12-
.WithCommand("--tlsverify=false"))
12+
.WithCommand("--tlsverify=false"), "20.10.18")
1313
{
1414
}
1515

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace DotNet.Testcontainers.Tests.Fixtures
2+
{
3+
using JetBrains.Annotations;
4+
5+
[UsedImplicitly]
6+
public sealed class OpenSsl1_1_1Fixture : DockerMTls
7+
{
8+
public OpenSsl1_1_1Fixture() : base("20.10.18")
9+
{
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace DotNet.Testcontainers.Tests.Fixtures
2+
{
3+
using JetBrains.Annotations;
4+
5+
[UsedImplicitly]
6+
public sealed class OpenSsl3_1Fixture : DockerMTls
7+
{
8+
public OpenSsl3_1Fixture() : base("24.0.5")
9+
{
10+
}
11+
}
12+
}

tests/Testcontainers.Tests/Fixtures/Containers/Unix/ProtectDockerDaemonSocket.cs

+17-9
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ namespace DotNet.Testcontainers.Tests.Fixtures
88
using DotNet.Testcontainers.Configurations;
99
using DotNet.Testcontainers.Containers;
1010
using DotNet.Testcontainers.Images;
11+
using Org.BouncyCastle.OpenSsl;
1112
using Xunit;
1213

1314
public abstract class ProtectDockerDaemonSocket : IAsyncLifetime
1415
{
15-
public const string DockerVersion = "20.10.18";
16-
1716
private const string CertsDirectoryName = "certs";
1817

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

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

25-
private readonly IImage _image = new DockerImage(string.Empty, "docker", DockerVersion + "-dind");
26-
2724
private readonly IContainer _container;
2825

29-
protected ProtectDockerDaemonSocket(ContainerBuilder containerConfiguration)
26+
protected ProtectDockerDaemonSocket(ContainerBuilder containerConfiguration, string dockerImageVersion)
3027
{
3128
_container = containerConfiguration
32-
.WithImage(_image)
29+
.WithImage(new DockerImage(string.Empty, "docker", dockerImageVersion + "-dind"))
3330
.WithPrivileged(true)
3431
.WithPortBinding(TlsPort, true)
3532
.WithBindMount(_hostCertsDirectoryPath, _containerCertsDirectoryPath, AccessMode.ReadWrite)
@@ -42,17 +39,28 @@ public virtual IList<string> CustomProperties
4239
get
4340
{
4441
var customProperties = new List<string>();
45-
customProperties.Add($"docker.host={TcpEndpoint}");
42+
customProperties.Add($"docker.host={new UriBuilder("tcp", _container.Hostname, _container.GetMappedPublicPort(TlsPort))}");
4643
customProperties.Add($"docker.cert.path={Path.Combine(_hostCertsDirectoryPath, "client")}");
4744
return customProperties;
4845
}
4946
}
5047

51-
private Uri TcpEndpoint
48+
public IImage Image
49+
{
50+
get
51+
{
52+
return _container.Image;
53+
}
54+
}
55+
56+
public object TlsKey
5257
{
5358
get
5459
{
55-
return new UriBuilder("tcp", _container.Hostname, _container.GetMappedPublicPort(TlsPort)).Uri;
60+
using (var tlsKeyStream = new StreamReader(Path.Combine(_hostCertsDirectoryPath, "client", "key.pem")))
61+
{
62+
return new PemReader(tlsKeyStream).ReadObject();
63+
}
5664
}
5765
}
5866

tests/Testcontainers.Tests/Unit/Containers/Unix/ProtectDockerDaemonSocketTest.cs

+41-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace DotNet.Testcontainers.Tests.Unit
88
using DotNet.Testcontainers.Configurations;
99
using DotNet.Testcontainers.Tests.Fixtures;
1010
using Microsoft.Extensions.Logging.Abstractions;
11+
using Org.BouncyCastle.Crypto;
12+
using Org.BouncyCastle.Crypto.Parameters;
1113
using Xunit;
1214

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

21-
public sealed class MTls : IClassFixture<DockerMTlsFixture>
23+
public sealed class MTlsOpenSsl1_1_1 : IClassFixture<OpenSsl1_1_1Fixture>
2224
{
25+
private readonly ProtectDockerDaemonSocket _fixture;
26+
27+
private readonly IDockerEndpointAuthenticationConfiguration _authConfig;
28+
29+
public MTlsOpenSsl1_1_1(OpenSsl1_1_1Fixture dockerMTlsFixture)
30+
{
31+
_fixture = dockerMTlsFixture;
32+
_authConfig = GetAuthConfig(dockerMTlsFixture);
33+
}
34+
35+
[Fact]
36+
public async Task GetVersionReturnsVersion()
37+
{
38+
// Given
39+
var client = new TestcontainersClient(Guid.Empty, _authConfig, NullLogger.Instance);
40+
41+
// When
42+
var version = await client.System.GetVersionAsync()
43+
.ConfigureAwait(false);
44+
45+
// Then
46+
Assert.StartsWith(version.Version, _fixture.Image.Tag);
47+
Assert.IsType<AsymmetricCipherKeyPair>(_fixture.TlsKey);
48+
}
49+
}
50+
51+
public sealed class MTlsOpenSsl3_1 : IClassFixture<OpenSsl3_1Fixture>
52+
{
53+
private readonly ProtectDockerDaemonSocket _fixture;
54+
2355
private readonly IDockerEndpointAuthenticationConfiguration _authConfig;
2456

25-
public MTls(DockerMTlsFixture dockerMTlsFixture)
57+
public MTlsOpenSsl3_1(OpenSsl3_1Fixture dockerMTlsFixture)
2658
{
59+
_fixture = dockerMTlsFixture;
2760
_authConfig = GetAuthConfig(dockerMTlsFixture);
2861
}
2962

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

4073
// Then
41-
Assert.Equal(ProtectDockerDaemonSocket.DockerVersion, version.Version);
74+
Assert.StartsWith(version.Version, _fixture.Image.Tag);
75+
Assert.IsType<RsaPrivateCrtKeyParameters>(_fixture.TlsKey);
4276
}
4377
}
4478

4579
public sealed class Tls : IClassFixture<DockerTlsFixture>
4680
{
81+
private readonly ProtectDockerDaemonSocket _fixture;
82+
4783
private readonly IDockerEndpointAuthenticationConfiguration _authConfig;
4884

4985
public Tls(DockerTlsFixture dockerTlsFixture)
5086
{
87+
_fixture = dockerTlsFixture;
5188
_authConfig = GetAuthConfig(dockerTlsFixture);
5289
}
5390

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

64101
// Then
65-
Assert.Equal(ProtectDockerDaemonSocket.DockerVersion, version.Version);
102+
Assert.StartsWith(version.Version, _fixture.Image.Tag);
66103
}
67104
}
68105
}

0 commit comments

Comments
 (0)