Recently I had to securely monitor a remote WSO2 carbon server.
But the problem is, now any remote user can implement a MBean on the target server and use System.exit() from the client end to kill the Carbon server. So we need to harden (or secure and restrict) the communication between client and server.
So I used jConsole via a SSL tunnel which enforces client authentication and RMI-registry authentication. Here’re the steps I took to solve the problem.
Note - Feel free to shout back in case you need more clarifications. In some steps I assumed the audience is aware of JMX, public key cryptography etc.
Content
- Applied for
- Enable remote JMX monitoring for Carbon
- Enforcing SSL
- Enforcing SSL client authentication
- Enforcing RMI-registry authentication
Applied for
- Carbon 3.2.0 (or above) based products (WSO2 BPS 2.1.0, WSO2 AS 4.1.0, WSO2 ESB 4.0.0 etc)
- Sun JDK 1.6.0_24
Enable remote JMX monitoring for Carbon
Refer the Carbon console log.
INFO - JMXServerManager - JMX Service URL : service:jmx:rmi://localhost:11111/jndi/rmi://localhost:9999/jmxrmi
You can modify these ports at $CARBON_HOME/repository/conf/carbon.xml. See
<Ports>
<!-- The JMX Ports -->
<JMX>
<!--The port RMI registry is exposed-->
<RMIRegistryPort>9999</RMIRegistryPort>
<!--The port RMI server should be exposed-->
<RMIServerPort>11111</RMIServerPort>
</JMX>
</Ports>
But to enable remote access, the JMX server should be unbound from localhost and bound to a remotely accessible IP address. This can be done by modifying $CARBON_HOME/repository/conf/advanced/jmx.xml. Modify the
<JMX xmlns="http://wso2.org/projects/carbon/jmx.xml">
<StartRMIServer>true</StartRMIServer>
<!-- HostName, or Network interface to which this RMI server should be bound -->
<HostName>localhost</HostName>
<!-- ${Ports.JMX.RMIRegistryPort} is defined in the Ports section of the carbon.xml-->
<RMIRegistryPort>${Ports.JMX.RMIRegistryPort}</RMIRegistryPort>
<!-- ${Ports.JMX.RMIRegistryPort} is defined in the Ports section of the carbon.xml-->
<RMIServerPort>${Ports.JMX.RMIServerPort}</RMIServerPort>
</JMX>
Once the server is restarted, any external user can remotely monitor the Carbon instance via the exposed ports.
See the console log to make sure, whether the modifications were applied correctly.
INFO - JMXServerManager - JMX Service URL : service:jmx:rmi://192.254.45.65:11111/jndi/rmi://192.254.45.65:9999/jmxrmi
Now the problem is using this exposed URL, a remote user can implement a MBean on the target server and use System.exit() from the client end to kill the Carbon instance. So we need to harden (or secure and restrict ) the communication between client and server.
Enforcing SSL
Next step is to adding SSL to the communication.
First of all we need to create a certificate which is used to encrypt the communication between JMX client and JMX server. For the simplicity we can use the keytool shipped with JDK to generate a self-signed certificate which will be used by the Carbon instance.
Note - For more information on keytool referhttp://download.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html
Use the following command to generate the certificate.
Note - you can prefer any place to keep the certificates. In as Carbon developers we use this location to store as a best-practice.
keytool -genkey -alias jconsole -keystore $CARBON_HOME/repository/resources/security/.jConsoleKeyStore
During this operation, a public and private key pair and a certificate which is signed by the private key is generated.
Now we need to export this self-signed certificate which is used by the JMX server and import it into trust store of our JMX client. In our case it’s jConsole. To do that use the following steps.
- Export the certificate to file called jConsole.certkeytool -export -alias jconsole -keystore $CARBON_HOME/repository/resources/security/.jConsoleKeyStore -file jconsole.cert
- Securely transfer jconsole.cert to the machine where the JMX client is installed
- Import the jconsole.cert to a truststore used by jConsole using the following commandkeytool -import -alias carbon -keystore JAVA_HOME/bin/.jconsoleTrustStore -file jconsole.cert
Now all the configurations are set for enforce SSL communication. Now the Carbon instance and jConsole need to be restarted with relavant JVM options.
- Use the following command to start the Carbon instance with the mentioned JVM options.
Note - Here the keystore password is what is specified while generating the self-signed certificate for Carbon instance.$CARBON_HOME/bin/wso2server.sh-Dcom.sun.management.jmxremote.ssl=true-Djavax.net.ssl.keyStore=$CARBON_HOME/repository/resources/security/.jConsoleKeyStore -Djavax.net.ssl.keyStorePassword=secret - To start jConsolejconsole -J-Djavax.net.ssl.trustStore=JAVA_HOME/bin/.jconsoleTrustStore
Now the next step is to enforce SSL client authentication.
Enforcing SSL client authentication
To enable SSL client authentication, what we have to do is same as enforcing SSL communication.
All we have to do is generate the self-signed certificate for jConsole and export that certificate and import it back to a trust store used by the Carbon instance.
Use the following command to generate the certificate.
keytool -genkey -alias jconsole_client -keystore JAVA_HOME/bin/.jconsoleKeyStore
Now Export the certificate to file called jconsole_client.cert.
Then securely transfer jconsole_client.cert to where the Carbon instance is running.
Now import jconsole_client.cert to the trust store used by Carbon instance using the following command.
keytool -import -alias jconsole_client -keystore $CARBON_HOME/repository/resources/security/.jConsoleTrustStore -file jconsole_client.cert
Now all the configurations are set for enforce SSL communication and SSL client authentication. Now the Carbon instance and jConsole need to be restarted with relavant JVM options.
- Use the following command to start the Carbon instance with the mentioned JVM options.
Note - Here the keystore password is what is specified while generating the self-signed certificate for Carbon instance.$CARBON_HOME/bin/wso2server.sh-Dcom.sun.management.jmxremote.ssl=true-Djavax.net.ssl.keyStore=$CARBON_HOME/repository/resources/security/.jConsoleKeyStore -Djavax.net.ssl.keyStorePassword=secret-Dcom.sun.management.jmxremote.ssl.need.client.auth=true-Djavax.net.ssl.trustStore=$CARBON_HOME/repository/resources/security/.jConsoleTrustStore - To start jConsole use the following commandjconsole -J-Djavax.net.ssl.keyStore=JAVA_HOME/bin/.jconsoleKeyStore -J-Djavax.net.ssl.keyStorePassword=secret -J-Djavax.net.ssl.trustStore=JAVA_HOME/bin/.jconsoleTrustStore
Enforcing RMI-registry authentication
Now RMI-registry which is remotely accessible from a separate port (in the above description it’s 9999) also need to be enforced with SSL client authentication. As we have all configured the trust stores and keystores in both client and server end, only requirement to enable RMI-registry authentication is to add the following JVM option to the WSO2 Carbon instance starting script.
-Dcom.sun.management.jmxremote.registry.ssl=true
eg -
$CARBON_HOME/bin/wso2server.sh
-Dcom.sun.management.jmxremote.ssl=true
-Djavax.net.ssl.keyStore=$CARBON_HOME/repository/resources/security/.jConsoleKeyStore -Djavax.net.ssl.keyStorePassword=secret
-Dcom.sun.management.jmxremote.ssl.need.client.auth=true
-Djavax.net.ssl.trustStore=$CARBON_HOME/repository/resources/security/.jConsoleTrustStore
-Dcom.sun.management.jmxremote.registry.ssl=true
No comments:
Post a Comment