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>();
        }
Esempio n. 2
0
        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;
            }
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        /// <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));
        }
Esempio n. 5
0
        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);
 }
Esempio n. 8
0
        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();
            }
        }
Esempio n. 9
0
        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();
        }
Esempio n. 10
0
        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();
        }