Skip to content

Commit e37e8ec

Browse files
authored
Fix channel factory to use root ca if provided (#325)
1 parent 09fcaa6 commit e37e8ec

File tree

10 files changed

+223
-173
lines changed

10 files changed

+223
-173
lines changed

.github/workflows/publish.yml

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
runs-on: ubuntu-latest
7777
steps:
7878
- name: Checkout code
79-
uses: actions/checkout@v2
79+
uses: actions/checkout@v4
8080
- name: Generate certificates
8181
run: |
8282
mkdir -p certs
@@ -89,7 +89,7 @@ jobs:
8989
sudo chown -R $USER:$USER certs
9090
sudo chmod -R 755 certs
9191
- name: Upload certificates
92-
uses: actions/upload-artifact@v2
92+
uses: actions/upload-artifact@v4
9393
with:
9494
name: certs
9595
path: certs
@@ -123,21 +123,10 @@ jobs:
123123
run: |
124124
dotnet build --configuration ${{ matrix.configuration }} --framework ${{ matrix.framework }} src/EventStore.Client
125125
- name: Download certificates
126-
uses: actions/download-artifact@v2
126+
uses: actions/download-artifact@v4
127127
with:
128128
name: certs
129129
path: certs
130-
- name: Import certificates (Linux)
131-
if: runner.os == 'Linux'
132-
shell: bash
133-
run: |
134-
sudo cp certs/ca/ca.crt /usr/local/share/ca-certificates/eventstore_ca.crt
135-
sudo update-ca-certificates
136-
- name: Import certificates (Windows)
137-
if: runner.os == 'Windows'
138-
shell: pwsh
139-
run: |
140-
Import-Certificate -FilePath "certs\ca\ca.crt" -CertStoreLocation "Cert:\LocalMachine\Root"
141130
- name: Run Tests (Linux)
142131
if: runner.os == 'Linux'
143132
shell: bash
@@ -191,7 +180,7 @@ jobs:
191180
/p:RepositoryUrl=https://github.com/EventStore/EventStore-Client-Dotnet \
192181
/p:RepositoryType=git
193182
- name: Publish Artifacts
194-
uses: actions/upload-artifact@v1
183+
uses: actions/upload-artifact@v4
195184
with:
196185
path: packages
197186
name: nuget-packages

gencert.ps1

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,3 @@ docker run --rm --volume .\certs:/tmp docker.eventstore.com/eventstore-utils/es-
2121

2222
# Set permissions recursively for the directory
2323
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)F"
24-
25-
Import-Certificate -FilePath ".\certs\ca\ca.crt" -CertStoreLocation Cert:\CurrentUser\Root

src/EventStore.Client/ChannelFactory.cs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using TChannel = Grpc.Net.Client.GrpcChannel;
66

77
namespace EventStore.Client {
8-
8+
99
internal static class ChannelFactory {
1010
private const int MaxReceiveMessageLength = 17 * 1024 * 1024;
1111

@@ -38,14 +38,8 @@ public static TChannel CreateChannel(EventStoreClientSettings settings, EndPoint
3838

3939
#if NET48
4040
static HttpMessageHandler CreateHandler(EventStoreClientSettings settings) {
41-
if (settings.CreateHttpMessageHandler != null) {
41+
if (settings.CreateHttpMessageHandler is not null)
4242
return settings.CreateHttpMessageHandler.Invoke();
43-
}
44-
45-
var certificate = settings.ConnectivitySettings.ClientCertificate ??
46-
settings.ConnectivitySettings.TlsCaFile;
47-
48-
var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;
4943

5044
var handler = new WinHttpHandler {
5145
TcpKeepAliveEnabled = true,
@@ -56,42 +50,53 @@ static HttpMessageHandler CreateHandler(EventStoreClientSettings settings) {
5650

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

59-
if (configureClientCert) {
60-
handler.ClientCertificates.Add(certificate!);
61-
}
53+
if (settings.ConnectivitySettings.ClientCertificate is not null)
54+
handler.ClientCertificates.Add(settings.ConnectivitySettings.ClientCertificate);
6255

63-
if (!settings.ConnectivitySettings.TlsVerifyCert) {
64-
handler.ServerCertificateValidationCallback = delegate { return true; };
65-
}
56+
handler.ServerCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
57+
false => delegate { return true; },
58+
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
59+
if (chain is null) return false;
60+
61+
chain.ChainPolicy.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
62+
return chain.Build(certificate);
63+
},
64+
_ => null
65+
};
6666

6767
return handler;
6868
}
6969
#else
7070
static HttpMessageHandler CreateHandler(EventStoreClientSettings settings) {
71-
if (settings.CreateHttpMessageHandler != null) {
71+
if (settings.CreateHttpMessageHandler is not null)
7272
return settings.CreateHttpMessageHandler.Invoke();
73-
}
74-
75-
var certificate = settings.ConnectivitySettings.ClientCertificate ??
76-
settings.ConnectivitySettings.TlsCaFile;
77-
78-
var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;
7973

8074
var handler = new SocketsHttpHandler {
8175
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
8276
KeepAlivePingTimeout = settings.ConnectivitySettings.KeepAliveTimeout,
83-
EnableMultipleHttp2Connections = true,
77+
EnableMultipleHttp2Connections = true
8478
};
8579

86-
if (settings.ConnectivitySettings.Insecure) return handler;
80+
if (settings.ConnectivitySettings.Insecure)
81+
return handler;
8782

88-
if (configureClientCert) {
89-
handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate! };
83+
if (settings.ConnectivitySettings.ClientCertificate is not null) {
84+
handler.SslOptions.ClientCertificates = new X509CertificateCollection {
85+
settings.ConnectivitySettings.ClientCertificate
86+
};
9087
}
9188

92-
if (!settings.ConnectivitySettings.TlsVerifyCert) {
93-
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
94-
}
89+
handler.SslOptions.RemoteCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
90+
false => delegate { return true; },
91+
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
92+
if (certificate is not X509Certificate2 peerCertificate || chain is null) return false;
93+
94+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
95+
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
96+
return chain.Build(peerCertificate);
97+
},
98+
_ => null
99+
};
95100

96101
return handler;
97102
}

src/EventStore.Client/EventStore.Client.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
1515
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
1616
<PackageReference Include="System.Linq.Async" Version="6.0.1"/>
17-
<PackageReference Include="System.Text.Json" Version="8.0.4"/>
17+
<PackageReference Include="System.Text.Json" Version="8.0.5"/>
1818
</ItemGroup>
1919

2020
<ItemGroup>

src/EventStore.Client/EventStoreClientSettings.ConnectionString.cs

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,8 @@ private static EventStoreClientSettings CreateSettings(
235235

236236
#if NET48
237237
HttpMessageHandler CreateDefaultHandler() {
238-
var certificate = settings.ConnectivitySettings.ClientCertificate ??
239-
settings.ConnectivitySettings.TlsCaFile;
240-
241-
var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;
238+
if (settings.CreateHttpMessageHandler is not null)
239+
return settings.CreateHttpMessageHandler.Invoke();
242240

243241
var handler = new WinHttpHandler {
244242
TcpKeepAliveEnabled = true,
@@ -249,38 +247,50 @@ HttpMessageHandler CreateDefaultHandler() {
249247

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

252-
if (configureClientCert) {
253-
handler.ClientCertificates.Add(certificate!);
254-
}
250+
if (settings.ConnectivitySettings.ClientCertificate is not null)
251+
handler.ClientCertificates.Add(settings.ConnectivitySettings.ClientCertificate);
255252

256-
if (!settings.ConnectivitySettings.TlsVerifyCert) {
257-
handler.ServerCertificateValidationCallback = delegate { return true; };
258-
}
253+
handler.ServerCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
254+
false => delegate { return true; },
255+
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
256+
if (chain is null) return false;
257+
258+
chain.ChainPolicy.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
259+
return chain.Build(certificate);
260+
},
261+
_ => null
262+
};
259263

260264
return handler;
261265
}
262266
#else
263267
HttpMessageHandler CreateDefaultHandler() {
264-
var certificate = settings.ConnectivitySettings.ClientCertificate ??
265-
settings.ConnectivitySettings.TlsCaFile;
266-
267-
var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;
268-
269268
var handler = new SocketsHttpHandler {
270269
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
271270
KeepAlivePingTimeout = settings.ConnectivitySettings.KeepAliveTimeout,
272-
EnableMultipleHttp2Connections = true,
271+
EnableMultipleHttp2Connections = true
273272
};
274273

275-
if (settings.ConnectivitySettings.Insecure) return handler;
274+
if (settings.ConnectivitySettings.Insecure)
275+
return handler;
276276

277-
if (configureClientCert) {
278-
handler.SslOptions.ClientCertificates = [certificate!];
277+
if (settings.ConnectivitySettings.ClientCertificate is not null) {
278+
handler.SslOptions.ClientCertificates = new X509CertificateCollection {
279+
settings.ConnectivitySettings.ClientCertificate
280+
};
279281
}
280282

281-
if (!settings.ConnectivitySettings.TlsVerifyCert) {
282-
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
283-
}
283+
handler.SslOptions.RemoteCertificateValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
284+
false => delegate { return true; },
285+
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
286+
if (certificate is not X509Certificate2 peerCertificate || chain is null) return false;
287+
288+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
289+
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
290+
return chain.Build(peerCertificate);
291+
},
292+
_ => null
293+
};
284294

285295
return handler;
286296
}

src/EventStore.Client/HttpFallback.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,27 @@ internal HttpFallback(EventStoreClientSettings settings) {
2121
if (!settings.ConnectivitySettings.Insecure) {
2222
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
2323

24-
if (settings.ConnectivitySettings.TlsCaFile != null)
25-
handler.ClientCertificates.Add(settings.ConnectivitySettings.TlsCaFile);
26-
27-
if (settings.ConnectivitySettings.ClientCertificate != null)
24+
if (settings.ConnectivitySettings.ClientCertificate is not null)
2825
handler.ClientCertificates.Add(settings.ConnectivitySettings.ClientCertificate);
2926

30-
if (!settings.ConnectivitySettings.TlsVerifyCert)
31-
handler.ServerCertificateCustomValidationCallback = delegate { return true; };
27+
handler.ServerCertificateCustomValidationCallback = settings.ConnectivitySettings.TlsVerifyCert switch {
28+
false => delegate { return true; },
29+
true when settings.ConnectivitySettings.TlsCaFile is not null => (sender, certificate, chain, errors) => {
30+
if (certificate is null || chain is null) return false;
31+
32+
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
33+
34+
#if NET48
35+
chain.ChainPolicy.ExtraStore.Add(settings.ConnectivitySettings.TlsCaFile);
36+
#else
37+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
38+
chain.ChainPolicy.CustomTrustStore.Add(settings.ConnectivitySettings.TlsCaFile);
39+
#endif
40+
41+
return chain.Build(certificate);
42+
},
43+
_ => null
44+
};
3245
}
3346

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

4760
var request = CreateRequest(path, HttpMethod.Get, channelInfo, userCredentials);
48-
61+
4962
var httpResult = await HttpSendAsync(request, onNotFound, deadline, cancellationToken).ConfigureAwait(false);
50-
63+
5164
#if NET
5265
var json = await httpResult.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
5366
#else
@@ -66,26 +79,26 @@ internal async Task HttpPostAsync(string path, string query, ChannelInfo channel
6679
UserCredentials? userCredentials, Action onNotFound, CancellationToken cancellationToken) {
6780

6881
var request = CreateRequest(path, query, HttpMethod.Post, channelInfo, userCredentials);
69-
82+
7083
await HttpSendAsync(request, onNotFound, deadline, cancellationToken).ConfigureAwait(false);
7184
}
7285

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

7689
if (!deadline.HasValue) {
77-
return await HttpSendAsync(request, onNotFound, cancellationToken).ConfigureAwait(false);
90+
return await HttpSendAsync(request, onNotFound, cancellationToken).ConfigureAwait(false);
7891
}
79-
92+
8093
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
8194
cts.CancelAfter(deadline.Value);
82-
95+
8396
return await HttpSendAsync(request, onNotFound, cts.Token).ConfigureAwait(false);
8497
}
85-
98+
8699
async Task<HttpResponseMessage> HttpSendAsync(HttpRequestMessage request, Action onNotFound,
87100
CancellationToken cancellationToken) {
88-
101+
89102
var httpResult = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
90103
if (httpResult.IsSuccessStatusCode) {
91104
return httpResult;
@@ -107,7 +120,7 @@ private HttpRequestMessage CreateRequest(string path, HttpMethod method, Channel
107120

108121
private HttpRequestMessage CreateRequest(string path, string query, HttpMethod method, ChannelInfo channelInfo,
109122
UserCredentials? credentials) {
110-
123+
111124
var uriBuilder = new UriBuilder($"{_addressScheme}://{channelInfo.Channel.Target}") {
112125
Path = path,
113126
Query = query
@@ -119,7 +132,7 @@ private HttpRequestMessage CreateRequest(string path, string query, HttpMethod m
119132
if (credentials != null) {
120133
httpRequest.Headers.Add(Constants.Headers.Authorization, credentials.ToString());
121134
}
122-
135+
123136
return httpRequest;
124137
}
125138

0 commit comments

Comments
 (0)