Spring:无法从防火墙后面使用RMI连接到JMX Server

Sid*_*dhu 4 java spring firewall jmx nat

我的Spring应用程序运行在NAT防火墙(pfSense)后面的机器上.机器的内部IP是a.b.c.d,NAT IP是w.x.y.z

Spring配置的serviceUrl设置为a.b.c.d端口上的内部IP()1100,当我启动应用程序时,我提供以下开关:

 -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=w.x.y.z -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
Run Code Online (Sandbox Code Playgroud)

如上所示,我进行了设置-Djava.rmi.server.hostname=w.x.y.z,以便可以通过NAT连接到应用程序.我还打开了机器防火墙上的相关端口,并设置了一个从w.x.y.z:1100前往的端口a.b.c.d:1100

当我尝试使用jconsole从网络外部连接到应用程序时w.x.y.z:1100,我得到了一个java.io.IOException: jmxrmi

 java.io.IOException: jmxrmi
at sun.tools.jconsole.ProxyClient.checkSslConfig(ProxyClient.java:236)
at sun.tools.jconsole.ProxyClient.<init>(ProxyClient.java:127)
at sun.tools.jconsole.ProxyClient.getProxyClient(ProxyClient.java:483)
at sun.tools.jconsole.JConsole$3.run(JConsole.java:524)
 Caused by: java.rmi.NotBoundException: jmxrmi
at sun.rmi.registry.RegistryImpl.lookup(RegistryImpl.java:136)
at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:409)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:267)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:275)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:252)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:378)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at sun.tools.jconsole.ProxyClient.checkSslConfig(ProxyClient.java:234)
Run Code Online (Sandbox Code Playgroud)

如果我尝试使用jconsole连接w.x.y.z:1099,我会得到一个java.rmi.ConnectException (Connection refused).如何在NATted防火墙之外公开我的JMX MBean?

 java.rmi.ConnectException: Connection refused to host: w.x.y.z; nested exception is: 
java.net.ConnectException: Operation timed out
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:129)
at javax.management.remote.rmi.RMIServerImpl_Stub.newClient(Unknown Source)
at javax.management.remote.rmi.RMIConnector.getConnection(RMIConnector.java:2373)
at javax.management.remote.rmi.RMIConnector.connect(RMIConnector.java:297)
at sun.tools.jconsole.ProxyClient.tryConnect(ProxyClient.java:355)
at sun.tools.jconsole.ProxyClient.connect(ProxyClient.java:313)
at sun.tools.jconsole.VMPanel$2.run(VMPanel.java:292)
Caused by: java.net.ConnectException: Operation timed out
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at java.net.Socket.connect(Socket.java:528)
at java.net.Socket.<init>(Socket.java:425)
at java.net.Socket.<init>(Socket.java:208)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:147)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
... 9 more
Run Code Online (Sandbox Code Playgroud)

有趣的是,我可以连接到两个端口(10991100上)w.x.y.z使用telnet.

我的Spring配置的相关部分是:

<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
    <property name="port" value="1100" />
</bean>
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry">
    <property name="server" ref="mbeanServer" />
    <property name="objectName" value="connector:name=rmi" />
    <property name="serviceUrl" value="service:jmx:rmi://a.b.c.d:1100/jndi/rmi://a.b.c.d:1100/server" />
    <property name="environment">
        <props>
            <prop key="jmx.remote.jndi.rebind">true</prop>
        </props>
    </property>
</bean>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <!-- indicate to first look for a server -->
    <property name="locateExistingServerIfPossible" value="true"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

注意:我没有使用任何servlet容器.

编辑:第一个答案为我提供了解决我的具体问题的基础,这可以在第二个答案中看到.

ant*_*tix 8

您定义的初始端口com.sun.management.jmxremote.port称为注册​​表端口,仅用于启动协商并确定用于"真实"通信的下一个端口.Java RMI机制使用动态分配的端口,通常与防火墙不兼容.

Java RMI连接使用什么端口?

也就是说,对于JMX来说,可以解决它

a)使用系统属性锁定两个端口(需要Java 7)

com.sun.management.jmxremote.port
com.sun.management.jmxremote.rmi.port
Run Code Online (Sandbox Code Playgroud)

b)使用自定义代码请求特定端口.请参阅 通过ssh本地端口转发的JConsole

也可以看看:


Sid*_*dhu 4

详细阐述了我使用链接和指针确定的解决方案,这anttix在他的回答中给了我。

因此,正如上面答案中提到的,使用系统属性com.sun.management.jmxremote.portcom.sun.management.jmxremote.rmi.port是解决方案的基础。

我更改了 Spring 配置以删除registryserverConnectorbeans,因为这不允许我注册端口1099PermissionExceptions导致我无法绑定到端口1099),并使用平台 mbean 服务器。

我使用的 Spring 配置是这样的:

 <bean id="mbeanServer" class="java.lang.management.ManagementFactory" factory-method="getPlatformMBeanServer"/>
Run Code Online (Sandbox Code Playgroud)

然后,我使用以下开关启动了该应用程序:

 -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=w.x.y.z -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.rmi.port=1099 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
Run Code Online (Sandbox Code Playgroud)

设置java.rmi.server.hostnameNAT VIPw.x.y.z至关重要,因为我们需要能够从网络外部对本地创建的远程对象执行远程操作。

注意:此解决方案仅适用于 Java 7u4 或更高版本。