10

I have a CXF client that connects to a Web Service. This client is installed in a machine tha has two IP addresses in the same network (for example 172.16.1.101 and 172.16.1.102).

How can I configure the CXF client to use a specific source IP address, so that the server sees the requests coming from that specific IP address and not the other?

If I had access to the Socket, I would do something like:

Socket s = new Socket(); 
s.bind(new InetSocketAddress("172.16.1.102", 0));  //this Ip address is the one I need to specify
s.connect(new InetSocketAddress("google.com", 80));

Is it possible to configure the Sockets created by CXF so I can specify the source IP address?

EDIT: I need to specify the source IP address because between the client and the web server there is a firewall that has rules for one of the IP addresses (connections coming from the other IP address are blocked).

user2518618
  • 1,360
  • 13
  • 32

3 Answers3

5

CXF client uses java.net.URLConnection to connect to a service. The URLConnection can be configured to select a local IP address in this way (see How can I specify the local address on a java.net.URLConnection?)

URL url = new URL(yourUrlHere);
Proxy proxy = new Proxy(Proxy.Type.DIRECT, 
    new InetSocketAddress( 
        InetAddress.getByAddress(
            new byte[]{your, ip, interface, here}), yourTcpPortHere));
URLConnection conn = url.openConnection(proxy);

I have inspected the code of artifacts cxf-rt-rs-client and cxf-rt-transports-http to see how CXF is creating the connection. In ProxyFactory is the code to create the Proxy object needed for the UrlConnection

private Proxy createProxy(final HTTPClientPolicy policy) {
    return new Proxy(Proxy.Type.valueOf(policy.getProxyServerType().toString()),
                     new InetSocketAddress(policy.getProxyServer(),
                                           policy.getProxyServerPort()));
}

As you can see, there is no way to configure the IP address, so i am afraid that the answer to the question is you can not configure source IP address with CXF

But, I think it would not be difficult to modify the source code to allow setting the source IP address

HTTPClientPolicy

Add the following code to org.apache.cxf.transports.http.configuration.HTTPClientPolicy at cxf-rt-transports-http

public class HTTPClientPolicy {
    protected byte[] sourceIPAddress;
    protected int port;

    public boolean isSetSourceIPAddress(){
        return (this.sourceIPAddress != null);
    }

ProxyFactory

Modify the following code to org.apache.cxf.transport.http.ProxyFactory at cxf-rt-transports-http

 //added || policy.isSetSourceIPAddress()
 //getProxy() calls finally to createProxy
 public Proxy createProxy(HTTPClientPolicy policy, URI currentUrl) {
    if (policy != null) {
        // Maybe the user has provided some proxy information
        if (policy.isSetProxyServer() || policy.isSetSourceIPAddress())
            && !StringUtils.isEmpty(policy.getProxyServer())) {
            return getProxy(policy, currentUrl.getHost());
        } else {
            // There is a policy but no Proxy configuration,
            // fallback on the system proxy configuration
            return getSystemProxy(currentUrl.getHost());
        }
    } else {
        // Use system proxy configuration
        return getSystemProxy(currentUrl.getHost());
    }
}

//Added condition to set the source IP address (is set)
//Will work also with a proxy
private Proxy createProxy(final HTTPClientPolicy policy) {
    if (policy.isSetSourceIPAddress()){
        Proxy proxy = new Proxy(Proxy.Type.DIRECT, 
            new InetSocketAddress( 
                InetAddress.getByAddress(
                    policy.getSourceIPAddress(), policy.getPort()));
    } else {
        return new Proxy(Proxy.Type.valueOf(policy.getProxyServerType().toString()),
                     new InetSocketAddress(policy.getProxyServer(),
                                           policy.getProxyServerPort()));
    }                
}

Usage

Client client = ClientProxy.getClient(service);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setSourceIPAddress(new byte[]{your, ip, interface, here}));
httpClientPolicy.setPort(yourTcpPortHere);
http.setClient(httpClientPolicy);
Community
  • 1
  • 1
pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • Looking at your answer I thought that maybe httpClientPolicy. setProxyServer(...) would work (without modifying CXF) as the original createProxy() uses it. But it doesn't work because there is no way to set the Proxy.Type.DIRECT and also the proxyServer is used in other parts of the ProxyFactory (to set up an actual proxy). So, your answer is completely correct and the only way to set the source IP address is to modify the CXF source code. This is a very complete answer. Thanks! – user2518618 Aug 03 '16 at 22:42
  • I tried to modify the CXF code as you suggest, but it doesn't work. It is not possible to create a new Proxy with Proxy.Type.DIRECT. If you look at the [code of Proxy](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/net/Proxy.java#Proxy.%3Cinit%3E%28java.net.Proxy.Type%2Cjava.net.SocketAddress%29) it throws an exception if the parameter is DIRECT. See also the comment in the answer of [How can I specify the local address on a java.net.URLConnection?](http://stackoverflow.com/questions/91678/how-can-i-specify-the-local-address-on-a-java-net-urlconnection) – user2518618 Aug 05 '16 at 03:07
  • You are absolutely right. The `Proxy` class throws en exception always with type DIRECT since JDK1.5 (I have not reviewed more versions). The code linked in http://stackoverflow.com/questions/91678/how-can-i-specify-the-local-address-on-a-java-net-urlconnection CAN NOT work. I tried to subclass `Proxy`, but `URLConnection` clones the proxy and fails. – pedrofb Aug 05 '16 at 07:04
  • I think it would be possible to apply the second answer to HTTPS connections opening the socket at `org.apache.cxf.transport.https.HttpsURLConnectionFactory.createConnection`, but that solution does not seem very reliable. In this question http://stackoverflow.com/questions/1721444/how-can-i-bind-an-outbound-of-course-httpurlconnection-to-a-specific-local-ip?noredirect=1&lq=1 They propose using a proxy on the machine itself or register a custom `URLStreamHandlerFactory` to manipulate the socket. Complex. :( I will review the answer – pedrofb Aug 05 '16 at 07:04
1

A custom URLStreamHandlerFactory works.

Example:

        ...
        URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
            @Override
            public URLStreamHandler createURLStreamHandler(String protocol) {
                if (protocol.equals("http")) {
                    return new HttpHandler();
                } else if (protocol.equals("https")) {
                    return new sun.net.www.protocol.https.Handler();
                }
                return null;
            }
        });
        ...

private class HttpHandler extends java.net.URLStreamHandler {

    protected int getDefaultPort() {
        return 80;
    }

    @Override
    protected URLConnection openConnection(URL u) throws IOException {
        return openConnection(u, (Proxy) null);
    }

    @Override
    protected URLConnection openConnection(URL u, Proxy p) throws IOException {
        return new HttpURLConnection(u, p) {

            @Override
            protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)
                    throws IOException {
                return new HttpClient(url, (String) null, -1, true, connectTimeout) {

                    @Override
                    protected Socket createSocket() throws IOException {
                        Socket s = new Socket();

                        s.bind(new InetSocketAddress(InetAddress.getByName("1.2.3.4"),
                                0)); // yours IP here

                        return s;
                    }

                };
            }

            @Override
            protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout,
                    boolean useCache) throws IOException {
                return getNewHttpClient(url, p, connectTimeout);
            }

        };
    }
}

For HTTPS also usable a custom SSLSocketFactory

HttpsURLConnection.setDefaultSSLSocketFactory(...);
heli
  • 46
  • 2
  • This is the correct answer as this is the only one that works. Sorry I already assigned the bonty to a different one. One important detail is that in order for the https to work, you need to set setUseHttpsURLConnectionDefaultSslSocketFactory(true) (in the HTTPConduit TLSClientParameters). – user2518618 Aug 01 '17 at 06:14
0

Not sure if i understood your question correctly but I don't think you need to specifically set an IP address on the client for server to read `

Message message = PhaseInterceptorChain.getCurrentMessage();
HttpServletRequest request = (HttpServletRequest)message.get(AbstractHTTPDestination.HTTP_REQUEST);
request.getRemoteAddr()

Add this code on server and it can actually find IP from request and you can do a comparison on server for the IP. Let me know if this is what you are looking for.

Edit : -----

Well, My understanding is Firewall doesn't see the IP that comes in Header or Payload in a message. It checks the IP that is of the originating Server.

Correct me if i am wrong, but i feel if you deploy your client on that server for which firewall rule is added and make a call to server , there is no reason it shouldn't work.

Let me know what kind of error your are getting, say a stack trace or something and we can see what is happening.

Aravind R
  • 716
  • 1
  • 10
  • 36
  • I edited the question. I need to specify the source IP address because between the client and the web server there is a firewall that contains rules for one of the IP addresses and not the other. If the connection doesn't come from the correct IP address, it doesn't go through the firewall – user2518618 Aug 01 '16 at 02:07
  • You are right, the firewall looks at the IP header, not the payload of the message. This is why I have to set the IP in the Socket. The error that I get from the client are timeout errors because the firewall is not forwarding the packets. If a machine has two IP addresses of the same network, you can configure which one is used when opening each socket. The CXF framework takes care of everything including the Socket (and the source IP address), but I need a way to configure this socket (if it is possible). – user2518618 Aug 01 '16 at 20:46