1

I am using Spring Integration's TcpNetServerConnectionFactory and TcpInboundGateway to receive TCP messages. Everything is working as expected, but I was wondering if there is any way to implement address whitelisting? (Basically I want to allow a specified address and reject connections from others.) Maybe there is a way to add a callback to accept/reject when a connection is made, I couldn't find any mention in the docs or samples.

wrapperapps
  • 937
  • 2
  • 18
  • 30
  • Can you use a TCP Connection Interceptor to accomplish this? (https://docs.spring.io/spring-integration/reference/html/ip.html#ip-interceptors) – Joe Chiavaroli Feb 23 '18 at 15:45
  • I'm not sure, but I've been digging into `TcpNetServerConnectionFactory.setTcpSocketFactorySupport()` though. I'm trying to create a new `SocketFactory` that checks the host/address inside its `createSocket()` functions, but it's not quite working... – wrapperapps Feb 23 '18 at 16:14

1 Answers1

1

Create a custom TcpNetConnectionSupport (subclass DefaultTcpNetConnectionSupport and override createNewConnection()).

I think you should be able to close the socket there.

Inject it into the server connection factory.

See Advanced Techniques.

EDIT

It was added in Spring Integration 5...

@SpringBootApplication
public class So48951046Application {

    public static void main(String[] args) {
        SpringApplication.run(So48951046Application.class, args).close();
    }

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234);
            Thread.sleep(10_000);
            socket = SocketFactory.getDefault().createSocket("localhost", 1234);
            Thread.sleep(10_000);
        };
    }

    @Bean
    public TcpNetServerConnectionFactory server() {
        TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(1234);
        server.setTcpNetConnectionSupport(new DefaultTcpNetConnectionSupport() {

            @Override
            public TcpNetConnection createNewConnection(Socket socket, boolean server, boolean lookupHost,
                    ApplicationEventPublisher applicationEventPublisher, String connectionFactoryName)
                    throws Exception {
                TcpNetConnection conn = super.createNewConnection(socket, server, lookupHost, applicationEventPublisher, connectionFactoryName);
                if (conn.getHostAddress().contains("127")) {
                    conn.close();
                }
                return conn;
            }

        });
        return server;
    }

    @Bean
    public TcpReceivingChannelAdapter adapter() {
        TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
        adapter.setConnectionFactory(server());
        adapter.setOutputChannel(new NullChannel());
        return adapter;
    }

}

and

: server, port=1234 Listening
: Started So48951046Application in 0.907 seconds (JVM running for 1.354)
: Accepted connection from 127.0.0.1
: New connection localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478
: server: Added new connection: localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478
: localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478 Reading...
: server: Removed closed connection: localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478
: Read exception localhost:63624:1234:b558c7ca-f209-41b1-b958-7d9844f4d478 SocketException:Socket is closed
: Accepted connection from 127.0.0.1
: New connection localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33
: server: Added new connection: localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33
: server: Removed closed connection: localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33
: localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33 Reading...
: Read exception localhost:63625:1234:50c7b774-522a-4c43-b111-555e76611a33 SocketException:Socket is closed
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • thanks for pointing me to `TcpNetConnectionSupport`, I was able to get it working! the example is appreciated as well, but follow-up question: is it enough to check the socket host address and throw an exception? or is it better to create the connection, then check the host address, and return a closed connection like you did? – wrapperapps Feb 23 '18 at 16:50
  • from testing both, in the former case it appears that only the first connection is checked and after throwing the exception, subsequent connections are not. but in the latter case, all subsequent connections are checked as expected. – wrapperapps Feb 23 '18 at 17:14
  • 1
    If you throw an exception there, you'll kill the server socket thread. That's why I closed it instead. – Gary Russell Feb 23 '18 at 18:33
  • thanks again. I had some messaging issues, so I ended up postponing this solution (I think due to: https://stackoverflow.com/questions/48662264/how-to-use-spring-integration-5-with-spring-boot-1-5-x) – wrapperapps Feb 23 '18 at 18:39
  • Oh, right; yes, the ability to configure this was added in 5.0. One possible workaround in 4.3.x would be to add an `ApplicationListener` `@Bean`. The `source` of the event is the connection object. However, there is a (small) race condition since we start the reader thread before publishing the event. The chance of them sending data in that time would be rather small, though. – Gary Russell Feb 23 '18 at 18:59
  • 1
    I've fixed the code to protect against user-provided code throwing an exception. By the way, functionality like this is probably better implemented in a firewall, iptables, etc – Gary Russell Feb 23 '18 at 21:10
  • hmm that's a great point, I may just end up adding this to the firewall scope rules instead. of course then i'm trusting the user to do an extra manual step during installation, but it's not too difficult. – wrapperapps Feb 23 '18 at 21:47