Skip to content

Commit e65f330

Browse files
w1amjosephcummings
andauthored
DEV-92 - Add missing option tlsCAFile (#281)
* Add tlsCAFile option * Put tests in separate files * Trust certificate * Verify certificate format and add more tests * Remove aliases, improve tests and add scripts - Remove connectivity settings alias - Change type of TlsCaFile - Add custom exception - Cleanup tests - Add gencert script for Windows, MacOS and WSL - Add handler in ChannelFactory * Combine wsl and linux and other things * Combine wsl and linux and other things * Refactor * Adjust gencert script for macOS --------- Co-authored-by: Joseph Cummings <[email protected]> Co-authored-by: Joseph Cummings <[email protected]>
1 parent 1173149 commit e65f330

File tree

9 files changed

+231
-69
lines changed

9 files changed

+231
-69
lines changed

.github/workflows/base.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
env:
4646
ES_DOCKER_TAG: ${{ inputs.docker-tag }}
4747
run: |
48-
./gencert.sh
48+
sudo ./gencert.sh
4949
dotnet test --configuration ${{ matrix.configuration }} --blame \
5050
--logger:"GitHubActions;report-warnings=false" --logger:"console;verbosity=normal" \
5151
--framework ${{ matrix.framework }} \

gencert.ps1

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Write-Host ">> Generating certificate..."
2+
3+
# Create directory if it doesn't exist
4+
New-Item -ItemType Directory -Path .\certs -Force
5+
6+
# Set permissions for the directory
7+
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)RX"
8+
9+
# Pull the Docker image
10+
docker pull eventstore/es-gencert-cli:1.0.2
11+
12+
# Create CA certificate
13+
docker run --rm --volume ${PWD}\certs:/tmp --user (Get-Process -Id $PID).SessionId eventstore/es-gencert-cli:1.0.2 create-ca -out /tmp/ca
14+
15+
# Create node certificate
16+
docker run --rm --volume ${PWD}\certs:/tmp --user (Get-Process -Id $PID).SessionId eventstore/es-gencert-cli:1.0.2 create-node -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/node -ip-addresses 127.0.0.1 -dns-names localhost
17+
18+
# Set permissions recursively for the directory
19+
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)RX"
20+
21+
Import-Certificate -FilePath ".\certs\ca\ca.crt" -CertStoreLocation Cert:\CurrentUser\Root

gencert.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
#!/usr/bin/env bash
22

3+
unameOutput="$(uname -sr)"
4+
case "${unameOutput}" in
5+
Linux*Microsoft*) machine=WSL;;
6+
Linux*) machine=Linux;;
7+
Darwin*) machine=MacOS;;
8+
*) machine="${unameOutput}"
9+
esac
10+
11+
echo ">> Generating certificate..."
312
mkdir -p certs
413

514
chmod 0755 ./certs
@@ -11,3 +20,15 @@ docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) eventstore/es-
1120
docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) eventstore/es-gencert-cli:1.0.2 create-node -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/node -ip-addresses 127.0.0.1 -dns-names localhost
1221

1322
chmod -R 0755 ./certs
23+
24+
if [ "${machine}" == "MacOS" ]; then
25+
echo ">> Installing certificate on ${machine}..."
26+
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain certs/ca/ca.crt
27+
elif [ "$machine" == "Linux" ] || [ "$machine" == "WSL" ]; then
28+
echo ">> Copying certificate..."
29+
cp certs/ca/ca.crt /usr/local/share/ca-certificates/eventstore_ca.crt
30+
echo ">> Installing certificate on ${machine}..."
31+
sudo update-ca-certificates
32+
else
33+
echo ">> Unknown platform. Please install the certificate manually."
34+
fi

src/EventStore.Client/ChannelFactory.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,40 @@ public static TChannel CreateChannel(EventStoreClientSettings settings, EndPoint
1616
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
1717
}
1818

19-
return TChannel.ForAddress(address, new GrpcChannelOptions {
19+
return TChannel.ForAddress(
20+
address,
21+
new GrpcChannelOptions {
2022
#if NET
21-
HttpClient = new HttpClient(CreateHandler(), true) {
22-
Timeout = System.Threading.Timeout.InfiniteTimeSpan,
23-
DefaultRequestVersion = new Version(2, 0)
24-
},
23+
HttpClient = new HttpClient(CreateHandler(), true) {
24+
Timeout = System.Threading.Timeout.InfiniteTimeSpan,
25+
DefaultRequestVersion = new Version(2, 0)
26+
},
2527
#else
2628
HttpHandler = CreateHandler(),
2729
#endif
28-
LoggerFactory = settings.LoggerFactory,
29-
Credentials = settings.ChannelCredentials,
30-
DisposeHttpClient = true,
31-
MaxReceiveMessageSize = MaxReceiveMessageLength
32-
});
33-
30+
LoggerFactory = settings.LoggerFactory,
31+
Credentials = settings.ChannelCredentials,
32+
DisposeHttpClient = true,
33+
MaxReceiveMessageSize = MaxReceiveMessageLength
34+
}
35+
);
36+
3437
HttpMessageHandler CreateHandler() {
3538
if (settings.CreateHttpMessageHandler != null) {
3639
return settings.CreateHttpMessageHandler.Invoke();
3740
}
41+
42+
var configureClientCert = settings.ConnectivitySettings is { TlsCaFile: not null, Insecure: false };
3843
#if NET
3944
var handler = new SocketsHttpHandler {
4045
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
4146
KeepAlivePingTimeout = settings.ConnectivitySettings.KeepAliveTimeout,
4247
EnableMultipleHttp2Connections = true,
4348
};
4449

50+
if (configureClientCert)
51+
handler.SslOptions.ClientCertificates = [settings.ConnectivitySettings.TlsCaFile!];
52+
4553
if (!settings.ConnectivitySettings.TlsVerifyCert) {
4654
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
4755
}
@@ -53,6 +61,9 @@ HttpMessageHandler CreateHandler() {
5361
EnableMultipleHttp2Connections = true
5462
};
5563

64+
if (configureClientCert)
65+
handler.ClientCertificates.Add(settings.ConnectivitySettings.TlsCaFile!);
66+
5667
if (!settings.ConnectivitySettings.TlsVerifyCert) {
5768
handler.ServerCertificateValidationCallback = delegate { return true; };
5869
}

src/EventStore.Client/EventStoreClientConnectivitySettings.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Net;
3+
using System.Security.Cryptography.X509Certificates;
34

45
namespace EventStore.Client {
56
/// <summary>
@@ -100,6 +101,12 @@ public bool Insecure {
100101
/// </summary>
101102
public bool TlsVerifyCert { get; set; } = true;
102103

104+
/// <summary>
105+
/// Path to a certificate file for secure connection. Not required for enabling secure connection. Useful for self-signed certificate
106+
/// that are not installed on the system trust store.
107+
/// </summary>
108+
public X509Certificate2? TlsCaFile { get; set; }
109+
103110
/// <summary>
104111
/// The default <see cref="EventStoreClientConnectivitySettings"/>.
105112
/// </summary>

0 commit comments

Comments
 (0)