Skip to content

Commit

Permalink
Fix channel factory to use root ca if provided
Browse files Browse the repository at this point in the history
  • Loading branch information
w1am committed Oct 23, 2024
1 parent 09fcaa6 commit 8adcd23
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 173 deletions.
19 changes: 4 additions & 15 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Generate certificates
run: |
mkdir -p certs
Expand All @@ -89,7 +89,7 @@ jobs:
sudo chown -R $USER:$USER certs
sudo chmod -R 755 certs
- name: Upload certificates
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: certs
path: certs
Expand Down Expand Up @@ -123,21 +123,10 @@ jobs:
run: |
dotnet build --configuration ${{ matrix.configuration }} --framework ${{ matrix.framework }} src/EventStore.Client
- name: Download certificates
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: certs
path: certs
- name: Import certificates (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
sudo cp certs/ca/ca.crt /usr/local/share/ca-certificates/eventstore_ca.crt
sudo update-ca-certificates
- name: Import certificates (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
Import-Certificate -FilePath "certs\ca\ca.crt" -CertStoreLocation "Cert:\LocalMachine\Root"
- name: Run Tests (Linux)
if: runner.os == 'Linux'
shell: bash
Expand Down Expand Up @@ -191,7 +180,7 @@ jobs:
/p:RepositoryUrl=https://github.com/EventStore/EventStore-Client-Dotnet \
/p:RepositoryType=git
- name: Publish Artifacts
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
path: packages
name: nuget-packages
Expand Down
2 changes: 0 additions & 2 deletions gencert.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,3 @@ docker run --rm --volume .\certs:/tmp docker.eventstore.com/eventstore-utils/es-

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

Import-Certificate -FilePath ".\certs\ca\ca.crt" -CertStoreLocation Cert:\CurrentUser\Root
65 changes: 37 additions & 28 deletions src/EventStore.Client/ChannelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using TChannel = Grpc.Net.Client.GrpcChannel;

namespace EventStore.Client {

internal static class ChannelFactory {
private const int MaxReceiveMessageLength = 17 * 1024 * 1024;

Expand Down Expand Up @@ -38,14 +38,8 @@ public static TChannel CreateChannel(EventStoreClientSettings settings, EndPoint

#if NET48
static HttpMessageHandler CreateHandler(EventStoreClientSettings settings) {
if (settings.CreateHttpMessageHandler != null) {
if (settings.CreateHttpMessageHandler is not null)
return settings.CreateHttpMessageHandler.Invoke();
}

var certificate = settings.ConnectivitySettings.ClientCertificate ??
settings.ConnectivitySettings.TlsCaFile;

var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;

var handler = new WinHttpHandler {
TcpKeepAliveEnabled = true,
Expand All @@ -56,42 +50,57 @@ static HttpMessageHandler CreateHandler(EventStoreClientSettings settings) {

if (settings.ConnectivitySettings.Insecure) return handler;

if (configureClientCert) {
handler.ClientCertificates.Add(certificate!);
}
if (settings.ConnectivitySettings.ClientCertificate is not null)
handler.ClientCertificates.Add(settings.ConnectivitySettings.ClientCertificate);

if (!settings.ConnectivitySettings.TlsVerifyCert) {
handler.ServerCertificateValidationCallback = delegate { return true; };
}
handler.ServerCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
false => delegate { return true; },
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
if (chain is null) return false;

chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
return chain.Build(certificate);
},
_ => null
};

return handler;
}
#else
static HttpMessageHandler CreateHandler(EventStoreClientSettings settings) {
if (settings.CreateHttpMessageHandler != null) {
if (settings.CreateHttpMessageHandler is not null)
return settings.CreateHttpMessageHandler.Invoke();
}

var certificate = settings.ConnectivitySettings.ClientCertificate ??
settings.ConnectivitySettings.TlsCaFile;

var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;

var handler = new SocketsHttpHandler {
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
KeepAlivePingTimeout = settings.ConnectivitySettings.KeepAliveTimeout,
EnableMultipleHttp2Connections = true,
EnableMultipleHttp2Connections = true
};

if (settings.ConnectivitySettings.Insecure) return handler;
if (settings.ConnectivitySettings.Insecure)
return handler;

if (configureClientCert) {
handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate! };
if (settings.ConnectivitySettings.ClientCertificate is not null) {
handler.SslOptions.ClientCertificates = new X509CertificateCollection {
settings.ConnectivitySettings.ClientCertificate
};
}

if (!settings.ConnectivitySettings.TlsVerifyCert) {
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
}
handler.SslOptions.RemoteCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
false => delegate { return true; },
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
if (certificate is not X509Certificate2 peerCertificate || chain is null) return false;

chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.DisableCertificateDownloads = true;
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
return chain.Build(peerCertificate);
},
_ => null
};

return handler;
}
Expand Down
2 changes: 1 addition & 1 deletion src/EventStore.Client/EventStore.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1"/>
<PackageReference Include="System.Text.Json" Version="8.0.4"/>
<PackageReference Include="System.Text.Json" Version="8.0.5"/>
</ItemGroup>

<ItemGroup>
Expand Down
58 changes: 36 additions & 22 deletions src/EventStore.Client/EventStoreClientSettings.ConnectionString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,8 @@ private static EventStoreClientSettings CreateSettings(

#if NET48
HttpMessageHandler CreateDefaultHandler() {
var certificate = settings.ConnectivitySettings.ClientCertificate ??
settings.ConnectivitySettings.TlsCaFile;

var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;
if (settings.CreateHttpMessageHandler is not null)
return settings.CreateHttpMessageHandler.Invoke();

var handler = new WinHttpHandler {
TcpKeepAliveEnabled = true,
Expand All @@ -249,38 +247,54 @@ HttpMessageHandler CreateDefaultHandler() {

if (settings.ConnectivitySettings.Insecure) return handler;

if (configureClientCert) {
handler.ClientCertificates.Add(certificate!);
}
if (settings.ConnectivitySettings.ClientCertificate is not null)
handler.ClientCertificates.Add(settings.ConnectivitySettings.ClientCertificate);

if (!settings.ConnectivitySettings.TlsVerifyCert) {
handler.ServerCertificateValidationCallback = delegate { return true; };
}
handler.ServerCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
false => delegate { return true; },
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
if (chain is null) return false;

chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
return chain.Build(certificate);
},
_ => null
};

return handler;
}
#else
HttpMessageHandler CreateDefaultHandler() {
var certificate = settings.ConnectivitySettings.ClientCertificate ??
settings.ConnectivitySettings.TlsCaFile;

var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;

var handler = new SocketsHttpHandler {
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
KeepAlivePingTimeout = settings.ConnectivitySettings.KeepAliveTimeout,
EnableMultipleHttp2Connections = true,
EnableMultipleHttp2Connections = true
};

if (settings.ConnectivitySettings.Insecure) return handler;
if (settings.ConnectivitySettings.Insecure)
return handler;

if (configureClientCert) {
handler.SslOptions.ClientCertificates = [certificate!];
if (settings.ConnectivitySettings.ClientCertificate is not null) {
handler.SslOptions.ClientCertificates = new X509CertificateCollection {
settings.ConnectivitySettings.ClientCertificate
};
}

if (!settings.ConnectivitySettings.TlsVerifyCert) {
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
}
handler.SslOptions.RemoteCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
false => delegate { return true; },
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
if (certificate is not X509Certificate2 peerCertificate || chain is null) return false;

chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.DisableCertificateDownloads = true;
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
return chain.Build(peerCertificate);
},
_ => null
};

return handler;
}
Expand Down
47 changes: 31 additions & 16 deletions src/EventStore.Client/HttpFallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,29 @@ internal HttpFallback(EventStoreClientSettings settings) {
if (!settings.ConnectivitySettings.Insecure) {
handler.ClientCertificateOptions = ClientCertificateOption.Manual;

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

if (settings.ConnectivitySettings.ClientCertificate != null)
if (settings.ConnectivitySettings.ClientCertificate is not null)
handler.ClientCertificates.Add(settings.ConnectivitySettings.ClientCertificate);

if (!settings.ConnectivitySettings.TlsVerifyCert)
handler.ServerCertificateCustomValidationCallback = delegate { return true; };
handler.ServerCertificateCustomValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
false => delegate { return true; },
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
if (certificate is null || chain is null) return false;

chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;

#if NET48
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
#else
chain.ChainPolicy.DisableCertificateDownloads = true;
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
#endif

return chain.Build(certificate);
},
_ => null
};
}

_httpClient = new HttpClient(handler);
Expand All @@ -45,9 +60,9 @@ internal async Task<T> HttpGetAsync<T>(string path, ChannelInfo channelInfo, Tim
UserCredentials? userCredentials, Action onNotFound, CancellationToken cancellationToken) {

var request = CreateRequest(path, HttpMethod.Get, channelInfo, userCredentials);

var httpResult = await HttpSendAsync(request, onNotFound, deadline, cancellationToken).ConfigureAwait(false);

#if NET
var json = await httpResult.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
#else
Expand All @@ -66,26 +81,26 @@ internal async Task HttpPostAsync(string path, string query, ChannelInfo channel
UserCredentials? userCredentials, Action onNotFound, CancellationToken cancellationToken) {

var request = CreateRequest(path, query, HttpMethod.Post, channelInfo, userCredentials);

await HttpSendAsync(request, onNotFound, deadline, cancellationToken).ConfigureAwait(false);
}

private async Task<HttpResponseMessage> HttpSendAsync(HttpRequestMessage request, Action onNotFound,
TimeSpan? deadline, CancellationToken cancellationToken) {

if (!deadline.HasValue) {
return await HttpSendAsync(request, onNotFound, cancellationToken).ConfigureAwait(false);
return await HttpSendAsync(request, onNotFound, cancellationToken).ConfigureAwait(false);
}

using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
cts.CancelAfter(deadline.Value);

return await HttpSendAsync(request, onNotFound, cts.Token).ConfigureAwait(false);
}

async Task<HttpResponseMessage> HttpSendAsync(HttpRequestMessage request, Action onNotFound,
CancellationToken cancellationToken) {

var httpResult = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (httpResult.IsSuccessStatusCode) {
return httpResult;
Expand All @@ -107,7 +122,7 @@ private HttpRequestMessage CreateRequest(string path, HttpMethod method, Channel

private HttpRequestMessage CreateRequest(string path, string query, HttpMethod method, ChannelInfo channelInfo,
UserCredentials? credentials) {

var uriBuilder = new UriBuilder($"{_addressScheme}://{channelInfo.Channel.Target}") {
Path = path,
Query = query
Expand All @@ -119,7 +134,7 @@ private HttpRequestMessage CreateRequest(string path, string query, HttpMethod m
if (credentials != null) {
httpRequest.Headers.Add(Constants.Headers.Authorization, credentials.ToString());
}

return httpRequest;
}

Expand Down
Loading

0 comments on commit 8adcd23

Please sign in to comment.