Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix channel factory to use root ca if provided #325

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
61 changes: 33 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,53 @@ 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.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.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
54 changes: 32 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,50 @@ 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.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.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
return chain.Build(peerCertificate);
},
_ => null
};

return handler;
}
Expand Down
45 changes: 29 additions & 16 deletions src/EventStore.Client/HttpFallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,27 @@ 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.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
#else
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 +58,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 +79,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 +120,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 +132,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
Loading