Skip to content

Commit e34a00a

Browse files
authored
Add observer for secure connection info (#677)
* Add observer for secure connection info * Don't use init-only properties * Include thumbrint in connection info
1 parent 055733e commit e34a00a

10 files changed

+161
-8
lines changed

source/Halibut.Tests/Transport/SecureClientFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public async Task SecureClientClearsPoolWhenAllConnectionsCorrupt()
8080
Params = new object[] { "Fred" }
8181
};
8282

83-
var tcpConnectionFactory = new TcpConnectionFactory(Certificates.Octopus, halibutTimeoutsAndLimits, new StreamFactory());
83+
var tcpConnectionFactory = new TcpConnectionFactory(Certificates.Octopus, halibutTimeoutsAndLimits, new StreamFactory(), NoOpSecureConnectionObserver.Instance);
8484
var secureClient = new SecureListeningClient(GetProtocol, endpoint, Certificates.Octopus, log, connectionManager, tcpConnectionFactory);
8585
ResponseMessage response = null!;
8686

source/Halibut.Tests/Transport/SecureListenerFixture.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public async Task SecureListenerDoesNotCreateHundredsOfIoEventsPerSecondOnWindow
7373
(_, _) => UnauthorizedClientConnectResponse.BlockConnection,
7474
timeoutsAndLimits,
7575
new StreamFactory(),
76-
NoOpConnectionsObserver.Instance
76+
NoOpConnectionsObserver.Instance,
77+
NoOpSecureConnectionObserver.Instance
7778
);
7879

7980
var idleAverage = CollectCounterValues(opsPerSec)

source/Halibut/HalibutRuntime.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class HalibutRuntime : IHalibutRuntime
4343
readonly IRpcObserver rpcObserver;
4444
readonly TcpConnectionFactory tcpConnectionFactory;
4545
readonly IConnectionsObserver connectionsObserver;
46+
readonly ISecureConnectionObserver secureConnectionObserver;
4647
readonly IActiveTcpConnectionsLimiter activeTcpConnectionsLimiter;
4748
readonly IControlMessageObserver controlMessageObserver;
4849

@@ -59,7 +60,9 @@ internal HalibutRuntime(
5960
IStreamFactory streamFactory,
6061
IRpcObserver rpcObserver,
6162
IConnectionsObserver connectionsObserver,
62-
IControlMessageObserver controlMessageObserver)
63+
IControlMessageObserver controlMessageObserver,
64+
ISecureConnectionObserver secureConnectionObserver
65+
)
6366
{
6467
this.serverCertificate = serverCertificate;
6568
this.trustProvider = trustProvider;
@@ -73,10 +76,11 @@ internal HalibutRuntime(
7376
invoker = new ServiceInvoker(serviceFactory);
7477
TimeoutsAndLimits = halibutTimeoutsAndLimits;
7578
this.connectionsObserver = connectionsObserver;
79+
this.secureConnectionObserver = secureConnectionObserver;
7680
this.controlMessageObserver = controlMessageObserver;
7781

7882
connectionManager = new ConnectionManagerAsync();
79-
this.tcpConnectionFactory = new TcpConnectionFactory(serverCertificate, TimeoutsAndLimits, streamFactory);
83+
this.tcpConnectionFactory = new TcpConnectionFactory(serverCertificate, TimeoutsAndLimits, streamFactory, secureConnectionObserver);
8084
activeTcpConnectionsLimiter = new ActiveTcpConnectionsLimiter(TimeoutsAndLimits);
8185
}
8286

@@ -130,7 +134,9 @@ public int Listen(IPEndPoint endpoint)
130134
HandleUnauthorizedClientConnect,
131135
TimeoutsAndLimits,
132136
streamFactory,
133-
connectionsObserver);
137+
connectionsObserver,
138+
secureConnectionObserver
139+
);
134140

135141
listeners.DoWithExclusiveAccess(l =>
136142
{

source/Halibut/HalibutRuntimeBuilder.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class HalibutRuntimeBuilder
2828
IStreamFactory? streamFactory;
2929
IRpcObserver? rpcObserver;
3030
IConnectionsObserver? connectionsObserver;
31+
ISecureConnectionObserver? secureConnectionObserver;
3132
IControlMessageObserver? controlMessageObserver;
3233
MessageStreamWrappers queueMessageStreamWrappers = new();
3334

@@ -43,6 +44,12 @@ public HalibutRuntimeBuilder WithConnectionsObserver(IConnectionsObserver connec
4344
return this;
4445
}
4546

47+
public HalibutRuntimeBuilder WithSecureConnectionObserver(ISecureConnectionObserver secureConnectionsObserver)
48+
{
49+
this.secureConnectionObserver = secureConnectionsObserver;
50+
return this;
51+
}
52+
4653
internal HalibutRuntimeBuilder WithStreamFactory(IStreamFactory streamFactory)
4754
{
4855
this.streamFactory = streamFactory;
@@ -175,6 +182,7 @@ public HalibutRuntime Build()
175182

176183
var streamFactory = this.streamFactory ?? new StreamFactory();
177184
var connectionsObserver = this.connectionsObserver ?? NoOpConnectionsObserver.Instance;
185+
var secureConnectionObserver = this.secureConnectionObserver ?? NoOpSecureConnectionObserver.Instance;
178186
var rpcObserver = this.rpcObserver ?? new NoRpcObserver();
179187
var controlMessageObserver = this.controlMessageObserver ?? new NoOpControlMessageObserver();
180188

@@ -191,7 +199,9 @@ public HalibutRuntime Build()
191199
streamFactory,
192200
rpcObserver,
193201
connectionsObserver,
194-
controlMessageObserver);
202+
controlMessageObserver,
203+
secureConnectionObserver
204+
);
195205

196206
if (onUnauthorizedClientConnect is not null)
197207
{
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2012-2013 Octopus Deploy Pty. Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace Halibut.Transport.Observability
16+
{
17+
public enum ConnectionDirection
18+
{
19+
Incoming,
20+
Outgoing
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2012-2013 Octopus Deploy Pty. Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Security.Authentication;
16+
17+
namespace Halibut.Transport.Observability
18+
{
19+
public interface ISecureConnectionObserver
20+
{
21+
public void SecureConnectionEstablished(SecureConnectionInfo secureConnectionInfo);
22+
}
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2012-2013 Octopus Deploy Pty. Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace Halibut.Transport.Observability
16+
{
17+
public class NoOpSecureConnectionObserver : ISecureConnectionObserver
18+
{
19+
static NoOpSecureConnectionObserver? singleInstance;
20+
public static NoOpSecureConnectionObserver Instance => singleInstance ??= new NoOpSecureConnectionObserver();
21+
public void SecureConnectionEstablished(SecureConnectionInfo secureConnectionInfo)
22+
{
23+
}
24+
}
25+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2012-2013 Octopus Deploy Pty. Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Security.Authentication;
16+
17+
namespace Halibut.Transport.Observability
18+
{
19+
public struct SecureConnectionInfo
20+
{
21+
SecureConnectionInfo(
22+
SslProtocols sslProtocols,
23+
ConnectionDirection connectionDirection,
24+
string thumbprint
25+
)
26+
{
27+
SslProtocols = sslProtocols;
28+
ConnectionDirection = connectionDirection;
29+
Thumbprint = thumbprint;
30+
}
31+
32+
public SslProtocols SslProtocols { get; }
33+
public ConnectionDirection ConnectionDirection { get; }
34+
public string Thumbprint { get; }
35+
36+
public static SecureConnectionInfo CreateIncoming(
37+
SslProtocols sslProtocols,
38+
string thumbprint
39+
)
40+
{
41+
return new SecureConnectionInfo(sslProtocols, ConnectionDirection.Incoming, thumbprint);
42+
}
43+
44+
public static SecureConnectionInfo CreateOutgoing(
45+
SslProtocols sslProtocols,
46+
string thumbprint
47+
)
48+
{
49+
return new(sslProtocols, ConnectionDirection.Outgoing, thumbprint);
50+
}
51+
}
52+
}

source/Halibut/Transport/SecureListener.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class SecureListener : IAsyncDisposable
4848
readonly HalibutTimeoutsAndLimits halibutTimeoutsAndLimits;
4949
readonly IStreamFactory streamFactory;
5050
readonly IConnectionsObserver connectionsObserver;
51+
readonly ISecureConnectionObserver secureConnectionObserver;
5152
ILog log;
5253
TcpListener listener;
5354
Thread? backgroundThread;
@@ -67,7 +68,9 @@ public SecureListener(
6768
Func<string, string, UnauthorizedClientConnectResponse> unauthorizedClientConnect,
6869
HalibutTimeoutsAndLimits halibutTimeoutsAndLimits,
6970
IStreamFactory streamFactory,
70-
IConnectionsObserver connectionsObserver)
71+
IConnectionsObserver connectionsObserver,
72+
ISecureConnectionObserver secureConnectionObserver
73+
)
7174
{
7275
this.endPoint = endPoint;
7376
this.serverCertificate = serverCertificate;
@@ -81,6 +84,7 @@ public SecureListener(
8184
this.halibutTimeoutsAndLimits = halibutTimeoutsAndLimits;
8285
this.streamFactory = streamFactory;
8386
this.connectionsObserver = connectionsObserver;
87+
this.secureConnectionObserver = secureConnectionObserver;
8488
this.cts = new CancellationTokenSource();
8589
this.cancellationToken = cts.Token;
8690

@@ -336,6 +340,7 @@ await ssl
336340
{
337341
connectionAuthorizedAndObserved = true;
338342
connectionsObserver.ConnectionAccepted(true);
343+
secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateIncoming(ssl.SslProtocol, thumbprint));
339344
tcpClientManager.AddActiveClient(thumbprint, client);
340345
errorEventType = EventType.Error;
341346
await ExchangeMessages(ssl).ConfigureAwait(false);

source/Halibut/Transport/TcpConnectionFactory.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Threading;
88
using System.Threading.Tasks;
99
using Halibut.Diagnostics;
10+
using Halibut.Transport.Observability;
1011
using Halibut.Transport.Protocol;
1112
using Halibut.Transport.Proxy;
1213
using Halibut.Transport.Streams;
@@ -20,12 +21,19 @@ public class TcpConnectionFactory : IConnectionFactory
2021
readonly X509Certificate2 clientCertificate;
2122
readonly HalibutTimeoutsAndLimits halibutTimeoutsAndLimits;
2223
readonly IStreamFactory streamFactory;
24+
readonly ISecureConnectionObserver secureConnectionObserver;
2325

24-
public TcpConnectionFactory(X509Certificate2 clientCertificate, HalibutTimeoutsAndLimits halibutTimeoutsAndLimits, IStreamFactory streamFactory)
26+
public TcpConnectionFactory(
27+
X509Certificate2 clientCertificate,
28+
HalibutTimeoutsAndLimits halibutTimeoutsAndLimits,
29+
IStreamFactory streamFactory,
30+
ISecureConnectionObserver secureConnectionObserver
31+
)
2532
{
2633
this.clientCertificate = clientCertificate;
2734
this.halibutTimeoutsAndLimits = halibutTimeoutsAndLimits;
2835
this.streamFactory = streamFactory;
36+
this.secureConnectionObserver = secureConnectionObserver;
2937
}
3038

3139
public async Task<IConnection> EstablishNewConnectionAsync(ExchangeProtocolBuilder exchangeProtocolBuilder, ServiceEndPoint serviceEndpoint, ILog log, CancellationToken cancellationToken)
@@ -60,6 +68,7 @@ await ssl.AuthenticateAsClientAsync(
6068
await ssl.FlushAsync(cancellationToken);
6169

6270
log.Write(EventType.Security, "Secure connection established. Server at {0} identified by thumbprint: {1}, using protocol {2}", client.Client.RemoteEndPoint, serviceEndpoint.RemoteThumbprint, ssl.SslProtocol.ToString());
71+
secureConnectionObserver.SecureConnectionEstablished(SecureConnectionInfo.CreateOutgoing(ssl.SslProtocol, serviceEndpoint.RemoteThumbprint ?? "Unknown"));
6372

6473
return new SecureConnection(client, ssl, exchangeProtocolBuilder, halibutTimeoutsAndLimits, log);
6574
}

0 commit comments

Comments
 (0)