Skip to content

Commit

Permalink
DEV-92 - Add missing option tlsCAFile (#281)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Feb 26, 2024
1 parent 1173149 commit e65f330
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
env:
ES_DOCKER_TAG: ${{ inputs.docker-tag }}
run: |
./gencert.sh
sudo ./gencert.sh
dotnet test --configuration ${{ matrix.configuration }} --blame \
--logger:"GitHubActions;report-warnings=false" --logger:"console;verbosity=normal" \
--framework ${{ matrix.framework }} \
Expand Down
21 changes: 21 additions & 0 deletions gencert.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Write-Host ">> Generating certificate..."

# Create directory if it doesn't exist
New-Item -ItemType Directory -Path .\certs -Force

# Set permissions for the directory
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)RX"

# Pull the Docker image
docker pull eventstore/es-gencert-cli:1.0.2

# Create CA certificate
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

# Create node certificate
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

# Set permissions recursively for the directory
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)RX"

Import-Certificate -FilePath ".\certs\ca\ca.crt" -CertStoreLocation Cert:\CurrentUser\Root
21 changes: 21 additions & 0 deletions gencert.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
#!/usr/bin/env bash

unameOutput="$(uname -sr)"
case "${unameOutput}" in
Linux*Microsoft*) machine=WSL;;
Linux*) machine=Linux;;
Darwin*) machine=MacOS;;
*) machine="${unameOutput}"
esac

echo ">> Generating certificate..."
mkdir -p certs

chmod 0755 ./certs
Expand All @@ -11,3 +20,15 @@ docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) eventstore/es-
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

chmod -R 0755 ./certs

if [ "${machine}" == "MacOS" ]; then
echo ">> Installing certificate on ${machine}..."
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain certs/ca/ca.crt
elif [ "$machine" == "Linux" ] || [ "$machine" == "WSL" ]; then
echo ">> Copying certificate..."
cp certs/ca/ca.crt /usr/local/share/ca-certificates/eventstore_ca.crt
echo ">> Installing certificate on ${machine}..."
sudo update-ca-certificates
else
echo ">> Unknown platform. Please install the certificate manually."
fi
33 changes: 22 additions & 11 deletions src/EventStore.Client/ChannelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,40 @@ public static TChannel CreateChannel(EventStoreClientSettings settings, EndPoint
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
}

return TChannel.ForAddress(address, new GrpcChannelOptions {
return TChannel.ForAddress(
address,
new GrpcChannelOptions {
#if NET
HttpClient = new HttpClient(CreateHandler(), true) {
Timeout = System.Threading.Timeout.InfiniteTimeSpan,
DefaultRequestVersion = new Version(2, 0)
},
HttpClient = new HttpClient(CreateHandler(), true) {
Timeout = System.Threading.Timeout.InfiniteTimeSpan,
DefaultRequestVersion = new Version(2, 0)
},
#else
HttpHandler = CreateHandler(),
#endif
LoggerFactory = settings.LoggerFactory,
Credentials = settings.ChannelCredentials,
DisposeHttpClient = true,
MaxReceiveMessageSize = MaxReceiveMessageLength
});

LoggerFactory = settings.LoggerFactory,
Credentials = settings.ChannelCredentials,
DisposeHttpClient = true,
MaxReceiveMessageSize = MaxReceiveMessageLength
}
);

HttpMessageHandler CreateHandler() {
if (settings.CreateHttpMessageHandler != null) {
return settings.CreateHttpMessageHandler.Invoke();
}

var configureClientCert = settings.ConnectivitySettings is { TlsCaFile: not null, Insecure: false };
#if NET
var handler = new SocketsHttpHandler {
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
KeepAlivePingTimeout = settings.ConnectivitySettings.KeepAliveTimeout,
EnableMultipleHttp2Connections = true,
};

if (configureClientCert)
handler.SslOptions.ClientCertificates = [settings.ConnectivitySettings.TlsCaFile!];

if (!settings.ConnectivitySettings.TlsVerifyCert) {
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
}
Expand All @@ -53,6 +61,9 @@ HttpMessageHandler CreateHandler() {
EnableMultipleHttp2Connections = true
};

if (configureClientCert)
handler.ClientCertificates.Add(settings.ConnectivitySettings.TlsCaFile!);

if (!settings.ConnectivitySettings.TlsVerifyCert) {
handler.ServerCertificateValidationCallback = delegate { return true; };
}
Expand Down
7 changes: 7 additions & 0 deletions src/EventStore.Client/EventStoreClientConnectivitySettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace EventStore.Client {
/// <summary>
Expand Down Expand Up @@ -100,6 +101,12 @@ public bool Insecure {
/// </summary>
public bool TlsVerifyCert { get; set; } = true;

/// <summary>
/// Path to a certificate file for secure connection. Not required for enabling secure connection. Useful for self-signed certificate
/// that are not installed on the system trust store.
/// </summary>
public X509Certificate2? TlsCaFile { get; set; }

/// <summary>
/// The default <see cref="EventStoreClientConnectivitySettings"/>.
/// </summary>
Expand Down
Loading

0 comments on commit e65f330

Please sign in to comment.