public EpoxyTransport( ILayerStackProvider layerStackProvider, Func <string, Task <IPAddress> > resolver, EpoxyServerTlsConfig serverTlsConfig, EpoxyClientTlsConfig clientTlsConfig, TimeoutConfig timeoutConfig, ILogSink logSink, bool enableDebugLogs, IMetricsSink metricsSink) { // Layer stack provider may be null this.layerStackProvider = layerStackProvider; // Always need a resolver, so substitute in default if given null this.resolver = resolver ?? ResolveViaDnsAsync; // may be null - indicates no TLS for listeners this.serverTlsConfig = serverTlsConfig; // Client-side TLS is determined by how the connection is // established, so we substitute in the Default TLS config if we // happened to get null this.clientTlsConfig = clientTlsConfig ?? EpoxyClientTlsConfig.Default; this.timeoutConfig = timeoutConfig; // Log sink may be null logger = new Logger(logSink, enableDebugLogs); // Metrics sink may be null metrics = new Metrics(metricsSink); connections = new CleanupCollection <EpoxyConnection>(); listeners = new CleanupCollection <EpoxyListener>(); }
public async Task <EpoxyConnection> ConnectToAsync(Endpoint endpoint, CancellationToken ct) { logger.Site().Information("Connecting to {0}.", endpoint); EpoxyClientTlsConfig tlsConfig = endpoint.UseTls ? clientTlsConfig : null; try { EpoxyNetworkStream epoxyStream = await EpoxyNetworkStream.MakeAsync( socketFunc : () => ConnectClientSocketAsync(endpoint), streamFunc : socket => EpoxyNetworkStream.MakeClientStreamAsync(endpoint.Host, socket, tlsConfig, logger), timeoutConfig : timeoutConfig, logger : logger); // TODO: keep these in some master collection for shutdown var connection = EpoxyConnection.MakeClientConnection(this, epoxyStream, logger, metrics); await connection.StartAsync(); return(connection); } catch (Exception ex) { logger.Site().Error(ex, "Failed to start Epoxy client connection to '{0}'", endpoint); throw; } }
public async Task <EpoxyConnection> ConnectToAsync(Endpoint endpoint, CancellationToken ct) { logger.Site().Information("Connecting to {0}.", endpoint); Socket socket = await ConnectClientSocketAsync(endpoint); // For MakeClientStreamAsync, null TLS config means insecure EpoxyClientTlsConfig tlsConfig = endpoint.UseTls ? clientTlsConfig : null; var epoxyStream = await EpoxyNetworkStream.MakeClientStreamAsync(endpoint.Host, socket, tlsConfig, logger); // TODO: keep these in some master collection for shutdown var connection = EpoxyConnection.MakeClientConnection(this, epoxyStream, logger, metrics); await connection.StartAsync(); return(connection); }
/// <summary> /// Creates a connected client-side EpoxyNetworkStream from a connected TCP socket. /// </summary> /// <remarks> /// If TLS is enabled, this will establish the TLS connection. Upon successful return, the /// stream owns the socket. /// </remarks> /// <param name="remoteHostname"> /// The expected name of the remote host. Used during certificate validation. /// </param> /// <param name="socket">The socket to wrap.</param> /// <param name="tlsConfig"> /// TLS config. May be <c>null</c> to indicate a plain-text connection. /// </param> /// <param name="logger">A logger.</param> /// <returns>An connected EpoxyNetworkStream.</returns> public static async Task <EpoxyNetworkStream> MakeClientStreamAsync( string remoteHostname, Socket socket, EpoxyClientTlsConfig tlsConfig, Logger logger) { Stream clientStream; NetworkStream networkStream = new NetworkStream(socket, ownsSocket: false); if (tlsConfig == null) { clientStream = networkStream; } else { const bool leaveInnerStreamOpen = false; var sslStream = new SslStream( networkStream, leaveInnerStreamOpen, tlsConfig.RemoteCertificateValidationCallback); var clientCertificates = new X509CertificateCollection(); if (tlsConfig.Certificate != null) { clientCertificates.Add(tlsConfig.Certificate); } await sslStream.AuthenticateAsClientAsync( remoteHostname, clientCertificates, AllowedTlsProtocols, tlsConfig.CheckCertificateRevocation); logger.Site().Debug("Authenticated connection to {0}[{1}]", remoteHostname, socket.RemoteEndPoint); clientStream = sslStream; } return(new EpoxyNetworkStream(socket, clientStream, logger)); }
public static async Task<EpoxyNetworkStream> MakeClientStreamAsync( string remoteHostname, Socket socket, EpoxyClientTlsConfig tlsConfig, Logger logger) { Stream clientStream; NetworkStream networkStream = new NetworkStream(socket, ownsSocket: false); if (tlsConfig == null) { clientStream = networkStream; } else { const bool leaveInnerStreamOpen = false; var sslStream = new SslStream( networkStream, leaveInnerStreamOpen, tlsConfig.RemoteCertificateValidationCallback); var clientCertificates = new X509CertificateCollection(); if (tlsConfig.Certificate != null) { clientCertificates.Add(tlsConfig.Certificate); } await sslStream.AuthenticateAsClientAsync( remoteHostname, clientCertificates, AllowedTlsProtocols, tlsConfig.CheckCertificateRevocation); logger.Site().Debug("Authenticated connection to {0}[{1}]", remoteHostname, socket.RemoteEndPoint); clientStream = sslStream; } return new EpoxyNetworkStream(socket, clientStream, logger); }
public async Task <EpoxyConnection> ConnectToAsync(Endpoint endpoint, CancellationToken ct) { logger.Site().Information("Connecting to {0}.", endpoint); EpoxyClientTlsConfig tlsConfig = endpoint.UseTls ? clientTlsConfig : null; try { EpoxyNetworkStream epoxyStream = await EpoxyNetworkStream.MakeAsync( socketFunc : () => ConnectClientSocketAsync(endpoint), streamFunc : socket => EpoxyNetworkStream.MakeClientStreamAsync(endpoint.Host, socket, tlsConfig, logger), timeoutConfig : timeoutConfig, logger : logger); var connection = EpoxyConnection.MakeClientConnection(this, epoxyStream, logger, metrics); try { connections.Add(connection); } catch (InvalidOperationException) { await connection.StopAsync(); throw new InvalidOperationException("This EpoxyTransport has been stopped already."); } await connection.StartAsync(); return(connection); } catch (Exception ex) { logger.Site().Error(ex, "Failed to start Epoxy client connection to '{0}'", endpoint); throw; } }
/// <summary> /// Sets the client-side TLS config to use when establishing /// connections. /// </summary> /// <param name="tlsConfig"> /// The client-side config. May be <c>null</c>. /// </param> /// <returns>The builder.</returns> /// <remarks> /// If <c>null</c>, /// <see cref="EpoxyClientTlsConfig.Default">EpoxyClientTlsConfig.Default</see> /// will be used for secure connections. /// </remarks> public EpoxyTransportBuilder SetClientTlsConfig(EpoxyClientTlsConfig tlsConfig) { clientTlsConfig = tlsConfig; return(this); }
public async Task Tls_MutualNoClientCert_ProxyDoesNotWork() { var serverTlsConfig = new EpoxyServerTlsConfig( testServerCert, checkCertificateRevocation: false, clientCertificateRequired: true, remoteCertificateValidationCallback: EnsureRootedWithTestCertificate ); var clientTlsConfig = new EpoxyClientTlsConfig(certificate: null, checkCertificateRevocation: false, remoteCertificateValidationCallback: EnsureRootedWithTestCertificate); var transport = new EpoxyTransportBuilder() .SetResolver(ResolveEverythingToLocalhost) .SetServerTlsConfig(serverTlsConfig) .SetClientTlsConfig(clientTlsConfig) .Construct(); listener = transport.MakeListener(new IPEndPoint(IPAddress.Loopback, EpoxyTransport.DefaultSecurePort)); listener.AddService(new DummyTestService()); await listener.StartAsync(); try { // The .NET SslStream implementation currently does not give us // a way to signal during TLS handshaking that the server is // rejecting the connection. Instead, we have to RST the // underlying socket. With Epoxy's current implementation, this // can't reliably be detected at connection time. So we attempt // to exercise the connection using a proxy and expect that to fail. EpoxyConnection clientConnection = await transport.ConnectToAsync("epoxys://bond-test-server1"); var proxy = new DummyTestProxy<EpoxyConnection>(clientConnection); await AssertRequestResponseWorksAsync(proxy); } catch (Exception ex) when (ex is InvalidOperationException || ex is AuthenticationException) { // An expected exception type, depending on timing, so pass the // test. } catch (Exception ex) { Assert.Fail("Unexpected exception of type {0}: {1}", ex.GetType(), ex); } finally { await transport.StopAsync(); } }
public async Task Tls_Mutual_CanAuthenticate() { var serverTlsConfig = new EpoxyServerTlsConfig( testServerCert, checkCertificateRevocation: false, clientCertificateRequired: true, remoteCertificateValidationCallback: EnsureRootedWithTestCertificate ); var clientTlsConfig = new EpoxyClientTlsConfig( certificate: testClientCert, checkCertificateRevocation: false, remoteCertificateValidationCallback: EnsureRootedWithTestCertificate); var transport = new EpoxyTransportBuilder() .SetResolver(ResolveEverythingToLocalhost) .SetServerTlsConfig(serverTlsConfig) .SetClientTlsConfig(clientTlsConfig) .Construct(); listener = transport.MakeListener(new IPEndPoint(IPAddress.Loopback, EpoxyTransport.DefaultSecurePort)); listener.AddService(new DummyTestService()); await listener.StartAsync(); EpoxyConnection clientConnection = await transport.ConnectToAsync("epoxys://bond-test-server1"); var proxy = new DummyTestProxy<EpoxyConnection>(clientConnection); await AssertRequestResponseWorksAsync(proxy); await transport.StopAsync(); }
public async Task Tls_ServerBadCert_ConnectionFails() { var serverTlsConfig = new EpoxyServerTlsConfig(testServerCert, checkCertificateRevocation: false); var serverTransport = new EpoxyTransportBuilder().SetServerTlsConfig(serverTlsConfig).Construct(); listener = serverTransport.MakeListener(new IPEndPoint(IPAddress.Loopback, EpoxyTransport.DefaultSecurePort)); listener.AddService(new DummyTestService()); await listener.StartAsync(); var clientTlsConfig = new EpoxyClientTlsConfig( checkCertificateRevocation: false, // Intentionally set this to null so that the client gets an // invalid certificate. If this test passes on your machine, // it's probably because you've installed the Bond test root // certificate as a trusted root. This certificate cannot be // trusted, so you should uninstall it. remoteCertificateValidationCallback: null); var clientTransport = new EpoxyTransportBuilder() .SetResolver(ResolveEverythingToLocalhost) .SetClientTlsConfig(clientTlsConfig) .Construct(); Assert.Throws<AuthenticationException>(async () => await clientTransport.ConnectToAsync("epoxys://bond-test-server1")); await clientTransport.StopAsync(); await serverTransport.StopAsync(); }